先列举:
五大类型:String、List、Hash、Set、Sorted Set、
其它: pubsub(应该算独立功能吧?)、HyperLogLog(2.8.9+)、Geo(3.2+)、Stream(redis5+)
* * * redisObject * * *
redis中实现了很多自定义数据结构,但redis并没有直接使用这些结构来构建k-v系统,而是使用这些结构创建了一个对象系统;这个对象系统包含了上面我们列举的字符串对象、列表对象、哈希对象、集合对象等等;
注:本文假定阅读者对于redis底层数据结构及实现有一定了解
注:底层数据结构包括: 简单动态字符串SDS、链表linkedlist、跳跃表skiplist、压缩列表ziplist、整数集合等等
使用对象的优点:
- 执行命令前,方便根据对象类型判断一个对象是否可执行给定的命令;
- 针对不同的使用场景,为对象设置多种不同的数据结构实现,以优化性能;
- redis的对象系统还实现了基于引用计数技术的内存回收机制;
- 通过引用计数,还实现了对象共享机制;
- redis的对象带有访问时间记录信息,可用于计算数据库键的空转时长,以辅助实现maxmemory;
redisObject:
- type:类型
- REDIS_STRING: string
- REDIS_LIST: list
- REDIS_HASH: hash
- REDIS_SET: set
- REDIS_ZSET: zset
- encoding:编码,即底层数据结构的类型;
- REDIS_ENCODING_INT:int = long型整数
- REDIS_ENCODING_EMBSTR:embstr = embstr编码的SDS
- REDIS_ENCODING_RAW:raw = SDS
- REDIS_ENCODING_HT:hashtable = 字典(哈希表)
- REDIS_ENCODING_LINKEDLIST:linkedlist = 链表
- REDIS_ENCODING_ZIPLIST:ziplist = 压缩列表
- REDIS_ENCODING_INTSET:intset = 整数集合
- REDIS_ENCODING_SKIPLIST:skiplist = 跳跃表
- ptr:指向底层数据结构实现的指针;
说明: redis是一个键值对系统,它的键总是字符串对象,值为上述类型中声明的对象类型(新版本有扩充),使用type
命令可查看数据库键的类型,实际就是数据库键对应的值的类型;
-------- 快乐的分割线-------下面介绍五大类型 ----------------
String 字符串
redisObject的type为REDIS_STRING,用type命令查询,显示string,编码表示底层数据结构:
- int: 如果一个字符串对像保存的是整数值,且该值可用long类型来表示,那么该对象会将整数值保存在ptr属性中(void*转换为long),并将encoding设置为int
- raw:字符串值(含浮点型),且长度大于32Byte(默认),那字符串对象将使用一个SDS来保存这个值,并将encoding设置为raw
- embstr:字符串值(含浮点型),且长度小于等于32字节,则使用embstr编码的方式来保存
注1:embstr是一种专门保存短字符串的优化编码,与raw一样使用redisObject+sdshdr结构,区别是,raw调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr则调用一次内存分配函数来分配一块连续的内存空间,该空间依次包含这两种结构
注2:浮点型数据,在redis也是做为字符串对象保存的,保存或者计算时会有一个类型转换的过程
List 列表
可以用ziplist
或linkedlist
表示,后来用quicklist
(3.2+)
ziplist满足以下条件时使用压缩列表做为list的底层实现:- 所有元素字符串长度都小于64Byte(默认值)
- 元素数量小于512
linkedlist不满足使用ziplist的条件时,使用linkedlist- quicklist redis3.2开始支持,是一个由
ziplist
组成的双端列表,链表的每一个节点都是一个ziplist,貌似3.2之后,list只用quicklist结构实现
list-max-ziplist-size:每个ziplist(即quicklist的节点)大小,默认-2,此值可正可负,其中正数表示每个压缩列表中存放的最大元素个数,如果是负数,表示按字节数来限定元素个数:
- -1 每个节点的ziplist字节大小不能超过4kb
- -2 每个节点的ziplist字节大小不能超过8kb(默认值)
- -3 每个节点的ziplist字节大小不能超过16kb
- -4 每个节点的ziplist字节大小不能超过32kb
- -5 每个节点的ziplist字节大小不能超过64kb
Hash 哈希
编码为ziplist
或hashtable
- ziplist 用两个ziplist节点分别表示键和值,添加键值对时,先将键节点推入到表尾,再将值节点推入到表尾,因些键值对总是紧挨着且先加入的在表头方向; 满足如下条件时使用ziplist做底层实现:
- 所有键值对的键和值字符串的长度全部小于64Byte(默认值,配置项:hash-max-ziplist-value)
- 键值对数量小于512(默认值,配置项:hash-max-ziplist-entries)
- hashtable 不满足使用ziplist的条件时,将使用hashtable做为底层结构
Set 集合
集合对象的编码可以是intset
或者hashtable
- intset 满足以下条件时用作集合的底层实现:
- 集合对象保存的所有元素值都是整数值
- 元素数量不超过512(默认值,配置项:
set-max-intset-entries
)
- hashtable 不适用
intset
时,使用hashtable
做为底层实现,使用hashtable
时,只使用键,值为Null,类似于java的HashSet
Sorted Set 有序集合
有序集合的编码可以是ziplist
或skiplist
- ziplist 使用
ziplist
实现时,每个有序集合元素使用挨在一起的两个ziplistNode
表示,第一个表示元素成员member,第二个表示分值score;ziplist
中的集合元素按分值从小到大排列,小的靠近表头方向,大的靠近表尾方向;满足如下条件时使用:- 元素数量小于128(默认值,配置项:
zset-max-ziplist-entries
) - 所有元素成员的长度小于64(默认值,配置项:
zset-max-ziplist-value
)
- 元素数量小于128(默认值,配置项:
- skiplist 使用
zset
结构做为底层实现,每个zset
同时包含一个字典dict和一个跳跃表skiplist:- skiplist 按分值从小到大保存了有序集合的元素,保证有序,提高范围型操作性能
- dict 字典则是为每个元素创建了成员到分值的映射,提供快速查找性能
注:编码为skiplist
时,跳跃表和字典会共享成员及分值,内存中只占一分,因为不存在内存浪费;
----- 快乐的分割线 -------- 下面其它类型,还没好好研究完---------
pubsub 发布/订阅
发布/订阅似乎是一项独立功能,不能算一种数据类型,不过从使用上差不多,估且也算在这儿吧;
redis的发布/订阅功能分为两类:频道的发布/订阅及模式的发布/订阅;redis的服务器在redis内部用redisServer
结构表示,而发布订阅的实现,依赖于redisServer
中的两个属性:
- 频道(channel)
pubsub_channels
字典,字典的键表示被订阅的频道,而值是一个链表,存储了所有订阅该频道的客户端;- 订阅:定位到频道的订阅链表,在表尾添加一个含订阅客户端的节点
- 退订:定位到频道的订阅链表,遍历,找到含退订客户端的节点,删除
- 发布:定位到频道的订阅链表,遍历,依次发消息
- 模式(patter)
pubsub_patterns
链表,每个节点都包含一个pubsubPattern
结构,该结构中存储了订阅模式的客户端和订阅的模式;- 订阅:创建一个
pubsubPattern
结构,添加到pubsub_patterns
链表尾 - 退订:遍历
pubsub_patterns
,找到含要退订的模式和客户端的节点,删除 - 发布:遍历
pubsub_patterns
,向模式匹配的客户发消息
- 订阅:创建一个
注:订阅将阻塞线程,应该在独立线程独立客户端中使用;
HyperLogLog
使用type查询一个HyperLogLog类型的值,是string,似乎,是基于string结构实现的HyperLogLog功能?待探究
Geo
Stream
尚未研究,且目前5.0版本中,作者不建议在生产环境中使用stream,所以学习后再补充
-------- 快乐的分割线------- 知识扩展 ----------------