字符串对象 string
三种编码
- int
- embstr
- raw
int
如果一个字符串对象保存的是整数,并且这个整数值可以用long来表示,那么这和字符串会将整数保存在ptr中,将void* 转换成long
raw
如果字符串的长度大于39字节,那么用SDS来保存这个字符串,编码为raw(这是最正常的情况)
embtr
如果这个字符串的大小,小于等于39,那么编码为embter,存储方式还是SDS。但这个SDS和raw有所区别。
embstr和raw都是用一个robj和shshdr来存储字符串的。raw会调用两次内存分配,分别创建两块内存,分别给robj和shshdr。embter只会调用一次内存分配,只创建一块内存,robj和shshdr呢诶村地址紧挨着共用该内存。
这样做的好处
- 内存分配从两次变为一次
- 释放内存从两次变为一次
- 节约内存
编码转换
int和embtr都是有条件的嘛,当不满足这个条件了,就都会变成raw格式了。
比如:
int类型后面调用函数追加了字符串
embtr类型追加字符串后长度大于39
列表对象 list
三种编码 (三种内部数据结构)
- ziplist
- linkedlist
- quicklist
列表是什么
列表就是传统意义上的队列。只能对队列头和尾进行入队和出队的操作。中间不可以操作。
3.2之前的版本
3.2之前的版本没有quicklist底层数据结构
- 保存的所有字符串长度都小于64字节
- 元素数量小于512个
二者都满足,使用ziplist
任一不满足,使用linkedlist
3.2及以后的版本
统一使用quicklist
哈希对象 hash
两种编码(底层数据结构)
- ziplist
- hashtable
ziplist
压缩列表怎么存呢??
答案是:列表中,前面是key,后面是value,紧挨着。
hashtable
传统意义的数组+链表的形式。
编码转换
- 保存的所有字符串长度都小于64字节
- 元素数量小于512个
二者都满足,使用ziplist
任一不满足,使用hashtable
集合对象 set
两种编码方式(内部数据结构)
- intset
- hashtable
intset
整数的set,就直接用intset来存储。
hashtable
hashtable是key/value的形式,怎么存set呢。只用key,value值不用全为null就好了。还是使用链表法解决冲突。
有序集合
两种编码方式(内部数据结构)
- ziplist
- skiplist
ziplist
还是key/value内存地址紧挨着。有序集合需要从小到大排序。分值较小的靠近表头,分值较大的靠近表尾。
skiplist
使用跳表和字典来实现
编码转换
- 保存的所有字符串长度都小于64字节
- 元素数量小于128个
二者都满足,使用ziplist
任一不满足,使用skiplist
对象共享
假设a创建了一个包含整数值100的字符串作为值,如果这时b也创建一个包含整数值100的字符串。那么服务器有以下两种做法:
- 为键b新创建一个包含整数值100的字符串对象
- 让键a键b共享一个对象
很明显,第二种取胜,更节约内存。
redis让多个指针指向同一个对象分两步:
- 将数据库key的指针,指向同一个对象
- 将被共享的值对象的引用计数 +1
如图所示,a的引用计数为7,set b后引用计数变为8。当b修改了之后,a的计数重新变为7。b与其他键共用了101值对象,所以引用计数是2。
redis会共享0-9999的字符串对象。
对象的空转时长
该属性记录了对象最后一次被命令程序访问的时间。
执行object idletime,使用当前时间 减去 空转时间lru得到的空转时长。
内存回收的策略,也会根据这个最后访问时间去进行决策。
内存回收策略,在redis存储章节展开讲。