一、对象
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,优先释放空转时长较高的。