1、redis数据类型
| 类型 | 底层 | 应用场景 | 编码类型 |
|---|---|---|---|
| String | SDS数组 | 帖子、评论、热点数据、输入缓冲 | RAW << EMBSTR << INT |
| List | QuickList | 评论列表、商品列表、发布与订阅、慢查询、监视器 | LINKEDLIST << ZIPLIST |
| Set | intSet | 适合交集、并集、查集操作,例如朋友关系 | HT << INSET |
| Zset | 跳跃表 | 去重后排序,适合排名场景 | SKIPLIST << ZIPLIST |
| Hash | 哈希 | 结构化数据,比如存储对象 | HT << ZIPLIST |
| Stream | 紧凑列表 | 消息队列 |
2、相关API
| String | SET | SETNX | SETEX | GET | GETSET | INCR | DECR | MSET | MGET |
| Hash | HSET | HSETNX | HGET | HDEL | HLEN | HMSET | HMGET | HKEYS | HGETALL |
| LIST | LPUSH | LPOP | RPUSH | RPOP | LINDEX | LREM | LRANGE | LLEN | RPOPLPUSH |
| ZSET | ZADD | ZREM | ZSCORE | ZCARD | ZRANGE | ZRANK | ZREVRANK | ZREVRANGE | |
| SET | SADD | SREM | SISMEMBER | SCARD | SINTER | SUNION | SDIFF | SPOP | SMEMBERS |
| 事务 | MULTI | EXEC | DISCARD | WATCH | UNWATCH |
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 ]