redis里面用到了主要数据结构,比如简单动态字符串,双端链表,字典,压缩列表,整数集合等,但redis里面并没有直接使用这些数据结构,而是基于这些数据结构建立了一个对象系统,这个对象系统包括字符串对象,列表对象,哈希对象,集合对象和有序集合对象。
使用对象的好处:
- 执行命令前,根据对象的类型判断一个对象是否可以执行给定的命令;
- 可以针对不同的场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景次啊的使用效率;
- redis对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象占用的内存就会被自动释放;另外,redis还通过引用计数技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享同一个对象来节约内存;
- redis的对象带有访问时间信息,该信息可以用于计算键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键会优先被服务器删除;
1. 对象的类型与编码
每当我们在redis的数据库中创建一个键值对时,至少会创建两个对象,一个对象用作键值对的键(键对象),一个用作键值对的值(值对象)。
redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type,encoding和ptr:
1.1 类型
记录对象的类型,这个属性的值可以是REDIS_STRING(字符串),REDIS_LIST(列表对象),REDIS_HASH(哈希对象),REDIS_SET(集合对象),REDIS_ZSET(有序集合对象)。
对于redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是五种对象中的其中一种。当我们执行type命令时,返回的是值对象的类型。
redis> set msg "hello world"
OK
redis> TYPE msg
string
1.2 编码和底层实现
对象的ptr指针指向对象的底层实现数据结构,这些数据结构由对像encoding属性决定。
encoding的值可以是以下几种:
- REDIS_ENCODING_INT:long类型的整数;
- REDIS_ENCODING_EMBSTR:embstr编码的简单动态字符串;
- REDIS_ENCODING_RAW:简单动态字符串;
- REDIS_ENCODING_HT:字段;
- REDIS_ENCODING_LINKEDLIST:双端链表;
- REDIS_ENCODING_ZIPLIST:压缩列表;
- REDIS_ENCODING_INTSET:整数集合;
- REDIS_ENCODING_SKIPLIST:跳跃表和字典;
每种类型的对象都至少使用了两种不同的编码,下表列出了每种类型的对象可以使用的编码:
使用OBJECT ENCODING命令可以查看数据库键的值对象的编码:
redis通过属性encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大的提升了redis的灵活性和效率。
2. 字符串对象
字符串对象的编码可以是int,raw,embstr;
如果字符串对象保存的是一个字符串值,并且这个值的长度大于32字节,那么字符串对象会使用简单动态字符串来保存,对象的编码为raw;如果一个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码保存这个值。
raw编码和embstr编码的区别:raw会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中以及包含redisObject和sdshdr两个结构;
raw编码示例:
embstr编码示例:
另外,如果我们保存的是一个浮点数,程序会先将这个浮点数转换成字符串值,再保存字符串值;如果执行命令对这个字符串进行增减,也是会把字符串取出来,转成浮点数,做完运算,再转成字符串存进去。
2.1 编码的转换
对于int编码的字符串对象,如果我们向对象执行命令,使得这个对象保存的不再是整数,而是一个字符串值,那么字符串对象的编码将从int变为raw;
redis没有为embstr编码的字符串对象编写任何的修改程序,也就是说embstr编码的字符串是只读的,当对embstr编码的对象执行修改命令时,程序会先将对象的编码从embstr转换成raw,因此,embstr编码的字符串对象在执行修改命令之后,都会变成一个raw编码的字符串对象。
2.2 字符串命令的实现
3. 列表对象
列表对象的编码可以是ziplist或者linkedlist,下图分别展示了ziplist和linkedlist编码的列表对象:
3.1 编码转换
当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:
- 保存的所有字符串元素的长度都小于64字节;
- 列表对象保存的元素数量小于512个;
不能满足这两个条件则使用linkedlist编码;
需要注意,上面两个条件的上限值是可以修改的,具体可以看配置文件中关于list-max-ziplist-value选项和list-max-ziplist-entries选项的说明;