redis 6源码解析之 ziplist
ziplist
ziplist结构
ziplist的布局如下,所有的字符默认使用小端序保存:
+--------+--------+--------+--------+-------+-------+-------+
|zlbytes | zltail | zllen | entry | ... | entry | zlend |
+--------+--------+--------+--------+-------+-------+-------+
uint32_t
zlbytes:为一个无符号整数。保存了ziplist占用的字节数,包含zlbytes字段本身占用的4个字节。主要用于调整数据结构的大小。uint32_t
zltail:最后一个entry的字节偏移量(非zlend)。用于从list的另一端执行pop操作(即倒序遍历)uint16_t
zllen:entry的数目。当保存的entry大于216-2个entry时,则将该值设置为216-1,此时需要遍历整个entry list来计算list中的entry数目uint8_t
zlend:表示ziplist中的最后一个entry。字节编码等同于255(即FF)。表示ziplist的结束符
ziplist中的每个entry都使用一个元数据作为前缀,该元数据包含两部分的信息:首先保存了前一个entry的长度,用于倒序查找;再者保存了entry的编码类型,表示entry的类型,如整数或字符串,当编码类型为字符串时,该字段也表示了字符串的长度。字符串的entry-data的长度就等同于该字符串的长度,而整数的entry-data的长度需要根据编码类型进行判断,并不一定等同于其entry-data字符串的长度(见下文encoding)。一个完整的entry为:
+--------+--------+----------+
|prevlen |encoding|entry-data|
+--------+--------+----------+
有时编码类型即表示entry本身(例如小的整数),这种情况下会忽略entry-data字段,此时entry变为:
+--------+--------+
|prevlen |encoding|
+--------+--------+
prevlen
prevlen表示前一个entry的长度,使用如下方式进行编码:当前一个entry的长度小于254(255是个特殊字符,被zlend使用)字节时,该字段会使用一个字节(即8 bit)表示长度;当长度大于或等于254时,将会使用5个字节,此时第一个字节会被设置为254(FE)来表示一个较大的数值,后续4个字节表示前面一个entry的长度。
因此,prevlen的编码为:
如果前一个entry的长度小于254,编码为:
+-------+--------+-----+
|prevlen|encoding|entry|
+-------+--------+-----+
如果前一个entry的长度大于254,编码如下:
+----+---------------+--------+-----+
|0xFE|4 bytes prevlen|encoding|entry|
+----+---------------+--------+-----+
encoding
entry的encoding字段取决于entry的内容。当entry为字符串时,encoding的第一个字节的前2bit保存了编码类型,剩余的bit位表示字符串的长度。当entry为整数时,encoding仅占用1个字节,encoding的前2bit都设置为1,后续的2bit用于指定整数的类型,如int16_t,int32_t。encoding中的第一个字节总是用于判定entry的类型。举例如下:
* |00pppppp| - 1 byte
* 字符串的长度小于或等于63字节(6 bits).
* "pppppp" 表示6bit长度的无符号整数.
* |01pppppp|qqqqqqqq| - 2 bytes
* 字符串的长度小于或等于16383字节(14 bits).
* IMPORTANT: 14 bit的数字使用大端序保存.
* |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes
* 字符串的长度大于或等于16384字节,只使用第1个字节之后的4个字节表示长度,最大为32^2-1,第一个
* 字节的低6位没有使用,设置为0。因此entry的最大长度为32
* IMPORTANT: 32 bit的数字使用大端序保存.
* |11000000| - 3 bytes
* 整数编码为int16_t (2 bytes).
* |11010000| - 5 bytes
* 整数编码为int32_t (4 bytes).
* |11100000| - 9 bytes
* I整数编码为int64_t (8 bytes).
* |11110000| - 4 bytes
* 编码为24 bit的有符号整数 (3 bytes).
* |11111110| - 2 bytes
* 编码为8 bit的有符号整数 (1 byte).
* |1111xxxx| - (xxxx 取值为 0000 到 1101) 表示4bit的整数
* 无符号整数的取值为0到12,由于无法使用0000(被|11110000|编码占用)和1111(被zlend占用),因此取值
* 为1到13,因此需要从低4位的整数减去1获得entry的值.
* |11111111| - 表示ziplist的终止entry,即zlend
举例
整数编码
如下ziplist包含2个元素,表示字符串"2"和"5",长度为15字节,可以看到由于数值小于13,其编码和数值放在了一个字节中。
[0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]
| | | | | |
zlbytes zltail entries "2" "5" end
前4个字节(zlbytes)表示15,即整个ziplist包含的字节数;第2个4字节(zltail)最后一个entry的字节偏移,即字符串为"5"的entry的位置,偏移量为12字节;接下来的16bit(entries)表示ziplist中的entry的数目,为2;"00 f3"表示list中的第一个entry "2",它包含了前一个entry的长度(prevlen),为0,"f3"对应的编码为"|1111xxxx|","xxxx"的取值为0001到1101,去除前4个bit "1111",并减去1,得到entry的值为2。下一个entry的prevlen为2,表示前一个entry占用了2字节."f6"的编码与前一个相同,去除前4个bit,并减去1,得到entry的值为5;最后的"ff"表示ziplist的结束(zlend)。
字符串编码
在上述ziplist中追加一个"Hello World"的entry的编码。第一个字节表示前面entry的长度,第二个字节表示encoding,二进制为"|00pppppp|",因此"0b"表示一个11字节的字符串。从第3个字节(48)到最后一个字节(64)表示ASCII编码的字符串"Hello World"。
[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]
源码解析参见:ziplist.c
redis 6源码解析之 ziplist的更多相关文章
- Redis源码解析之ziplist
Ziplist是用字符串来实现的双向链表,对于容量较小的键值对,为其创建一个结构复杂的哈希表太浪费内存,所以redis 创建了ziplist来存放这些键值对,这可以减少存放节点指针的空间,因此它被用来 ...
- Redis系列(十):数据结构Set源码解析和SADD、SINTER、SDIFF、SUNION、SPOP命令
1.介绍 Hash是以K->V形式存储,而Set则是K存储,空间节省了很多 Redis中Set是String类型的无序集合:集合成员是唯一的. 这就意味着集合中不能出现重复的数据.可根据应用场景 ...
- .Net Core缓存组件(Redis)源码解析
上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...
- Redis系列(九):数据结构Hash源码解析和HSET、HGET命令
2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...
- Redis 源码解析之通用双向链表(adlist)
Redis 源码解析之通用双向链表(adlist) 概述 Redis源码中广泛使用 adlist(A generic doubly linked list),作为一种通用的双向链表,用于简单的数据集合 ...
- Redis 动态字符串 SDS 源码解析
本文作者: Pushy 本文链接: http://pushy.site/2019/12/21/redis-sds/ 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可 ...
- Ocelot简易教程(七)之配置文件数据库存储插件源码解析
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储 ...
- t-io 集群解决方案以及源码解析
t-io 集群解决方案以及源码解析 0x01 概要说明 本博客是基于老谭t-io showcase中的tio-websocket-showcase 示例来实现集群.看showcase 入门还是挺容易的 ...
- .Net Core缓存组件(MemoryCache)源码解析
一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...
- Spring-Session实现Session共享实现原理以及源码解析
知其然,还要知其所以然 ! 本篇介绍Spring-Session的整个实现的原理.以及对核心的源码进行简单的介绍! 实现原理介绍 实现原理这里简单说明描述: 就是当Web服务器接收到http请求后,当 ...
随机推荐
- 一文带你认识MindSpore新一代分子模拟库SPONGE
[本期推荐专题]物联网从业人员必读:华为云专家为你详细解读LiteOS各模块开发及其实现原理. 摘要:基于MindSpore自动并行.图算融合等特性,SPONGE可高效地完成传统分子模拟过程,利用Mi ...
- 在openEuler上做开发?这个大赛拿出30万寻找开源的yyds
摘要:为鲲鹏产业生态的一部分,在服务器操作系统层面,开源操作系统openEuler更是令人期待. 本文分享自华为云社区<在openEuler上做开发?这个大赛拿出30万寻找开源的yyds> ...
- 一文详解Java日志框架JUL
摘要:JUL(Java util logging),Java原生日志框架,不需要引入第三方依赖包,使用简单方便. 本文分享自华为云社区<Java 日志框架 JUL 详解大全>,作者: 陈皮 ...
- 让快更快,火山引擎 ByteHouse 为 ClickHouse 提速
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 近日,火山引擎数智平台VeDI与DataFun联合举办以"OLAP计算引擎"为主题的直播活 ...
- Solon 开发进阶,三、常用配置说明
Solon 开发进阶 一.插件扩展机制 二.体外扩展机制 三.常用配置说明 四.启动参数说明 五.全局异常订阅 应用主配置文件为: resources/app.yml( 或 app.propertie ...
- Codeforces 1092C Prefixes and Suffixes【字符串+思维】
题目链接:点这里 题意:理解错了题意导致WA好几发,QAQ暴击 题意是判断给你的2*n-2个字符串是前缀还是后缀,不是判断这个字符串的内容...我真的欲哭无泪,理解能力太菜了 思路:将两个n-1长的字 ...
- chage详解:liunx账户密码过期时处理
公司安装elasticSearcher7.10.2版本时提示账户密码过期,可以做以下的处理方式: 一.查看账户的使用情况 chage -l baikang [root@localhost ~]# ch ...
- Servlet系列:生命周期(init、 service、destroy)详解
Servlet的生命周期是由Web容器(如Tomcat)管理的,包括以下三个阶段: 加载和实例化:当Web应用程序启动时,Web容器会加载和实例化Servlet.加载和实例化过程可以在应用程序启动时自 ...
- C语言中的操作符:了解与实践
欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 1. 操作符的分类 操作符又叫运算符,它在C语言中起着非常大的作用,以下是 ...
- Windows 下 Outlook 点击关闭最小化和开机自动运行
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...