redis数据结构

redis的数据结构

常见的5种:
String:字符串,最基础的数据类型。
List:列表。
Hash:哈希对象。
Set:集合。
Sorted Set:有序集合,Set 的基础上加了个分值。
高级的4种:
HyperLogLog:通常用于基数统计。使用少量固定大小的内存,来统计集合中唯一元素的数量。统计结果不是精确值,而是一个带有0.81%标准差(standard error)的近似值。所以,HyperLogLog适用于一些对于统计结果精确度要求不是特别高的场景,例如网站的UV统计。
Geo:redis 3.2 版本的新特性。可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作:获取2个位置的距离、根据给定地理位置坐标获取指定范围内的地理位置集合。
Bitmap:位图。
Stream:主要用于消息队列,类似于 kafka,可以认为是 pub/sub 的改进版。提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。

Sorted Set底层数据结构

Sorted Set(有序集合)当前有两种编码实现:ziplist、skiplist

ziplist:使用压缩列表实现,当保存的元素长度都小于64字节,同时数量小于128时,使用该编码方式,否则会使用 skiplist。这两个参数可以通过 zset-max-ziplist-entries、zset-max-ziplist-value 来自定义修改。

ziplist数据结构:
(1).zlbytes:记录了压缩列表占用的内存字节数,在对压缩列表进行内存重分配,或者计算zlend的位置时使用。它本身占了4个字节;
(2).zltail:记录了尾节点(entry)至起始节点(entry)的偏移量。通过这个偏移量,可以快速确定最后一个entry节点的地址;
(3).zllen:记录了entry节点的数量。当zllen的值小于65535时,这个值就表示节点的数量。当zllen的值大于65535时,节点的真实数量需要遍历整个压缩列表才能得出;
(4).entry:压缩列表中所包含的每个节点。每个节点的长度根据该节点的内容来决定;
(5).zlend:特殊值0XFF(255),标记了压缩列表的末端。表示该压缩列表到此为止;
ziplist的entry节点的结构:
(1).prev_entry_len:记录前驱节点的长度;
(2).encoding:记录当前节点的value成员的数据类型以及长度;
(3).value:根据encoding来保存字节数组或整数
ziplist的特点:
(1).压缩列表ziplist结构本身就是一个连续的内存块,由表头(记录了压缩列表的一些信息,比如zlbytes压缩列表占用的内存字节
数,zltail起始节点到尾结点的偏移量,zllen记录了entry节点的个数)、若干个entry节点和压缩列表尾部标识符zlend组成,通过(8种)一系列编码规则,提高内存的利用率,使用于存储整数和短字符串;
(2).压缩列表ziplist结构的缺点是:每次插入或删除一个元素时,都需要进行频繁的进行内存的扩展或减小,然后还需要进行内存的
整理,造成严重效率的损失,所以redis默认情况下在元素个数小于128时才使用ziplist,在元素大于128时使用skiplist;

skiplist:zset实现,一个zset同时包含一个字典(dict)和一个跳跃表(zskiplist)

skiplist数据结构:
(1).是一个多层链表的数据结构,每个节点的level是通过一定的概率随机产生的(使用抛硬币的方式);
(2).每一层都是一个有序的链表,默认是升序;
(3).最底层(Level 1)的链表包含所有元素;
(4).跳跃表的空间复杂度为 O(n);
(5).跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找;
在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树;
跳跃表 vs 红黑树
红黑可以说是二叉查找树的一种变形,红黑在查找,插入,删除也是近似O(logn)的时间复杂度,但学过红黑树的都知道,
红黑树比跳跃表复杂多了。
但红黑树插入,删除结点时,是通过调整结构来保持红黑树的平衡,比起跳跃表直接通过一个随机数来决定跨越几层,在时间复杂度的花销上是要高于跳跃表的。
当然,红黑树并不是一定比跳跃表差,在有些场合红黑树会是更好的选择,所以选择一种数据结构,关键还得看场合。
总上所述,维护一组有序的集合,并且希望在查找、插入、删除等操作上尽可能快,那么跳跃表会是不错的选择。

Sorted Set为什么同时使用字典和跳跃表

主要是为了性能。
单独使用字典:在执行范围型操作,比如zrank、zrange,字典需要进行排序,至少需要O(NlogN)的时间复杂度及额外O(N)的内存空间。
单独使用跳跃表:根据成员查找分值操作的复杂度从O(1)上升为O(logN)。

Sorted Set为什么使用跳跃表,而不是红黑树

1)跳表的性能和红黑树差不多;
2)跳表更容易实现和调试;
3)红黑树插入,删除结点时,是通过调整结构来保持红黑树的平衡,比起跳跃表直接通过一个随机数来决定跨越几层,在时间复杂度的花销上是要高于跳跃表的;

Hash 对象底层结构

Hash对象当前有两种编码:ziplist、hashtable
ziplist:使用压缩列表实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的节点推入到压缩列表的表尾,然后再将保存了值的节点推入到压缩列表表尾。
因此:
1)保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后;
2)先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加的会被放在表尾方向。
hashtable:使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值来保存,跟 java 中的 HashMap 类似。