Redis篇: 中篇(redis底层数据结构)

242 阅读4分钟

1、redis数据类型

类型底层应用场景编码类型
StringSDS数组帖子、评论、热点数据、输入缓冲RAW << EMBSTR << INT
ListQuickList评论列表、商品列表、发布与订阅、慢查询、监视器LINKEDLIST << ZIPLIST
SetintSet适合交集、并集、查集操作,例如朋友关系HT << INSET
Zset跳跃表去重后排序,适合排名场景SKIPLIST << ZIPLIST
Hash哈希结构化数据,比如存储对象HT << ZIPLIST
Stream紧凑列表消息队列

2、相关API


http://redisdoc.com

StringSETSETNXSETEXGETGETSETINCRDECRMSETMGET
HashHSETHSETNXHGETHDELHLENHMSETHMGETHKEYSHGETALL
LISTLPUSHLPOPRPUSHRPOPLINDEXLREMLRANGELLENRPOPLPUSH
ZSETZADDZREMZSCOREZCARDZRANGEZRANKZREVRANKZREVRANGE
SETSADDSREMSISMEMBERSCARDSINTERSUNIONSDIFFSPOPSMEMBERS
事务MULTIEXECDISCARDWATCHUNWATCH

3、redis底层结构

SDS数组结构,用于存储字符串和整型数据及输入缓冲。

struct sdshdr{ 
  int len;//记录buf数组中已使用字节的数量 
  int free; //记录 buf 数组中未使用字节的数量 
  char buf[];//字符数组,用于保存字符串
}

跳跃表:将有序链表中的部分节点分层,每一层都是一个有序链表。

1、可以快速查找到需要的节点 O(logn) ,额外存储了一倍的空间

2、可以在O(1)的时间复杂度下,快速获得跳跃表的头节点、尾结点、长度和高度。

字典dict: 又称散列表(hash),是用来存储键值对的一种数据结构。

Redis整个数据库是用字典来存储的(K-V结构) —Hash+数组+链表

Redis字典实现包括:字典(dict)、Hash表(dictht)、Hash表节点(dictEntry)

字典达到存储上限(阈值 0.75),需要rehash(扩容)

1、初次申请默认容量为4个dictEntry,非初次申请为当前hash表容量的一倍。

2、rehashidx=0表示要进行rehash操作。

3、新增加的数据在新的hash表h[1] 、修改、删除、查询在老hash表h[0]

4、将老的hash表h[0]的数据重新计算索引值后全部迁移到新的hash表h[1]中,这个过程称为 rehash。

渐进式rehash

由于当数据量巨大时rehash的过程是非常缓慢的,所以需要进行优化。 可根据服务器空闲程度批量rehash部分节点

压缩列表zipList

压缩列表(ziplist)是由一系列特殊编码的连续内存块组成的顺序型数据结构,节省内容

sorted-set和hash元素个数少且是小整数或短字符串(直接使用)

list用快速链表(quicklist)数据结构存储,而快速链表是双向列表与压缩列表的组合。(间接使用)

整数集合intSet

整数集合(intset)是一个有序的(整数升序)、存储整数的连续存储结构。

当Redis集合类型的元素都是整数并且都处在64位有符号整数范围内(2^64),使用该结构体存储。

快速列表quickList

快速列表(quicklist)是Redis底层重要的数据结构。是Redis3.2列表的底层实现。

(在Redis3.2之前,Redis采 用双向链表(adlist)和压缩列表(ziplist)实现。)

Redis Stream的底层主要使用了listpack(紧凑列表)和Rax树(基数树)。

listpack表示一个字符串列表的序列化,listpack可用于存储字符串或整数。用于存储stream的消息内 容。

Rax树是一个有序字典树 (基数树 Radix Tree),按照 key 的字典序排列,支持快速地定位、插入和删除操 作。

4、Zset底层实现

跳表(skip List)是一种随机化的数据结构,基于并联的链表,实现简单,插入、删除、查找的复杂度均为O(logN)。简单说来跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳表能够提供O(logN)的时间复杂度

Zset数据量少的时候使用压缩链表ziplist实现,有序集合使用紧挨在一起的压缩列表节点来保存,第一个节点保存member,第二个保存score。ziplist内的集合元素按score从小到大排序,score较小的排在表头位置。 数据量大的时候使用跳跃列表skiplist和哈希表hash_map结合实现,查找删除插入的时间复杂度都是O(longN)

Redis使用跳表而不使用红黑树,是因为跳表的索引结构序列化和反序列化更加快速,方便持久化。

搜索

跳跃表按 score 从小到大保存所有集合元素,查找时间复杂度为平均 O(logN),最坏 O(N) 。

插入

  选用链表作为底层结构支持,为了高效地动态增删。因为跳表底层的单链表是有序的,为了维护这种有序性,在插入前需要遍历链表,找到该插入的位置,单链表遍历查找的时间复杂度是O(n),同理可得,跳表的遍历也是需要遍历索引数,所以是O(logn)。

删除

  如果该节点还在索引中,删除时不仅要删除单链表中的节点,还要删除索引中的节点;单链表在知道删除的节点是谁时,时间复杂度为O(1),但针对单链表来说,删除时都需要拿到前驱节点O(logN)才可改变引用关系从而删除目标节点。


  • [ 萱儿AXW ]