全文目录:
-
- 开篇语
- [🌟 前言](#🌟 前言)
- [📜 目录](#📜 目录)
- [💡 什么是ZSet?](#💡 什么是ZSet?)
- [🧩 ZSet的底层数据结构](#🧩 ZSet的底层数据结构)
-
- [1. 小数据量时使用 ziplist](#1. 小数据量时使用 ziplist)
-
- [ziplist 结构](#ziplist 结构)
- [2. 大数据量时使用 skiplist + hash table](#2. 大数据量时使用 skiplist + hash table)
-
- 跳表(skiplist)
- [哈希表(hash table)](#哈希表(hash table))
- [🛠️ ZSet核心操作与复杂度](#🛠️ ZSet核心操作与复杂度)
- [🚀 ZSet应用场景](#🚀 ZSet应用场景)
-
- [1. 排行榜系统](#1. 排行榜系统)
- [2. 延迟队列](#2. 延迟队列)
- [3. 社交平台中的热门话题](#3. 社交平台中的热门话题)
- [4. 任务调度系统](#4. 任务调度系统)
- [🔍 ZSet在Redis内存中的优化](#🔍 ZSet在Redis内存中的优化)
- [🎉 总结](#🎉 总结)
- 文末
开篇语
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
🌟 前言
大家在使用Redis时,可能会接触到一种非常有用的数据结构:有序集合(ZSet) 。与普通集合(Set)相比,ZSet不仅能够存储不重复的元素,还可以为每个元素关联一个评分(Score),并通过这个评分实现自动排序。这样的设计使得ZSet在排行榜、优先级队列等场景中广泛应用。那么,ZSet是如何在内部实现高效的排序和存储的呢?今天我们深入剖析ZSet的底层结构,一探究竟!💡
📜 目录
- 💡 什么是ZSet?
- 🧩 ZSet的底层数据结构
- 🛠️ ZSet核心操作与复杂度
- 🚀 ZSet应用场景
- 🔍 ZSet在Redis内存中的优化
- 🎉 总结
💡 什么是ZSet?
在Redis中,ZSet (Sorted Set)是一种有序的集合 。它和普通的Set一样,不允许元素重复,但与Set不同的是,ZSet中的每个元素都有一个分数(Score)。ZSet依据这个分数对元素进行排序,并支持根据分数范围进行快速查询。
ZSet 的数据结构支持以下特性:
- 去重:元素不可重复。
- 排序:元素按分数自动排序。
- 范围查询:支持通过分数或排名范围进行快速查询。
ZSet 实现了自动排序功能,且可以灵活地根据需求来取出分数范围或排名范围内的元素,非常适合实现排行榜和优先级队列等功能。
🧩 ZSet的底层数据结构
Redis 中的 ZSet 底层数据结构主要由 跳表(Skip List) 和 哈希表(Hash Table) 共同实现。根据数据量大小和存储需求,Redis会在不同场景下切换底层实现结构,以平衡内存和性能。
1. 小数据量时使用 ziplist
ziplist(压缩列表)是一种压缩后的双向链表结构,用于存储少量数据的 ZSet,以减少内存开销。当以下条件同时满足时,ZSet 使用 ziplist:
- 元素数量 :小于
zset-max-ziplist-entries
(默认128)。 - 分数大小 :分数的字符串长度小于
zset-max-ziplist-value
(默认64字节)。
ziplist 结构
在 ziplist 中,数据是以压缩格式连续存储的,通过减少内存分配的碎片化来降低内存使用。其特点如下:
- 连续存储:所有元素和分数的存储在一段连续的内存中。
- 空间效率高:适用于少量数据的场景。
- 读写性能较低:ziplist 中每次插入都需调整位置,适合存储少量静态数据。
注意:ziplist 的效率在数据量较小时表现较优,但不适用于大数据量的动态操作。
2. 大数据量时使用 skiplist + hash table
当数据量较大或分数较长时,Redis会自动切换为 跳表(skiplist)+ 哈希表(hash table) 的结构,这种组合可以兼顾插入、删除的速度与有序性。
跳表(skiplist)
跳表是一种分层链表结构,通过多级索引加速查找,是 ZSet 排序的核心组件。
- 层级设计:跳表包含多层链表,每一层是对下层的索引,层级越高跨度越大。
- 插入与删除:插入、删除时间复杂度为 O(log N)。
- 排序:跳表按分数从低到高进行排序,通过分层索引快速定位元素位置。
哈希表(hash table)
哈希表在 ZSet 中用于存储元素和分数的映射关系,便于查找元素。
- 查找效率高:Hash Table 中的查找复杂度为 O(1)。
- 数据一致性:Hash Table 存储键值对确保每个元素的分数准确,跳表则维护元素的有序性。
🛠️ ZSet核心操作与复杂度
ZSet 提供了多种操作,以便对有序数据进行快速访问。常见操作及其时间复杂度如下:
操作 | 命令 | 时间复杂度 | 说明 |
---|---|---|---|
插入/更新 | ZADD |
O(log N) | 插入或更新分数 |
删除元素 | ZREM |
O(log N) | 删除指定元素 |
获取排名 | ZRANK / ZREVRANK |
O(log N) | 获取指定元素的排名 |
获取分数范围 | ZRANGEBYSCORE |
O(log N + M) | 获取分数在指定范围内的元素 |
获取元素范围 | ZRANGE / ZREVRANGE |
O(log N + M) | 按排名获取元素,支持倒序 |
计数 | ZCOUNT |
O(log N) | 统计指定分数范围的元素数量 |
取最大/最小值 | ZPOPMAX / ZPOPMIN |
O(log N) | 获取并删除最大/最小分数的元素 |
注意 :
N
为集合的元素数量,M
为范围查询的结果数量。跳表和哈希表的组合让这些操作在 ZSet 中变得非常高效!
🚀 ZSet应用场景
ZSet的有序和范围查询能力使其成为多种应用场景的理想选择。以下是一些典型的使用场景:
1. 排行榜系统
ZSet 的有序性和按分数排序的特性非常适合实现排行榜。比如游戏排行榜中,玩家的积分作为分数,玩家名作为成员,通过 ZSet 可以实现排名查询、范围查询等操作。
ZADD
:添加或更新玩家的分数。ZRANK
/ZREVRANK
:获取玩家的排名。ZRANGE
/ZREVRANGE
:获取排名前几名的玩家。
2. 延迟队列
通过 ZSet 可以构建一个延迟队列,消息的分数设为到期时间戳。查询到期消息时,只需查找分数小于当前时间的元素。
ZADD
:将消息及其执行时间添加到队列。ZRANGEBYSCORE
:获取即将到期的消息。ZREM
:执行后从队列中删除。
3. 社交平台中的热门话题
在社交媒体应用中,通过 ZSet 可以实现"热门话题"榜单,话题作为成员,热度分数作为评分值。定期更新 ZSet,可以动态展示热门话题。
ZINCRBY
:增加话题热度。ZRANGE
/ZREVRANGE
:获取热度排行靠前的热门话题。
4. 任务调度系统
ZSet 还可用于任务调度,将任务按优先级或到期时间排序,以决定任务的执行顺序。
🔍 ZSet在Redis内存中的优化
Redis对ZSet的内存使用进行了多项优化,包括:
- ziplist优化:在小数据量下,ZSet 使用 ziplist 实现,减少内存开销。
- skiplist与hash table的组合:对于大数据量使用跳表+哈希表,确保性能与内存使用的平衡。
- 分数存储压缩:ZSet 中的分数存储采用压缩算法,在性能和空间上达到优化。
小贴士:我们可以通过配置
zset-max-ziplist-entries
和zset-max-ziplist-value
来控制 Redis 切换 ZSet 底层数据结构的条件,以适应不同的应用场景。
🎉 总结
Redis的ZSet数据结构通过跳表和哈希表的组合实现了高效的排序和查询,非常适合处理需要排序或优先级的场景。无论是排行榜、延迟队列还是热门话题,ZSet 都能轻松应对。掌握其底层实现与常见操作,我们可以在实际应用中发挥 ZSet 的最大潜力!
希望这篇解析能帮助你更好地理解和使用Redis的ZSet!Happy Coding!
... ...
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
... ...
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。