标签:存储类型 car null zadd alt 多个 定义 函数 基础
Redis对象类型
Redis基于基础的数据结构创建的对象:
对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放;Redis通过引用计数技术实现了对象共享机制,在适当的条件下通过让多个数据库键共享同一个内存对象来节约内存;
一、RedisObject
在server.h文件中,给出了RedisObject的结构体定义:
typedef struct redisObject { unsigned type:4;//类型 unsigned encoding:4;//编码 unsigned lru:LRU_BITS; //访问时间lru int refcount;// 引用计数refcount void *ptr;//指向底层实现数据结构的指针 } robj;
Redis的对象有五种类型,分别是string、hash、list、set和zset,type属性就是用来标识着五种数据类型。type占用4个bit位,其取值和类型对应如下:
#define OBJ_STRING 0 #define OBJ_LIST 1 #define OBJ_SET 2 #define OBJ_ZSET 3 #define OBJ_HASH 4
Redis对象的编码方式由encoding参数指定,也就是表示ptr指向的数据以何种数据结构作为底层实现。该字段也占用4个bit位。其取值和对应类型对应如下:
#define OBJ_ENCODING_RAW 0 /* Raw representation */ #define OBJ_ENCODING_INT 1 /* Encoded as integer */ #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */ #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */ #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
上述这几种编码类型与其底层实现如下表:
编码类型 | 底层实现 |
OBJ_ENCODING_RAW | 简单动态字符串sds |
OBJ_ENCODING_INT | long类型的整数 |
OBJ_ENCODING_HT | 字典dict |
OBJ_ENCODING_LINKEDLIST | 双端队列sdlist |
OBJ_ENCODING_ZIPLIST | 压缩列表ziplist |
OBJ_ENCODING_INTSET | 整数集合intset |
OBJ_ENCODING_SKIPLIST | 跳跃表skiplist和字典dict |
OBJ_ENCODING_EMBSTR | EMBSTR编码的简单动态字符串sds |
OBJ_ENCODING_QUICKLIST | 由双端链表和压缩列表构成的快速列表 |
Redis的每一种对象类型可以对应不同的编码方式,这就极大地提升了Redis的灵活性和效率。Redis可以根据不同的使用场景,来选择合适的编码方式,五种对象类型对应的底层编码方式如下表所示:
对象类型 | 编码方式 |
OBJ_STRING 字符串类型 |
OBJ_ENCODING_RAW ,OBJ_ENCODING_INT ,OBJ_ENCODING_EMBSTR 简单动态字符串sds;long类型的整数;EMBSTR编码的简单动态字符串sds |
OBJ_LIST 列表类型 |
OBJ_ENCODING_LINKEDLIST ,OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_QUICKLIST 双端队列sdlist;压缩列表ziplist;由双端链表和压缩列表构成的快速列表 |
OBJ_SET 集合类型 |
OBJ_ENCODING_INTSET ,OBJ_ENCODING_HT 整数集合intset、字典dict |
OBJ_ZSET 有序集合类型 |
OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_SKIPLIST 压缩列表ziplist、跳跃表skiplist和字典dict |
OBJ_HASH 哈希类型 |
OBJ_ENCODING_ZIPLIST ,OBJ_ENCODING_HT 压缩列表ziplist、字典dict |
lru表示该对象最后一次被访问的时间,其占用24个bit位。保存该值的目的是为了计算该对象的空转时长,便于后续根据空转时长来决定是否释放该键,回收内存。键的空转时长还有另外一项作用:如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。
C语言不具备自动内存回收机制,所以Redis对每一个对象设定了引用计数refcount字段,程序通过该字段的信息,在适当的时候自动释放内存进行内存回收。此功能与C++的智能指针相似。
二、t_String 字符串类型
字符串是Redis中最为常见的数据存储类型,其底层实现是简单动态字符串sds,因此,该字符串类型是二进制安全的,这就意味着它可以接受任何格式的数据。另外,Redis规定,字符串类型最多可以容纳的数据长度为512M。Redis提供了下列函数,来检测字符串键的大小。
static int checkStringLength(client *c, long long size) { // 超出了512M,就直接报错 if (size > 512*1024*1024) { addReplyError(c,"string exceeds maximum allowed size (512MB)"); return C_ERR; } return C_OK; }
字符串对象的编码可以是int、raw或者embstr。
三、t_list 列表对象类型
当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:
不能满足这两个条件的列表对象需要使用linkedlist编码。
以上两个条件的上限值是可以修改的,配置文件中关于list-max-ziplist-value选项和list-max-ziplist-entries
四、t_hash 哈希对象类型
当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:
不能满足这两个条件的哈希对象需要使用hashtable编码。
这两个条件的上限值是可以修改的,配置文件中关于hash-max-ziplist-value选项和hash-max-ziplist-entrie
每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:
五、t_set 集合对象类型
集合对象的编码可以是intset或者hashtable。
当集合对象可以同时满足以下两个条件时,对象使用intset编码:
不能满足这两个条件的集合对象需要使用hashtable编码。第二个条件的上限值是可以修改的,配置文件中关于set-max-intset-entries选项的说明
六、t_zset 集合对象类型
压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。
zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素。每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围型操作。
除此之外,zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,ZSCORE命令就是根据这一特性实现的,而很多其他有序集合命令都在实现的内部用到了这一特性。
在理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论单独使用字典还是跳跃表,在性能上对比起同时使用字典和跳跃表都会有所降低。举个例子,如果我们只使用字典来实现有序集合,那么虽然以O(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作——比如ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少O(NlogN)时间复杂度,以及额外的O(N)内存空间(因为要创建一个数组来保存排序后的元素)。
另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从O(1)上升为O(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。
七、类型检查与命令多态
Redis中用于操作键的命令基本上可以分为两种类型。
?SET、GET、APPEND、STRLEN等命令只能对字符串键执行;
?HDEL、HSET、HGET、HLEN等命令只能对哈希键执行;
?RPUSH、LPOP、LINSERT、LLEN等命令只能对列表键执行;
?SADD、SPOP、SINTER、SCARD等命令只能对集合键执行;
?ZADD、ZCARD、ZRANK、ZSCORE等命令只能对有序集合键执行;
标签:存储类型 car null zadd alt 多个 定义 函数 基础
原文地址:https://www.cnblogs.com/winterfells/p/9152028.html