[编程技术] 秒杀面试官系列 - Redis zset底层是怎么实现的
作者:CC下载站 日期:2023-01-08 01:00:51 浏览:5 分类:编程开发
前言
如果Redis面试有什么必问的面试题,那么Zset的底层原理一定是排名前三的。
在上篇文章中我们提到ZSET
的底层实现是ziplist
压缩列表 和skiplist
跳跃表。
这篇文章我们就详细讲解:
- zset是什么
- zset怎么用
- zset的原理是什么。
让你轻松秒杀面试官,获得Offer。
Zset是什么
Redis ZSET(Sorted Set) 有序集合,ZSET中的成员是有序排列的。
它和 set 集合的相同之处在于:
- 集合中的每一个成员都是字符串类型,并且不允许重复
而它们最大区别是:
- zset 是有序的
- set 是无序的
这是因为有序集合中每个成员都会关联一个 double64
(双精度浮点数)类型的 score
(分数值),Redis 正是通过 score 实现了对集合成员的排序。
zset 是 Redis 常用数据类型之一,它适用于排行榜类型的业务场景。
Redis 使用以下命令创建一个有序集合:
1
127.0.0.1:6379> ZADD key score member [score member ...]
- key:指定一个键名;
- score:分数值,用来描述 member,它是实现排序的关键;
- member:要添加的成员(元素)。
也就是,一个ZSET里面有多个 <score, member>
的pair,通过member的score来排序,最终你就得到了一个有序的member列表。
比如:member是用户ID,score是这个用户ID的战力值。那么你使用ZSET很容易得到一个排行榜,以score排好序的member列表
当 key 不存在时,将会创建一个新的有序集合,并把分数/成员(score/member)添加到有序集合中;当 key 存在时,但 key 并非 zset 类型,此时就不能完成添加成员的操作,同时会返回一个错误提示。
在有序集合中,成员是唯一存在的,但是分数(score)却可以重复。有序集合的最大的成员数为 2^32 - 1 (大约 40 多亿个)。
**注意:**score使用的是double64,精度只有53bit
。所以如果使用int64作为score的请注意,不要超过53位,否则会截断,导致排序不符合你的预期。
官网页面有描述精度问题:Redis ZAdd
ZSET怎么用
最简单的增删改查,统计数量,范围查找,交集并集等等都是很方便的支持的。完成的命令可以查看官网文档:sorted-set
几个最简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# key: fighting_rank, member: ana, score: 100000
127.0.0.1:6379> ZADD fighting_rank 100000 "ana"
# 取排行榜前三名
127.0.0.1:6379> ZREVRANGE fighting_rank 0 2
1) "fff"
2) "eee"
3) "ddd"
# zset长度
127.0.0.1:6379> ZCARD fighting_rank
4
# 取某一个member的score
127.0.0.1:6379> ZSCORE fighting_rank "aaa"
100000.0
你也可以自己在线测试Redis的命令来熟悉。https://try.redis.io/
ZSET底层实现
我们讲了ZSET是什么,以及ZSET怎么用,相信你已经在脑海中对Redis ZSET有了初步的概念。那下一步我们就要讲解ZSET的核心,底层是怎么实现的。让你了解这个数据结构的设计有多优秀。
上面我们说过,ZSET底层使用ziplist和skiplist实现。
并不是两个都会用到,而是二选一的。在满足以下条件的时候会使用ziplist:
- 保存元素(member)数量小于128
- 每个元素(member)长度小于64字节
注意:这两个参数可以通过redis.conf
里面的zset-max-ziplist-entries
选项和 zset-max-ziplist-value
进行修改。
1
2
3
4
5
# Similarly to hashes and lists, sorted sets are also specially encoded in
# order to save a lot of space. This encoding is only used when the length and
# elements of a sorted set are below the following limits:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
1. ziplist 压缩列表
压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry)。ziplist就是为了节省内存redis会用时间换空间的典型例子。
解决问题
- 内存紧凑型列表,节省内存空间、提升内存使用率
存在问题
- 查询效率O(n)
- 内存重分配
- 连锁更新
当 ziplist
保存的元素过多时,查找中间数据的复杂度就增加了。更糟糕的是,如果 ziplist 里面保存的是字符串,ziplist 在查找某个元素时,还需要逐一判断元素的每个字符,这样又进一步增加了复杂度。查询效率O(n)
。
所以Redis zset使用ziplist要限制元素的总数量和每个元素的长度上限。因为超过这个值之后使用ziplist的优势就没有了,就会自动切换成skiplist。
这里你应该明白了,为什么在数据量和长度比较小的时候使用ziplist了,因为Redis的瓶颈之一就是内存,所以Redis的设计极致的考虑了内存的使用。在数据量较少的时候,ziplist充分体现了它内存的优势,又不会影响整体的查询效率。
zset中entry的添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, double score) {
// some code hide here
// ...
/* Keep offset relative to zl, as it might be re-allocated. */
offset = eptr-zl;
zl = ziplistInsert(zl,eptr,(unsigned char*)ele,sdslen(ele));
eptr = zl+offset;
/* Insert score after the element. */
serverAssert((sptr = ziplistNext(zl,eptr)) != NULL);
zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);
return zl;
}
自动转换编码格式
如果发现满足ziplist的规则(长度小于等于128且最大的元素的长度小于等于64)
1
2
3
4
5
6
7
8
9
10
11
/* Convert the sorted set object into a ziplist if it is not already a ziplist
* and if the number of elements and the maximum element size is within the
* expected ranges. */
void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;
zset *zset = zobj->ptr;
if (zset->zsl->length <= server.zset_max_ziplist_entries &&
maxelelen <= server.zset_max_ziplist_value)
zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);
}
可以看到,在作为zset实现的时候,每当插入一个<member, score>
pair,就insert了两个连续的entry,第一个为member,第二个为score。
总结
ziplist是为节省内存空间而生的。 ziplist是一个为Redis专门提供的底层数据结构之一,本身可以有序也可以无序。当作为list和hash的底层实现时,节点之间没有顺序;当作为zset的底层实现时,节点之间会按照大小顺序排列。 如果长度变化的时候会检查是否满足规则,底层结构会在ziplist和skiplist之间转换。
2. skiplist 跳跃表
概念
跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其它节点的指针,从而达到快速访问节点的目的。
跳跃表支持平均O(logN)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。
如果任意一个条件不满足,就会Convert为SKIPLIST
底层实现。
1
2
3
4
5
6
7
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
if (sdslen(ele) > server.zset_max_ziplist_value)
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
这里的OBJ_ENCODING_SKIPLIST
,实际上是skiplist
(跳跃表)+dict
(字典)。
为什么要这么设计呢?我们知道数据量大的时候才会使用skiplist,skiplist的查询时间复杂度为O(logN)。
dict
保存着member到score的映射,这样就可以使用O(1)
的复杂度来查找member对应的score值。虽然同时使用两种结构,但它们会通过指针来共享相同元素的 member 和 score,因此不会浪费额外的内存。
1
2
3
tmp = zuiNewSdsFromValue(&zval);
znode = zslInsert(dstzset->zsl,score,tmp);
dictAdd(dstzset->dict,tmp,&znode->score);
就是为了性能而做的考虑。
为什么使用跳跃表(skiplist)而不使用平衡树
Redis使用SkipList而不是用平衡树的主要原因有:
- 平衡树不适合范围查找。需要中序遍历继续寻找其它节点。而Skiplist就非常简单了,使用
lv1
的指针进行向右遍历即可。 - 平衡树的插入和删除引发子树调整,逻辑复杂,SkipList相对简单很多
- 平衡树每个节点包含两个指针,SkipList平均不到2个指针,内存上更有优势。
- 从算法实现难度上来比较,Skiplist 比平衡树要简单得多。
结论
看了Redis ZSET的实现,不得不感叹,Redis的设计真的优秀,每个细节都考虑到了,对内存的使用有着完美的执着。这也就是为什么Redis能火,成为互联网公司必使用的技术之一。也是程序员面试的必面类型之一。了解Redis不仅能让你轻松通过面试,也能让你掌握更多知识,轻松应付工作中的各种需求。
<全文完>
猜你还喜欢
- 03-29 [编程相关] Winform窗体圆角以及描边完美解决方案
- 03-29 [前端问题] has been blocked by CORS policy跨域问题解决
- 03-29 [编程相关] GitHub Actions 入门教程
- 03-29 [编程探讨] CSS Grid 网格布局教程
- 10-12 [编程相关] python实现文件夹所有文件编码从GBK转为UTF8
- 10-11 [编程算法] opencv之霍夫变换:圆
- 10-11 [编程算法] OpenCV Camshift算法+目标跟踪源码
- 10-11 [Python] python 创建 Telnet 客户端
- 10-11 [编程相关] Python 基于 Yolov8 + CPU 实现物体检测
- 03-15 [脚本工具] 使用go语言开发自动化脚本 - 一键定场、抢购、预约、捡漏
- 01-08 [编程技术] 秒杀面试官系列 - Redis zset底层是怎么实现的
- 01-05 [编程技术] 《Redis设计与实现》pdf
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[课程] 爆款视频前三秒如何设计50招_短视频运营婧姐
[游戏娱乐] 《辐射4次世代版》v1.10.980中文版
[影视推荐] 果断收藏!全球恐怖新片,这15部最强
[小说] 《江南作品全集》
[绘画] 油画棒原创作品【绽放】图文教程来咯~
[资料] 非常多行业-工作总结word 共736份,个人部门述职范文,完整内容直接套用
[书籍] 双色球中奖分析与擒号秘技全图解(实用案例全新版)
[课程] 解锁AI未来:14门顶尖的平台付费AI课程全收录
[游戏娱乐] 《沙漠大冒险》v1.0.3中文版
[游戏娱乐] 《野卡橄榄球》v20240423原版英文
[资料] [大学期末救急课] 猴博士+高斯课堂+斐多课堂,全集视频合集
[云资源] 价值2万元的老男孩Python教程
[书库] 史上最全摄影书推荐(附700本PDF版打包下载)
[云资源] 花了一千多元买的私人健身教程
[下载工具] Internet Download Manager 6.42.7 (IDM)
[影视] 灌篮高手 WEB-DL版下载/Slam Dunk/スラムダンク/灌篮高手:THE FIRST/灌篮高手电影版 2022 The First Slam Dunk 61.35G
[即时通讯] 腾讯QQ PC版9.7.22.29315去广告绿色纯净版
[开发环境] PhpStorm2023中文激活版v2023.3.3 正式版
[资料] 3000 套电影电视剧 LOGO 宣传片常用音效合集包
[安卓软件] 酷我音乐APP_v10.7.6.4 去广告破解豪华VIP版
[云资源] 价值2万元的老男孩Python教程
[影视] 灌篮高手 WEB-DL版下载/Slam Dunk/スラムダンク/灌篮高手:THE FIRST/灌篮高手电影版 2022 The First Slam Dunk 61.35G
[云资源] 花了一千多元买的私人健身教程
[书库] 史上最全摄影书推荐(附700本PDF版打包下载)
[动画] 北斗神拳(1984) [两季合集] [MKV]
[资料] 抗战阵亡将士资料+续编
[电视剧] 三体 (2024) 全8集 网飞版本 中文字幕 合集
[影视] 三大队 WEB-DL版下载/Endless Journey/请转告局长,三大队任务完成了 2023 三大队 6.7G
[纪录片] 河西走廊【10集 国语 中文字幕 1080P 10.8G MP4】
[安卓软件] OfficeSuite中文版APP v14.2.50872.0破解版
- 最新评论
-
我想看看mw2ddyy 评论于:04-26 好东西阿zfy123123 评论于:04-18 谢谢楼主xiaoqi 评论于:04-12 勿在线解压,勿手机解压,请在电脑上用最新款压缩软件解压!推荐360压缩或者好压CC下载站 评论于:04-10 无法解压啊,客服能不能给个解压教程ravengrey 评论于:04-10 谢谢支持!!CC下载站 评论于:03-26 很棒的资源,感谢分享云体风身 评论于:03-26 感谢分享,好东西云体风身 评论于:03-26 谢谢支持!CC下载站 评论于:03-14 央视精品,感谢付出提供。qwer9009 评论于:03-14
- 热门tag