[编程技术] 秒杀面试官系列 - Redis zset底层是怎么实现的
作者:CC下载站 日期:2023-01-08 01:00:51 浏览:31 分类:编程开发
前言
如果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
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[动画] 迪士尼系列动画139部 国英双语音轨 【蓝光珍藏版440GB】
[电影] 莫妮卡贝鲁奇为艺术献身电影大合集 1080P超清 双语字幕
[电影] DC电影宇宙系列合集18部 4K 高码率 内嵌中英字幕 273G
[音乐] 【坤曲/4坤时】鸡你太美全网最全,385首小黑子战歌,黄昏见证虔诚的信徒,巅峰诞生虚伪的拥护!
[音乐] 用餐背景音乐大合集 [MP3/flac]
[书籍] 彭子益医书合集 [PDF/DOC]
[电影] 《环太平洋两部合集》 4K REMUX原盘 [杜比视界] 国英双语音轨 [内封特效字幕] [133.8G]
[电影] 异人之下 The Traveller 2024✨【影版】【4K正式版/HQ超高码/DDP5.1】✚【1080高码】无水印/无压缩
[动漫] 头文字D 动漫 (1998) S01-S06季 1080P 国粤日音轨 续作 剧场版 电影
[小说] 知轩藏书全站7667册txt小说合集精心校对版
[书籍] 彭子益医书合集 [PDF/DOC]
[游戏] 《黑神话悟空》免安装学习版【全dlc整合完整版】+Steam游戏解锁+游戏修改工具!
[动画] 《名侦探柯南》名侦探柯南百万美元的五菱星 [TC] [MP4]
[电视剧集] [BT下载][黑暗城市- 清扫魔 Dark City: The Cleaner 第一季][全06集][英语无字][MKV][720P/1080P][WEB-RAW]
[涨点姿势] 男性性技宝典:14招实战驭女术——爱抚、按摩、催情、姿势、高潮全攻略
[动画] 2002《火影忍者》720集全【4K典藏版】+11部剧场版+OVA+漫画 内嵌简日字幕
[剧集] 《斯巴达克斯》1-4季合集 无删减版 1080P 内嵌简英特效字幕
[CG剧情] 《黑神话:悟空》158分钟CG完整剧情合集 4K120帧最高画质
[短剧] 被下架·禁播的羞羞短剧·午夜短剧合集
[游戏] 黑神话悟空离线完整版+修改器
[影视] 美国内战 4K蓝光原盘下载+高清MKV版/内战/帝国浩劫:美国内战(台)/美帝崩裂(港) 2024 Civil War 63.86G
[影视] 一命 3D 蓝光高清MKV版/切腹 / 切腹:武士之死 / Hara-Kiri: Death of a Samurai / Ichimei 2011 一命 13.6G
[影视] 爱情我你他 蓝光原盘下载+高清MKV版/你、我、他她他 2005 Me and You and Everyone We Know 23.2G
[影视] 穿越美国 蓝光原盘下载+高清MKV版/窈窕老爸 / 寻找他妈…的故事 2005 Transamerica 20.8G
[电影] 《黄飞鸿》全系列合集
[Android] 开罗游戏 ▎像素风格的模拟经营的游戏厂商安卓游戏大合集
[游戏合集] 要战便战 v0.9.107 免安装绿色中文版
[书籍] 彭子益医书合集 [PDF/DOC]
[资源] 精整2023年知识星球付费文合集136篇【PDF格式】
[系统]【黑果小兵】macOS Big Sur 11.0.1 20B50 正式版 with Clover 5126 黑苹果系统镜像下载
- 最新评论
-
有靳东!嘻嘻奥古斯都.凯撒 评论于:10-28 流星花园是F4处女作也是4人集体搭配的唯一一部!奥古斯都.凯撒 评论于:10-28 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢AAAAA 评论于:10-26 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢password63 评论于:10-26 找了好久的资源,终于在这里找齐了!!!!blog001 评论于:10-21 找了好久的资源,终于在这里找齐了!!!!blog001 评论于:10-21 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢WillKwok 评论于:10-09 感谢分享1234123 评论于:10-07 太好了终于找到了谢谢Tom 评论于:10-07 谢谢分享loonghd 评论于:09-30
- 热门tag