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请求后,当 ...
随机推荐
- Karmada v1.2发布:开启全文本搜索新纪元
摘要:Karmada v1.2 版本对调度器能力做了较大增强,初步提供了分布式搜索引擎支持,此外还借助聚合API提供了诸如 logs, watch等实用的命令行工具,资源解释器(Resource In ...
- JS对象拷贝:深拷贝和浅拷贝
摘要:对象拷贝,简而言之就是将对象再复制一份,但是,复制的方法不同将会得到不同的结果. 本文分享自华为云社区<js对象深浅拷贝,来,试试看!>,作者: 北极光之夜.. 一.速识概念: 对象 ...
- HanLP — HMM隐马尔可夫模型 -- 预测
https://www.bilibili.com/video/BV1aP4y147gA?p=8
- Mindjet MindManager 拖动页面
常规的软件是按住空格建+鼠标左健 进行拖放,但 MindManager 不支持,如何对Mindjet MindManager 拖动页面? 按住 鼠标右键 直接拖拽 配合 Ctrl+滚轮 放大缩小,一起 ...
- PPT 光效果
点状.线状.面状.光影 "光" = PPT高大上的秘密
- Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter
Caused by: java.lang.NoClassDefFoundError: javax/servlet/Filter at java.lang.Class.getDeclaredMethod ...
- 【Java爬虫】如何通过 API 递归分页爬取网页数据
前言 在最近的互联网项目开发中,需要获取用户的访问ip信息进行统计的需求,用户的访问方式可能会从微信内置浏览器.Windows浏览器等方式对产品进行访问. 当然,获取这些关于ip的信息是合法的.但是, ...
- Flask依赖的核心模块werkzeug
Flask的web框架实现 Flask是python Web框架,自然要遵 WSGI 规范,需要同时实现服务端程序+应用程序,并且要丰富完善请求和响应的处理. Flask自身并没有实现WSGI,而是依 ...
- Ipa Guard使用手册
使用手册 开始使用ipa guard 代码混淆界面介绍 文件混淆-界面介绍 安装和登录Ipa Guard 相关教程 下载安装Ipa Guard ipaguard注册和登录 下载安装Ipa Guar ...
- php基础之PHP语言学习介绍
前言 PHP是网络安全中需要掌握的一门语言,但是就这么一点儿时间学网络安全,所以不可能特别精通PHP,这里并不是说要求你精通PHP,但是需要对于一些基础代码能够认识.能够编写那么就可以了. 同时,这里 ...