一、对象

Redis中每一个对象都是由redisObject结构构成。

typedef struct redisObject {
    unsigned type:4; //类型
    unsigned encoding:4;
    unsigned lru:LRU_BITS; //编码
    int refcount;
    void *ptr; //底层数据结构指针
} robj;

Redis中键总是字符串类型,值对象可以是字符串,哈希,列表,集合,有序集合。

查看类型:

127.0.0.1:6379> set msg "hello world"
OK
127.0.0.1:6379> type msg
string
127.0.0.1:6379> rpush members 1 3 4
(integer) 3
127.0.0.1:6379> type members
list
127.0.0.1:6379> HMSET gender  1 男 2 女
OK
127.0.0.1:6379> type gender
hash
127.0.0.1:6379> sadd book_set java spring redis
(integer) 3
127.0.0.1:6379> type book_set
set
127.0.0.1:6379> zadd book_zset 1 java 2 spring 3 redis
(integer) 3
127.0.0.1:6379> type book_zset
zset

查看编码:

127.0.0.1:6379> object encoding msg
"embstr"

1、字符串对象

编码有 int,raw,embstr。

int:整数值
embstr:小于等于44个字节的字符串
raw:大于44个字节的字符串

127.0.0.1:6379> object encoding a
"int"
127.0.0.1:6379> set b "sss"
OK
127.0.0.1:6379> object encoding b
"embstr"
127.0.0.1:6379> set c "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
OK
127.0.0.1:6379> object encoding c
"raw"

int 编码的话,redisObject的ptr保存整数值
embstr编码和raw编码,redisObject的ptr都是指向SDS.

embstr编码只调用一次内存重分配,一个连续的redisObject和sds结构。
raw编码会调用两次内存重分配,分别是一次redisObject和一次sds结构。

浮点数也会装成字符串类型

127.0.0.1:6379> set d 3.14
OK
127.0.0.1:6379> object encoding d
"embstr"

编码转换:

1、int 和 embstr 编码的追加字符串,会转化成raw编码。

127.0.0.1:6379> object encoding a
"int"
127.0.0.1:6379> get a
"1"
127.0.0.1:6379> append a   " hello world"
(integer) 13
127.0.0.1:6379> object encoding a
"raw"

2、embstr类型的没有修改程序,回先把它转化成raw,再去修改。

127.0.0.1:6379> object encoding b
"embstr"
127.0.0.1:6379> append b " world"
(integer) 11
127.0.0.1:6379> object encoding b
"raw"

2、列表对象

老版本采用ziplist和linkedlist。

ziplist编码,全部满足。否则是linkedlist。
1、所有元素的长度都小于等于64
2、元素个数不超过512个

列表对象新版本是quicklist,也就是多个ziplist的组合。

127.0.0.1:6379> eval "for i=1,512 do redis.call('rpush',KEYS[1],i) end " 1 integers
(nil)
127.0.0.1:6379> llen integers
(integer) 512
127.0.0.1:6379> object encoding integers
"quicklist"

3、哈希对象

采用ziplist和hashtable 编码。

编码转换:全部满足是ziplist,否则是hashtable
1、所有键值对的键和值的长度都小于等于64
2、键值对个数不超过512个

127.0.0.1:6379> hset user name aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 0
127.0.0.1:6379> object encoding user
"ziplist"
127.0.0.1:6379> hset user name aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 0
127.0.0.1:6379> object encoding user
"hashtable"

127.0.0.1:6379> eval "for i=1,512 do redis.call('hset',KEYS[1],i,i) end " 1 "ahash"
(nil)
127.0.0.1:6379> object encoding ahash
"ziplist"
127.0.0.1:6379> hset ahash a 1
(integer) 1
127.0.0.1:6379> object encoding ahash
"hashtable"

ziplist编码追加的时候,追加到尾部,键在前,值在后。

4、集合对象

整数集合使用intset,否则是使用hashtable编码。

127.0.0.1:6379> sadd aset 1 2 
(integer) 2
127.0.0.1:6379> object encoding aset
"intset"
127.0.0.1:6379> sadd bset 1 2 c 
(integer) 3
127.0.0.1:6379> object encoding aset
"intset"
127.0.0.1:6379> object encoding bset
"hashtable"
127.0.0.1:6379> sadd cset  1.0 1.2
(integer) 2
127.0.0.1:6379> object encoding cset
"hashtable"

hashtable编码时值对象是null。

编码转换:全部满足是intset

1、所有元素都是整数
2、元素额数不超过512

127.0.0.1:6379> eval "for i=1,512 do redis.call('sadd',KEYS[1],i,i) end " 1 "dset"
(nil)
127.0.0.1:6379> 
127.0.0.1:6379> 
127.0.0.1:6379> object encoding dset
"intset"
127.0.0.1:6379> sadd dset  abc
(integer) 1
127.0.0.1:6379> object encoding dset
"hashtable"

5、有序集合

使用ziplist和skiplist的编码

压缩列表,使用一个紧挨的一起的两个节点来存储,先存储成员,再存储分值。 按分值从小到大排序。

skiplist的编码底层使用zset 实现:

typedef struct zset {
    dict *dict; //字典
    zskiplist *zsl; //zskiplist压缩列表
} zset;

编码转换:全部满足是ziplist
1、元素长度不超过64
2、元素个数不超过128

27.0.0.1:6379> object encoding dzset
"ziplist"
127.0.0.1:6379> zadd dzset  1.0  a 2 b 3 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 1
127.0.0.1:6379> object encoding dzset
"skiplist"
127.0.0.1:6379> eval "for i=1,128 do redis.call('zadd',KEYS[1],i,i) end " 1 "zset2"
(nil)
127.0.0.1:6379> object encoding zset2
"ziplist"
127.0.0.1:6379> eval "for i=1,129 do redis.call('zadd',KEYS[1],i,i) end " 1 "zset3"
(nil)
127.0.0.1:6379> object encoding zset3
"skiplist"

6、对象特性

命令多态
根据类型,编码,调用不同的数据结构函数。

内存回收
引用计数法,在redisObject里有一个refcount 属性。

对象共享
多个key共用一个值对象,key指向值对象,值对象的引用数加1。

127.0.0.1:6379> SET c 9999
OK
127.0.0.1:6379> object refcount c
(integer) 2147483647   //引用次数太多了
127.0.0.1:6379> SET c 99999
OK
127.0.0.1:6379> object refcount c
(integer) 1  //1次

空转时长
redisObject里还有一个lru属性,记录最后一次被访问的时间。空转时长就是当前时间减去最后一次被访问的时间。

127.0.0.1:6379> object idletime c
(integer) 333
127.0.0.1:6379> get c
"99999"
127.0.0.1:6379> object idletime c
(integer) 3

另个作用是服务器如果打开了maxmemory的选项,内存算法为volatile-lru或allkeys-lru时,一旦内存超过了maxmemory,优先释放空转时长较高的。