特点
- 数据类型多
- 支持事务、持久化
- 多种集群方式
- lua命令、缓存过期等策略
memcache:
- 只能kv存储
- 无法持久化
mysql:
- 磁盘io慢,无法支持高性能
- redis单线程,支持高性能10w QPS
架构
事件处理
- 网络 IO 读写
- 命令执行
- 时间事件处理
数据管理
- 数据以 kye/value 的格式存储在内存中,多个redisDB的字典
- 持久化存储aof/rdb文件
集群管理
过期处理
为插件化功能扩展的 Module System 模块
读写流程
每当有客户端连接到服务器的时候,都会创建一个Socket连接,对应一个套接字文件描述符fd;
当客户端与Redis服务器连接之后,redisServer结构中会存储一个客户端的链表,记录客户端信息
通过命令对数据库进行了读写之后,Redis同时会做一些维护工作:
数据结构
- redisDb 结构,表示 Redis 数据库的结构,结构体里存放了指向了 dict 结构的指针;
- dict 结构,结构体里存放了 2 个哈希表,正常情况下都是用「哈希表1」,「哈希表2」只有在 rehash 的时候才用
-
ditctht 结构,表示哈希表的结构,结构里存放了哈希表数组,数组中的元素是指向哈希表节点的指针;
-
dictEntry 结构,表示哈希表节点的结构,结构里存放了 **void * key 和 void * value 指针, key 指向的是 String 对象,而 value 则可以指向 String 对象,也可以指向集合类型的对象
hash
哈希表
保存键值对(key-value)的数据结构
渐进式 rehash:
-
哈希表元素的删除、查找、更新等操作都会在这两个哈希表
- 每次请求也会顺序将旧表索引位置上的所有 key-value 迁移到新表上
-
新增只维护在新表
-
时机:负载因子(哈希表节点/大小) >=1 & 非rdb快照和aof重写操作时机;负载因子 > 5
优点
- 以 O(1) 的复杂度快速查询数据
- 冗余存储数字,节省空间
缺点
-
哈希冲突(链式哈希)
listPack
string
sds:简单动态字符串
-
支持二进制存储(支持存储/0字符)
-
记录长度,计算o1
-
由于能提前取到长度(alloc-len),支持动态扩展
-
空间预分配规则:size<1m时扩容加倍,size>1m时只会扩容1M
-
惰性空间释放:通过free属性,把字节数量记录下来,重复使用
raw编码(长度>44 byte):调用2次内存分配函数
embstr编码:只调用一次内存分配函数
list
无环的双向链表 linkedList
优点
- 获取长度O1(len)
- 获取前节点、后节点、头节点、尾节点都只是O1
缺点
-
需要冗余存储节点结构头
-
内存不连续
压缩列表 ziplist
由连续内存块组成的顺序型数据结构,根据数据大小和类型进行不同空间大小分配的设计思想。
-
prelen:前一个节点的「长度」确定「空间大小」
-
encoding:根据整数/字符串记录
- 前两个 bit 表示数据的类型
- 其他 bit 标识字符串数据的实际长度
优点
- 连续内存(可以利用cpu缓存)
- 获取头节点、尾节点只是O1
- 方便后往前、前往后遍历
- 根据类型进行编码,节省内存
缺点
-
无法快速读取元素(遍历)
-
插入元素的连锁更新效应(多次调整内存空间,内存重新分配)
-
无法存储大量数据
quickList
双向链表,每个元素又是一个压缩列表
插入元素时会判断是否加入压缩列表,还是说创建个新的节点;
来控制 quicklistNode 结构里的压缩列表的大小或者元素个数
使用LZF算法压缩ZipList
listPack
listpack 只记录当前节点的长度,不关心前一个节点的长度。
当加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题。
set
整数集合
集合元素从小到大有序排列,支持二分查找
Set 对象只包含整数值元素,并且元素数量不大
整数升级:添加的元素比当前所有元素类型都要长的时候节省内存(无法降级)
- 数组原地扩容
- 元素计算位置,并按顺序放置
zset
跳表 skiplist
「多层」的有序链表
vs 平衡树:
-
内存占用 2个节点> 内存占用1- 1/0.25=1.3个节点
-
范围查找快
-
插入快
-
支持平均 O(logN) 复杂度的节点查找
- 本层查找「权重靠近」&&「数据靠近」的节点
- 不满足条件时逐层查找
-
支持插入O1(改变指针)
-
支持查找节点排名(跨度)
-
查找头节点、尾节点O1
-
随机确定层数,打散(0.250.250.25*XXX)
延迟队列
Score 属性,存储延迟执行的时间
zrangebysocre 命令,查询符合条件的所有待处理的任务, 循环执行队列任务即可
BITMAP
比特数组,将具体的数据映射到比特数组的某个位置,通过0和1记录其状态
优点:大数据的查询,去重等场景,空间利用率高
缺点:空间的利用率低,offset大的情况下,数据分布稀疏
- RLE编码(行程长度编码)压缩
BLOOM FILTER
测试元素是否为集合的成员,底层使用BitMap进行存储。判定不在的数据一定不存在,存在的数据不一定存在
k个哈希函数来计算给定输入的哈希值,分别对k位offset置为1
优点:节省空间的概率数据结构,快速判断是否存在
缺点:随着元素的添加,误判率会越来越高
- 增加哈希函数与更多的bit