图忆Redis核心点系列(1)——基础数据结构

56 阅读5分钟

特点

  • 数据类型多
  • 支持事务、持久化
  • 多种集群方式
  • 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