当我们执行redis以下命令时:
set hello world对应的redis内存存储模型图
dictEntry:每个键值对都会包装成dictEntry对象,存储了指向Key和Value的指针;next指向下一个dictEntry。Key:Key(”hello”)并不是直接以字符串存储,而是存储在SDS结构中。redisObject:值的存储,包装成了redisObject对象,里面的type是表示redis值的类型(string,set,list等),ptr 是存储的具体值(也是sds存储)。sds (Simple Dynamic String,简单动态字符串)是 Redis 底层所使用的字符串表示, 几乎所有的 Redis 模块中都用了 sds。
为什么redis不用简单的char * 而是用sds? sds存储结构:
typedef char *sds; struct sdshdr { // buf 已占用长度 int len; // buf 剩余可用长度 int free; // 实际保存字符串数据的地方 char buf[]; };当redis存储hello world 字符串时,sds是这样的:
struct sdshdr { len = 11; free = 0; buf = "hello world\0"; // buf 的实际长度为 len + 1 };通过 len 属性, sdshdr 可以实现复杂度为 θ(1) 的长度计算操作。 当我们使用append命令追加字符串 abcdef时:
struct sdshdr { len = 18; free = 18; buf = "hello world abcdef\0 "; // 空白的地方为预分配空间,共 18 + 18 + 1 个字节 }注意, 当调用 SET 命令创建 sdshdr 时, sdshdr 的 free 属性为 0 , Redis 也没有为 buf 创建额外的空间 —— 而在执行 APPEND 之后, Redis 为 buf 创建了多于所需空间一倍的大小。这样以后在append 当小于18时,就不会在额外分配空间。 sds总结:
计算长度len,复杂度是θ(1)。高效的追加,通过预分配,降低内存分配。Redis在编译时便会指定内存分配器;内存分配器可以是 libc 、jemalloc或者tcmalloc,默认是jemalloc。
jemalloc作为Redis的默认内存分配器,在减小内存碎片方面做的相对比较好。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储。 例如,如果需要存储大小为130字节的对象,jemalloc会将其放入160字节的内存单元中。
redis值的存储对象。以下是存储结构:
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */ int refcount; void *ptr; } robj;(1)type 表示对象类型,占4个bit。 目前包括REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。 当我们执行type命令时,就是读取的这个字段。
(2)encoding 表示对象的内部编码,占4个bit比特。 每种redis类型,都支持多种编码。以列表对象为例,有压缩列表和双端链表两种编码方式;如果列表中的元素较少,Redis倾向于使用压缩列表进行存储,因为压缩列表占用内存更少,而且比双端链表可以更快载入;当列表对象元素较多时,压缩列表就会转化为更适合存储大量元素的双端链表。
(3)lru lru记录的是对象最后一次被命令程序访问的时间,占据的比特数不同的版本有所不同(如4.0版本占24比特,2.6版本占22比特)。 通过lru时间与当前时间可以算出对象空转时间(多久没被操作过了); object idletime key命令可以显示空转时间,并且不改变lru时间。
10.0.47.76:14159> OBJECT idletime limit_activity.2765980 19 10.0.47.76:14159> OBJECT idletime limit_activity.2765980 23(4)refcount refcount记录的是该对象被引用的次数。当初始化时为1,对有对象也是指向这个对象时,refcount就加1。 redis中被多次使用的对象叫做共享对象。
就目前的实现来说,Redis服务器在初始化时,会创建10000个字符串对象,值分别是0-9999的整数值;当Redis需要使用值为0-9999的字符串对象时,可以直接使用这些共享对象。 共享对象的引用次数可以通过object refcount命令查看
10.0.47.76:14159> OBJECT refcount limit_activity.2765980 1(5)ptr 指向的具体数据。
(1)maxmemory Redis使用 maxmemory 参数限制最大可用内存,默认值为0,表示无限制。
maxmemory 限制的是 Redis 实际使用的内存量,也就是 used_memory 统计项对应的内存。实际消耗的内存可能会比 maxmemory 设置的大,要小心因为这部内存导致 OOM。所以,如果你有 10GB 的内存,最好将 maxmemory 设置为 8 或者 9G。