Redis数据结构底层以及应用场景

Redis基本数据结构有:String、Hash、List、Set、Zset。
高级数据结构有:bitmap、HyperLogLog、GEO、Streams。

它们的底层是:
在这里插入图片描述底层实现:

1、字符串:Redis自己构建了一种名叫Simple dynamic string(SDS)的数据结构。
它的优点:
开发者不用担心字符串变更造成的内存溢出问题。
常数时间复杂度获取字符串长度len字段。
空间预分配free字段,会默认留够一定的空间防止多次重分配内存。

2、链表:Redis的链表在双向链表上扩展了头、尾节点、元素数等属性。
它的优点:
可以直接获得头、尾节点。
常数时间复杂度得到链表长度。

3、字典:Redis的Hash,就是在数组+链表的基础上,进行了一些rehash优化等。
特点:
Reids的Hash采用链地址法来处理冲突,然后它没有使用红黑树优化。
哈希表节点采用单链表结构。
rehash优化。
rehash:将数组a[0]迁移到a[1],先为a[1]分配内存空间,然后将a[0]中元素重新计算hash,迁移到a[1].
渐进式rehash:redis考虑到大量数据迁移带来的cpu繁忙(可能导致一段时间内停止服务),所以采用了渐进式rehash的方案,当数据进行增删改查的时候才进行迁移。

4、跳跃表:它和平衡树性能很相似。

5、整数集合:intset就是redis用于保存整数值的集合数据结构。采用encoding升级的方式:有个Int16类型的整数集合,现在要将int32类型加进这个集合,int16是存储不下的,所以就要对整数集合进行升级。

6、压缩列表(ziplist):ziplist是redis为了节约内存而开发的顺序型数据结构。

7、快速列表(quicklist):一个由ziplist组成的双向链表。但是一个quicklist可以有多个quicklist节点,它很像B树的存储方式。

高级数据结构
HyperLogLog 内部数据结构:HyperLogLog算法是一种非常巧妙的近似统计海量去重元素数量的算法。它内部维护了 16384 个桶(bucket)来记录各自桶的元素数量。当一个元素到来时,它会散列到其中一个桶,以一定的概率影响这个桶的计数值。因为是概率算法,所以单个桶的计数值并不准确,但是将所有的桶计数值进行调合均值累加起来,结果就会非常接近真实的计数值。
HyperLogLog算法中每个桶所占用的空间实际上只有 6 个 bit,这 6 个 bit 自然是无法容纳桶中所有元素的,它记录的是桶中元素数量的对数值。
了说明这个对数值具体是个什么东西,我们先来考虑一个小问题。一个随机的整数值,这个整数的尾部有一个 0 的概率是 50%,要么是 0 要么是 1。同样,尾部有两个 0 的概率是 25%,有三个零的概率是 12.5%,以此类推,有 k 个 0 的概率是 2^(-k)。如果我们随机出了很多整数,整数的数量我们并不知道,但是我们记录了整数尾部连续 0 的最大数量 K。我们就可以通过这个 K 来近似推断出整数的数量,这个数量就是 2^K。

当然结果是非常不准确的,因为可能接下来你随机了非常多的整数,但是末尾连续零的最大数量 K 没有变化,但是估计值还是 2^K。你也许会想到要是这个 K 是个浮点数就好了,每次随机一个新元素,它都可以稍微往上涨一点点,那么估计值应该会准确很多。

HyperLogLog通过分配 16384 个桶,然后对所有的桶的最大数量 K 进行调合平均来得到一个平均的末尾零最大数量 K# ,K# 是一个浮点数,使用平均后的 2^K# 来估计元素的总量相对而言就会准确很多。不过这只是简化算法,真实的算法还有很多修正因子,因为涉及到的数学理论知识过于繁多,这里就不再精确描述。

下面我们看看Redis HyperLogLog 算法的具体实现。我们知道一个HyperLogLog实际占用的空间大约是 13684 * 6bit / 8 = 12k 字节。但是在计数比较小的时候,大多数桶的计数值都是零。如果 12k 字节里面太多的字节都是零,那么这个空间是可以适当节约一下的。Redis 在计数值比较小的情况下采用了稀疏存储,稀疏存储的空间占用远远小于 12k 字节。相对于稀疏存储的就是密集存储,密集存储会恒定占用 12k 字节。

bitmap:假设已经有3个元素a、b和c,分别通过3个hash算法h1()、h2()和h2()计算然后对一个bit进行赋值,接下来假设需要判断d是否已经存在,那么也需要使用3个hash算法h1()、h2()和h3()对d进行计算,然后得到3个bit的值,恰好这3个bit的值为1,这就能够说明:d可能存在集合中。再判断e,由于h1(e)算出来的bit之前的值是0,那么说明:e一定不存在集合中。

GEO:GEO数据结构可以在Redis中存储地理坐标,并且坐标有限制,Geo本身不是一种数据结构,它本质上还是借助于Sorted Set(ZSET),并且使用GeoHash技术进行填充。Redis中将经纬度使用52位的整数进行编码,放进zset中,score就是GeoHash的52位整数值。在使用Redis进行Geo查询时,其内部对应的操作其实就是zset(skiplist)的操作。通过zset的score进行排序就可以得到坐标附近的其它元素,通过将score还原成坐标值就可以得到元素的原始坐标。总之,Redis中处理这些地理位置坐标点的思想是:二维平面坐标点 --> 一维整数编码值 --> zset(score为编码值) --> zrangebyrank(获取score相近的元素)、zrangebyscore --> 通过score(整数编码值)反解坐标点 --> 附近点的地理位置坐标。

streams:streams底层的数据结构是radix tree,用一句话概括Streams就是Redis实现的内存版kafka。Radix Tree(基数树) 事实上就几乎相同是传统的二叉树。

应用场景
String:缓存功能、计数器、共享session、
hash:用户信息等管理。
列表:消息队列、文章列表、
set:用户兴趣标签。
zset:排行榜。

猜你喜欢

转载自blog.csdn.net/qq_43360777/article/details/107846653
今日推荐