上面讲解了大半天,可能大家都觉得枯燥无味了,也可能会觉得云里雾里。这个没有关系,这些只要心里有个概念,用到的时候再查询对应资料就可以了,并不需要全部记住。接下来让我们一起通过两个例子来体会一下`ziplist`到底是如何来组织存储数据的。
下面就是一个压缩列表的存储示例,这个压缩列表里面存储了`2`个节点,节点中存储的是整数`2`和`5`:
~~~c
[0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]
| | | | | |
zlbytes zltail zllen "2" "5" end
~~~
1. 第一组`4`个字节为`zlbytes`部分,`0f`转成二进制就是`1111`也就是`15`,代表整个`ziplist`长度是`15`个字节。
2. 第二组`4`个字节`zltail`部分,`0c`转成二进制就是`1100`也就是`12`,这里记录的是压缩列表尾节点距离起始地址有多少个字节,也就是说`[02 f6]`这个尾节点距离起始位置有`12`个字节。
3. 第三组`2`个字节就是记录了当前`ziplist`中`entry`的数量,`02`转成二进制就是`10`,也就是说当前`ziplist`有`2`个节点。
4. 第四组`2`个字节`[00 f3]`就是第一个`entry`。`00`表示`0`,因为这是第`1`个节点,所以前一个节点长度为`0`,`f3`转成二进制就是`11110011`,刚好对应了表格中的编码`1111xxxx`,所以后面四位就是存储了一个`0-12`位的整数。`0011`转成十进制就是`3`,减去`1`得到`2`,所以第一个`entry`存储的数据就是`2`。
5. 第五组`2`个字节`[02 f6]`就是第二个`entry`。`02`即为`2`,表示前一个节点的长度为`2`,注意,因为这里算出来的结果是小于`254`,所以就代表了这里只用到了`1`个字节来存储上一个节点的长度(如果等于`254`,这说明接下来`4`个字节存储的才是长度),所以后面的`f6`就是当前节点的数据,转换成二进制为`11110110`,对应了表格中的编码`1111xxxx`,同样的后四位`0110`存储的是真实数据,计算之后得出是`5`。
6. 最后一组`1`个字节`[ff]`转成二进制就是`11111111`,代表这是整个`ziplist`的结尾。
假如这时候又添加了一个`Hello World`字符串到列表中,那么就会新增一个`entry`,如下所示:
~~~c
[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]
~~~
1. 第一组的`1`个字节`[02]`转成十进制就是`2`,表示前一个节点(即上面示例中的`[02 f6]`)长度是`2`。
2. 第二组的`2`个字节`[0b]`转成二进制为`00001011`。以`00`开头,符合编码`00pppppp`,而除掉最开始的两位`00`,计算之后得到十进制`11`,这就说明后面字节数组的长度是`11`。
3. 第三组刚好是`11`个字节,对应了上面的长度,所以这里就是真正存储了`Hello World`的字节数组。
- Redis 为什么这么快
- 什么是 Redis
- Redis 的安装
- Redis 到底有多快
- Redis 是单线程还是多线程
- Redis 为什么选择使用单线程来执行请求
- 什么是 IO 多路复用机制
- Redis 中 I/O 多路复用的应用
- 一个简单的字符串,为什么 Redis 要设计的如此特别
- Redis 的 9 种数据类型
- 二进制安全字符串
- sds 空间分配策略
- sds 和 C 语言字符串区别
- sds 是如何被存储的
- type 属性
- encoding 属性
- 通过牺牲速度来节省内存,Redis 是觉得自己太快了吗
- 什么是压缩列表
- ziplist 的存储结构
- entry 存储结构
- ziplist 数据示例
- ziplist 连锁更新问题
- 为了加快速度,Redis 都做了哪些“变态”设计
- 列表对象
- linkedlist
- linkedlist 和 ziplist 的选择
- quicklist
- 列表对象常用操作命令
- Redis 中哈希分布不均匀该怎么办
- 哈希对象
- hashtable
- ziplist
- ziplist 和 hashtable 的编码转换
- 哈希对象常用命令
- 同一份数据,Redis 为什么要存”两次”
- 五种基本类型之集合对象
- intset 编码
- 集合对象常用命令
- 五种基本类型之有序集合对象
- skiplist 编码
- ziplist 编码
- ziplist 和 skiplist 编码转换
- 有序集合对象常用命令
- 要想用活 Redis,Lua 脚本是绕不过去的坎
- 发布与订阅
- 基于频道的实现
- 基于模式的实现
- Lua 脚本
- Lua 脚本的调用
- Lua 脚本中执行 Redis 命令
- Lua 脚本摘要
- Lua 脚本文件
- 脚本异常
- 作为一款内存数据库,为什么断电后 Redis 数据不会丢失
- Redis 持久化机制
- RDB 持久化机制
- AOF 持久化机制
- 内存耗尽后 Redis 会发生什么
- 内存回收
- 过期策略
- 8 种淘汰策略
- LRU 算法
- LFU 算法
- 不能回滚的 Redis 事务还能用吗
- Redis 有事务吗
- Redis 事务实现原理
- Redis 事务 ACID 特性
- watch 命令
- watch 命令的作用
- watch 原理分析
- Redis 为什么不直接用 master-slave 集群
- Redis 集群方案
- 主从复制
- 配置一主两从 master-slave 集群
- 主从复制原理分析
- 主从服务的不足之处
- Sentinel(哨兵)机制为什么从神坛滑落
- 哨兵 Sentinel 机制
- Sentinel 原理分析
- 配置 Sentinel 集群
- Sentinel 机制实战
- Sentinel 机制的不足之处
- Redis Cluster 集群凭什么成为了最终的胜利者
- Redis 分布式集群方案
- 客户端实现分片
- 中间代理服务实现分片
- Redis Cluster 方案
- 手动配置一个 Redis Cluster 集群
- Redis Cluster 集群常用命令
- 客户端如何使用 Redis Cluster 集群
- Redis Cluster 的不足
- 如何从 10 亿数据中快速判断是否存在某一个元素
- 缓存雪崩
- 缓存击穿
- 缓存穿透
- 布隆过滤器(Bloom Filter)
- 布隆过滤器的 2 大特点
- 布隆过滤器的实现(Guava)
- 布隆过滤器的如何删除