Redis的每个键值都是使用一个redisObject结构体保存的: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;
type字段表示数据类型:数组
/* Object types */ #define REDIS_STRING 0 #define REDIS_LIST 1 #define REDIS_SET 2 #define REDIS_ZSET 3 #define REDIS_HASH 4
encoding表示内部编码方式:bash
#define REDIS_ENCODING_RAW 0 /* Raw representation */ #define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_HT 2 /* Encoded as hash table */ #define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define REDIS_ENCODING_INTSET 6 /* Encoded as intset */ #define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
数据结构与编码方式对应:数据结构
redis使用sdshdr类型的变量来存储字符串,redisObject的ptr字段指向sdshdr的地址,sdshdr的定义以下:性能
struct sdshdr { int len; //buf已占用的空间长度 int free; //buf中剩余的空间长度 char but[]; //数据 真实存储c字符串 }
好比执行set key "hello"时,内存结构以下:优化
当存储字符串时,实际占用空间就如上图所示,字符串值存储在sdshdr结构中。ui
若是值的内容能够用一个64位符号整数表示,redis将会把值转换成long,内存结构以下:编码
能够看出若是值是long,那么将不会放到sdshdr结构中。spa
另外,redis会事先创建10000个key,分别存储0到9999这些数字,若是值是0-9999,那么能够直接使用这10000个数字而不需在建立redisObject了。3d
当配置了maxmemory设置了redis的最大空间大小时,redis不会使用共享对象,由于对于每个键值都要上会用一个redisObject来记录LRU信息。
当键值内容不超过39个字节时,reids会采用EMBSTR编码,这个编码的好处是把sdshdr结构与redisObject分配到连续的内存空间:
这么作的好处是无论分配存储仍是释放内存,所须要的操做都从2次减小到1次。
当对EMBSTR编码的字符串执行修改操做时(如APPEND操做),Redis会将其转换为RAW编码。
散列类型内部使用HT或者ZIPLIST编码。
两种数据结构的切换配置在:
hash-max-ziplist-entries 512 hash-max-ziplist-value 64
当散列类型字段个数少于hash-max-ziplist-entries个,或者每一个字段名和字段值的长度都小于hash-max-ziplist-value个字节,Redis会用ziplist编码存储数据,不然就使用ht存储数据,转换过程是透明的。
Redis的ZIPLIST,是一种紧凑的编码格式,它牺牲了部分读取性能换区极高的空间利用率,因此当entries在较少的个数而且value在较小的长度时使用。
具体实现不在此记录了,能够搜索相关文章。
列表类型采用LINKEDLIST和ZIPLIST两种编码。
LINKEDLIST采用双向链表,链表中的元素都是redisObject存储的,此处与字符串优化方式相同。
两种数据结构的切换配置在:
list-max-ziplist-entries 512 list-max-ziplist-value 64
转换方式和Hash同样。
其实linkedlist已经趋向于淘汰了,新版本的redis提供了QUICKLIST编码方式:
将长列表分红若干个以链表形式组织的ziplist,达到减小空间占用的同时还能够提高ziplist编码性能的效果,具体实现能够在网上搜索,此处不记录了。
内部实现方式是HT和INTSET。
当元素都是整型且元素的个数小于配置文件中set-max-intset-entries=512个时,Redis会使用INTSET,不然使用HT。
typedef struct intset { uint32_t encoding; // 编码类型 int16_t、int32_t、int64_t uint32_t length; // 长度 最大长度:2^32 int8_t contents[]; // 柔性数组 } intset;
其中centents存储的就是集合中的元素,根据encoding不一样,每一个元素占用的大小也不一样。默认是INT16(2字节),当新增长的整数没法用2字节存储时,Redis会将该集合的encoding升级到INT32(4字节),以此类推到INT64(8字节)。INTSET以有序的方式存储元素,因此可用二分法查找,但添加或删除元素都须要调整元素的内存位置,因此元素太多时性能很差,故定义了entries的个数限制。
INTSET转成HT后,不会在自动转回INTSET。
内部实现方式是SKIPLIST和ZIPLIST。
两种数据结构的切换配置在:
zset-max-ziplist-entries 128 zset-max-ziplist-value 64
当使用ZIPLIST时,
每一个集合元素使用两个紧挨在一块儿的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。而且压缩列表内的集合元素按分值从小到大的顺序进行排列,小的放置在靠近表头的位置,大的放置在靠近表尾的位置。
当使用SKIPLIST时,有序集合对象使用 zet 结构做为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表。
关于SKIPLIST,能够在网上搜索redis zset skiplist数据结构,在此处不作详细记录了。