1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

Redis重要知识点部分

1、词句勘误和调整;
2、标点符号勘误和调整;
3、统一B树、B+树专有名词。
This commit is contained in:
xuqi 2025-03-02 20:11:20 +08:00
parent 86af2d5626
commit 0682a55265
10 changed files with 230 additions and 230 deletions

View File

@ -120,7 +120,7 @@ B 树也称 B- 树,全称为 **多路平衡查找树**B+ 树是 B 树的一
**B树& B+树两者有何异同呢?** **B树& B+树两者有何异同呢?**
- B 树的所有节点既存放键(key)也存放数据(data),而 B+ 树只有叶子节点存放 key 和 data其他内节点只存放 key。 - B树的所有节点既存放键key也存放数据data而 B+树只有叶子节点存放 key 和 data其他内节点只存放 key。
- B树的叶子节点都是独立的B+树的叶子节点有一条引用链指向与它相邻的叶子节点。 - B树的叶子节点都是独立的B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B树的检索的过程相当于对范围内的每个节点的关键字做二分查找可能还没有到达叶子节点检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。 - B树的检索的过程相当于对范围内的每个节点的关键字做二分查找可能还没有到达叶子节点检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
- 在 B树中进行范围查询时首先找到要查找的下限然后对 B树进行中序遍历直到找到查找的上限而 B+树的范围查询,只需要对链表进行遍历即可。 - 在 B树中进行范围查询时首先找到要查找的下限然后对 B树进行中序遍历直到找到查找的上限而 B+树的范围查询,只需要对链表进行遍历即可。

View File

@ -9,7 +9,7 @@ tag:
在我看来,造成这个问题的原因是我们在学习 Redis 的时候,可能只是简单写了一些 Demo并没有去关注缓存的读写策略或者说压根不知道这回事。 在我看来,造成这个问题的原因是我们在学习 Redis 的时候,可能只是简单写了一些 Demo并没有去关注缓存的读写策略或者说压根不知道这回事。
但是,搞懂 3 种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的! 但是,搞懂 3 种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的!
**下面介绍到的三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式。** **下面介绍到的三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式。**
@ -23,17 +23,17 @@ Cache Aside Pattern 中服务端需要同时维系 db 和 cache并且是以 d
**写** **写**
- 先更新 db - 先更新 db
- 然后直接删除 cache。 - 然后直接删除 cache。
简单画了一张图帮助大家理解写的步骤。 简单画了一张图帮助大家理解写的步骤。
![](https://oss.javaguide.cn/github/javaguide/database/redis/cache-aside-write.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/cache-aside-write.png)
**读** : **读**
- 从 cache 中读取数据,读取到就直接返回 - 从 cache 中读取数据,读取到就直接返回
- cache 中读取不到的话,就从 db 中读取数据返回 - cache 中读取不到的话,就从 db 中读取数据返回
- 再把数据放到 cache 中。 - 再把数据放到 cache 中。
简单画了一张图帮助大家理解读的步骤。 简单画了一张图帮助大家理解读的步骤。
@ -44,7 +44,7 @@ Cache Aside Pattern 中服务端需要同时维系 db 和 cache并且是以 d
比如说面试官很可能会追问:“**在写数据的过程中,可以先删除 cache后更新 db 么?**” 比如说面试官很可能会追问:“**在写数据的过程中,可以先删除 cache后更新 db 么?**”
**答案** 那肯定是不行的!因为这样可能会造成 **数据库db和缓存Cache数据不一致**的问题。 **答案**那肯定是不行的!因为这样可能会造成 **数据库db和缓存Cache数据不一致** 的问题。
举例:请求 1 先写数据 A请求 2 随后读数据 A 的话,就很有可能产生数据不一致性的问题。 举例:请求 1 先写数据 A请求 2 随后读数据 A 的话,就很有可能产生数据不一致性的问题。
@ -54,7 +54,7 @@ Cache Aside Pattern 中服务端需要同时维系 db 和 cache并且是以 d
当你这样回答之后,面试官可能会紧接着就追问:“**在写数据的过程中,先更新 db后删除 cache 就没有问题了么?**” 当你这样回答之后,面试官可能会紧接着就追问:“**在写数据的过程中,先更新 db后删除 cache 就没有问题了么?**”
**答案** 理论上来说还是可能会出现数据不一致性的问题,不过概率非常小,因为缓存的写入速度是比数据库的写入速度快很多。 **答案**理论上来说还是可能会出现数据不一致性的问题,不过概率非常小,因为缓存的写入速度是比数据库的写入速度快很多。
举例:请求 1 先读数据 A请求 2 随后写数据 A并且数据 A 在请求 1 请求之前不在缓存中的话,也有可能产生数据不一致性的问题。 举例:请求 1 先读数据 A请求 2 随后写数据 A并且数据 A 在请求 1 请求之前不在缓存中的话,也有可能产生数据不一致性的问题。
@ -64,7 +64,7 @@ Cache Aside Pattern 中服务端需要同时维系 db 和 cache并且是以 d
现在我们再来分析一下 **Cache Aside Pattern 的缺陷** 现在我们再来分析一下 **Cache Aside Pattern 的缺陷**
**缺陷 1首次请求数据一定不在 cache 的问题** **缺陷 1首次请求数据一定不在 cache 的问题**
解决办法:可以将热点数据可以提前放入 cache 中。 解决办法:可以将热点数据可以提前放入 cache 中。
@ -81,7 +81,7 @@ Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从
这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能。 这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能。
**写Write Through** **写Write Through**
- 先查 cachecache 中不存在,直接更新 db。 - 先查 cachecache 中不存在,直接更新 db。
- cache 中存在,则先更新 cache然后 cache 服务自己更新 db**同步更新 cache 和 db**)。 - cache 中存在,则先更新 cache然后 cache 服务自己更新 db**同步更新 cache 和 db**)。
@ -90,7 +90,7 @@ Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从
![](https://oss.javaguide.cn/github/javaguide/database/redis/write-through.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/write-through.png)
**读(Read Through)** **读(Read Through)**
- 从 cache 中读取数据,读取到就直接返回。 - 从 cache 中读取数据,读取到就直接返回。
- 读取不到的话,先从 db 加载,写入到 cache 后返回响应。 - 读取不到的话,先从 db 加载,写入到 cache 后返回响应。

View File

@ -32,8 +32,8 @@ Redis 中的大部分命令都是 O(1)时间复杂度,但也有少部分 O(n)
Redis 提供了两个命令来生成 RDB 快照文件: Redis 提供了两个命令来生成 RDB 快照文件:
- `save` : 同步保存操作,会阻塞 Redis 主线程; - `save`同步保存操作,会阻塞 Redis 主线程;
- `bgsave` : fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。 - `bgsave`fork 出一个子进程,子进程执行保存操作,不会阻塞 Redis 主线程,默认选项。
默认情况下Redis 默认配置会使用 `bgsave` 命令。如果手动使用 `save` 命令生成 RDB 快照文件的话,就会阻塞主线程。 默认情况下Redis 默认配置会使用 `bgsave` 命令。如果手动使用 `save` 命令生成 RDB 快照文件的话,就会阻塞主线程。
@ -83,7 +83,7 @@ Redis AOF 持久化机制是在执行完命令之后再记录日志,这和关
如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢有一个不是特别精确的参考标准 如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢有一个不是特别精确的参考标准
- string 类型的 value 超过 1MB - string 类型的 value 超过 1MB
- 复合类型(列表、哈希、集合、有序集合等)的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。 - 复合类型(列表、哈希、集合、有序集合等)的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。
大 key 造成的阻塞问题如下: 大 key 造成的阻塞问题如下:
@ -100,7 +100,7 @@ Redis AOF 持久化机制是在执行完命令之后再记录日志,这和关
- 通过分析 RDB 文件来找出 big key这种方案的前提是 Redis 采用的是 RDB 持久化。网上有现成的工具: - 通过分析 RDB 文件来找出 big key这种方案的前提是 Redis 采用的是 RDB 持久化。网上有现成的工具:
- - redis-rdb-toolsPython 语言写的用来分析 Redis 的 RDB 快照文件用的工具 - redis-rdb-toolsPython 语言写的用来分析 Redis 的 RDB 快照文件用的工具
- rdb_bigkeysGo 语言写的用来分析 Redis 的 RDB 快照文件用的工具,性能更好。 - rdb_bigkeysGo 语言写的用来分析 Redis 的 RDB 快照文件用的工具,性能更好。
### 删除大 key ### 删除大 key
@ -156,9 +156,9 @@ Swap: 0kB
预防内存交换的方法: 预防内存交换的方法:
- 保证机器充足的可用内存 - 保证机器充足的可用内存
- 确保所有 Redis 实例设置最大可用内存(maxmemory),防止极端情况 Redis 内存不可控的增长 - 确保所有 Redis 实例设置最大可用内存maxmemory,防止极端情况 Redis 内存不可控的增长
- 降低系统使用 swap 优先级,如`echo 10 > /proc/sys/vm/swappiness` - 降低系统使用 swap 优先级,如 `echo 10 > /proc/sys/vm/swappiness`
## CPU 竞争 ## CPU 竞争
@ -168,7 +168,7 @@ Redis 是典型的 CPU 密集型应用,不建议和其他多核 CPU 密集型
## 网络问题 ## 网络问题
连接拒绝、网络延迟网卡软中断等网络问题也可能会导致 Redis 阻塞。 连接拒绝、网络延迟网卡软中断等网络问题也可能会导致 Redis 阻塞。
## 参考 ## 参考

View File

@ -14,7 +14,7 @@ head:
Redis 共有 5 种基本数据类型String字符串、List列表、Set集合、Hash散列、Zset有序集合 Redis 共有 5 种基本数据类型String字符串、List列表、Set集合、Hash散列、Zset有序集合
这 5 种数据类型是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:简单动态字符串SDS、LinkedList双向链表、Dict哈希表/字典、SkipList跳跃表、Intset整数集合、ZipList压缩列表、QuickList快速列表 这 5 种数据类型是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:SDS简单动态字符串、LinkedList双向链表、Dict哈希表/字典、SkipList跳跃表、Intset整数集合、ZipList压缩列表、QuickList快速列表
Redis 5 种基本数据类型对应的底层数据结构实现如下表所示: Redis 5 种基本数据类型对应的底层数据结构实现如下表所示:
@ -43,7 +43,7 @@ String 是一种二进制安全的数据类型,可以用来存储任何类型
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124403897.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124403897.png)
虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**Simple Dynamic String**SDS**)。相比于 C 的原生字符串Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)C 字符串为 O(N),除此之外Redis 的 SDS API 是安全的,不会造成缓冲区溢出。 虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**Simple Dynamic String**SDS**)。相比于 C 的原生字符串Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)C 字符串为 O(N)除此之外Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
### 常用命令 ### 常用命令
@ -90,7 +90,7 @@ OK
2) "value2" 2) "value2"
``` ```
**计数器(字符串的内容为整数的时候可以使用)** **计数器(字符串的内容为整数的时候可以使用)**
```bash ```bash
> SET number 1 > SET number 1
@ -120,7 +120,7 @@ OK
**需要存储常规数据的场景** **需要存储常规数据的场景**
- 举例:缓存 Session、Token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存) - 举例:缓存 Session、Token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)
- 相关命令:`SET``GET` - 相关命令:`SET``GET`
**需要计数的场景** **需要计数的场景**
@ -136,21 +136,21 @@ OK
### 介绍 ### 介绍
Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据结构 :数组、链表、栈、队列](https://javaguide.cn/cs-basics/data-structure/linear-data-structure.html) 这篇文章中详细介绍了链表这种数据结构,这里就不多做介绍了。 Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据结构 :数组、链表、栈、队列](https://javaguide.cn/cs-basics/data-structure/linear-data-structure.html) 这篇文章中详细介绍了链表这种数据结构,这里就不多做介绍了。
许多高级编程语言都内置了链表的实现比如 Java 中的 `LinkedList`,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 List 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 许多高级编程语言都内置了链表的实现比如 Java 中的 `LinkedList`,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 List 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124413287.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124413287.png)
### 常用命令 ### 常用命令
| 命令 | 介绍 | | 命令 | 介绍 |
| --------------------------- | ------------------------------------------ | | --------------------------- |-----------------------------|
| RPUSH key value1 value2 ... | 在指定列表的尾部(右边)添加一个或多个元素 | | RPUSH key value1 value2 ... | 在指定列表的尾部(右边)添加一个或多个元素 |
| LPUSH key value1 value2 ... | 在指定列表的头部(左边)添加一个或多个元素 | | LPUSH key value1 value2 ... | 在指定列表的头部(左边)添加一个或多个元素 |
| LSET key index value | 将指定列表索引 index 位置的值设置为 value | | LSET key index value | 将指定列表索引 index 位置的值设置为 value |
| LPOP key | 移除并获取指定列表的第一个元素(最左边) | | LPOP key | 移除并获取指定列表的第一个元素(最左边) |
| RPOP key | 移除并获取指定列表的最后一个元素(最右边) | | RPOP key | 移除并获取指定列表的最后一个元素(最右边) |
| LLEN key | 获取列表元素数量 | | LLEN key | 获取列表元素数量 |
| LRANGE key start end | 获取列表 start 和 end 之间 的元素 | | LRANGE key start end | 获取列表 start 和 end 之间 的元素 |
@ -182,7 +182,7 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据
"value3" "value3"
``` ```
我专门画了一个图方便大家理解 `RPUSH` , `LPOP` , `lpush` , `RPOP` 命令: 我专门画了一个图方便大家理解 `RPUSH``LPOP``lpush``RPOP` 命令:
![](https://oss.javaguide.cn/github/javaguide/database/redis/redis-list.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/redis-list.png)
@ -220,7 +220,7 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据
`List` 可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。 `List` 可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。
相对来说Redis 5.0 新增加的一个数据结构 `Stream` 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。 相对来说Redis 5.0 新增加的一个数据结构 `Stream` 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。
## Hash哈希 ## Hash哈希
@ -228,17 +228,17 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据
Redis 中的 Hash 是一个 String 类型的 field-value键值对 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。 Redis 中的 Hash 是一个 String 类型的 field-value键值对 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。
Hash 类似于 JDK1.8 前的 `HashMap`,内部实现也差不多(数组 + 链表)。不过Redis 的 Hash 做了更多优化。 Hash 类似于 JDK1.8 前的 `HashMap`,内部实现也差不多(数组+链表)。不过Redis 的 Hash 做了更多优化。
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124421703.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719124421703.png)
### 常用命令 ### 常用命令
| 命令 | 介绍 | | 命令 | 介绍 |
| ----------------------------------------- | -------------------------------------------------------- | | ----------------------------------------- |-------------------------------------|
| HSET key field value | 设置指定哈希表中指定字段的值 | | HSET key field value | 设置指定哈希表中指定字段的值 |
| HSETNX key field value | 只有指定字段不存在时设置指定字段的值 | | HSETNX key field value | 只有指定字段不存在时设置指定字段的值 |
| HMSET key field1 value1 field2 value2 ... | 同时将一个或多个 field-value (域-值)对设置到指定哈希表中 | | HMSET key field1 value1 field2 value2 ... | 同时将一个或多个 field-value(域-值)对设置到指定哈希表中 |
| HGET key field | 获取指定哈希表中指定字段的值 | | HGET key field | 获取指定哈希表中指定字段的值 |
| HMGET key field1 field2 ... | 获取指定哈希表中一个或者多个指定字段的值 | | HMGET key field1 field2 ... | 获取指定哈希表中一个或者多个指定字段的值 |
| HGETALL key | 获取指定哈希表中所有的键值对 | | HGETALL key | 获取指定哈希表中所有的键值对 |
@ -328,7 +328,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺
(integer) 2 (integer) 2
``` ```
- `mySet` : `value1``value2` - `mySet``value1``value2`
- `mySet2``value2``value3` - `mySet2``value2``value3`
**求交集** **求交集**
@ -367,7 +367,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺
**需要获取多个数据源交集、并集和差集的场景** **需要获取多个数据源交集、并集和差集的场景**
- 举例:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。 - 举例:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集)等场景。
- 相关命令:`SINTER`(交集)、`SINTERSTORE`(交集)、`SUNION`(并集)、`SUNIONSTORE`(并集)、`SDIFF`(差集)、`SDIFFSTORE`(差集)。 - 相关命令:`SINTER`(交集)、`SINTERSTORE`(交集)、`SUNION`(并集)、`SUNIONSTORE`(并集)、`SDIFF`(差集)、`SDIFFSTORE`(差集)。
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719074543513.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719074543513.png)
@ -388,7 +388,7 @@ Sorted Set 类似于 Set但和 Set 相比Sorted Set 增加了一个权重
### 常用命令 ### 常用命令
| 命令 | 介绍 | | 命令 | 介绍 |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | --------------------------------------------- |-------------------------------------------------------------------------|
| ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一个或多个元素 | | ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一个或多个元素 |
| ZCARD KEY | 获取指定有序集合的元素数量 | | ZCARD KEY | 获取指定有序集合的元素数量 |
| ZSCORE key member | 获取指定有序集合中指定元素的 score 值 | | ZSCORE key member | 获取指定有序集合中指定元素的 score 值 |
@ -397,7 +397,7 @@ Sorted Set 类似于 Set但和 Set 相比Sorted Set 增加了一个权重
| ZDIFFSTORE destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 类似 | | ZDIFFSTORE destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 类似 |
| ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素score 从低到高) | | ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素score 从低到高) |
| ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素score 从高到底) | | ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素score 从高到底) |
| ZREVRANK key member | 获取指定有序集合中指定元素的排名(score 从大到小排序) | | ZREVRANK key member | 获取指定有序集合中指定元素的排名score 从大到小排序) |
更多 Redis Sorted Set 命令以及详细使用指南,请查看 Redis 官网对应的介绍:<https://redis.io/commands/?group=sorted-set> 更多 Redis Sorted Set 命令以及详细使用指南,请查看 Redis 官网对应的介绍:<https://redis.io/commands/?group=sorted-set>
@ -421,8 +421,8 @@ Sorted Set 类似于 Set但和 Set 相比Sorted Set 增加了一个权重
``` ```
- `myZset` : `value1`(2.0)、`value2`(1.0) - `myZset``value1`2.0)、`value2`1.0
- `myZset2``value2` 4.0)、`value3`(3.0) - `myZset2``value2`4.0)、`value3`3.0
**获取指定元素的排名** **获取指定元素的排名**
@ -469,8 +469,8 @@ value1
**需要随机获取数据源中的元素根据某个权重进行排序的场景** **需要随机获取数据源中的元素根据某个权重进行排序的场景**
- 举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。 - 举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
- 相关命令:`ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名) - 相关命令:`ZRANGE`(从小到大排序)、`ZREVRANGE`(从大到小排序)、`ZREVRANK`(指定元素排名)
![](https://oss.javaguide.cn/github/javaguide/database/redis/2021060714195385.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/2021060714195385.png)
@ -478,15 +478,15 @@ value1
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719071115140.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220719071115140.png)
**需要存储的数据有优先级或者重要程度的场景** 比如优先级任务队列。 **需要存储的数据有优先级或者重要程度的场景**
- 举例:优先级任务队列。 - 举例:优先级任务队列。
- 相关命令:`ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名) - 相关命令:`ZRANGE`(从小到大排序)、`ZREVRANGE`(从大到小排序)、`ZREVRANK`(指定元素排名)
## 总结 ## 总结
| 数据类型 | 说明 | | 数据类型 | 说明 |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| String | 一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。 | | String | 一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。 |
| List | Redis 的 List 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 | | List | Redis 的 List 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 |
| Hash | 一个 String 类型的 field-value键值对 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。 | | Hash | 一个 String 类型的 field-value键值对 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。 |
@ -498,6 +498,6 @@ value1
- Redis Data Structures<https://redis.com/redis-enterprise/data-structures/> - Redis Data Structures<https://redis.com/redis-enterprise/data-structures/>
- Redis Commands<https://redis.io/commands/> - Redis Commands<https://redis.io/commands/>
- Redis Data types tutorial<https://redis.io/docs/manual/data-types/data-types-tutorial/> - Redis Data types tutorial<https://redis.io/docs/manual/data-types/data-types-tutorial/>
- Redis 存储对象信息是用 Hash 还是 String : <https://segmentfault.com/a/1190000040032006> - Redis 存储对象信息是用 Hash 还是 String<https://segmentfault.com/a/1190000040032006>
<!-- @include: @article-footer.snippet.md --> <!-- @include: @article-footer.snippet.md -->

View File

@ -24,7 +24,7 @@ head:
> >
> Bitmap 不是 Redis 中的实际数据类型,而是在 String 类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全的块,且最大长度为 512 MB它们适合用于设置最多 2^32 个不同的位。 > Bitmap 不是 Redis 中的实际数据类型,而是在 String 类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全的块,且最大长度为 512 MB它们适合用于设置最多 2^32 个不同的位。
Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。 Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。
你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量 你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量
@ -33,11 +33,11 @@ Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需
### 常用命令 ### 常用命令
| 命令 | 介绍 | | 命令 | 介绍 |
| ------------------------------------- | ---------------------------------------------------------------- | | ------------------------------------- |---------------------------------------------|
| SETBIT key offset value | 设置指定 offset 位置的值 | | SETBIT key offset value | 设置指定 offset 位置的值 |
| GETBIT key offset | 获取指定 offset 位置的值 | | GETBIT key offset | 获取指定 offset 位置的值 |
| BITCOUNT key start end | 获取 start 和 end 之间值为 1 的元素个数 | | BITCOUNT key start end | 获取 start 和 end 之间值为 1 的元素个数 |
| BITOP operation destkey key1 key2 ... | 对一个或多个 Bitmap 进行运算,可用运算符有 AND, OR, XOR 以及 NOT | | BITOP operation destkey key1 key2 ... | 对一个或多个 Bitmap 进行运算,可用运算符有 AND、OR、XOR 以及 NOT |
**Bitmap 基本操作演示** **Bitmap 基本操作演示**
@ -69,7 +69,7 @@ Bitmap 存储的是连续的二进制数字0 和 1通过 Bitmap, 只需
### 介绍 ### 介绍
HyperLogLog 是一种有名的基数计数概率算法 ,基于 LogLog Counting(LLC)优化改进得来,并不是 Redis 特有的Redis 只是实现了这个算法并提供了一些开箱即用的 API。 HyperLogLog 是一种有名的基数计数概率算法,基于 LogLog CountingLLC优化改进得来,并不是 Redis 特有的Redis 只是实现了这个算法并提供了一些开箱即用的 API。
Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近 `2^64` 个不同元素。这是真的厉害这就是数学的魅力么并且Redis 对 HyperLogLog 的存储结构做了优化,采用两种方式计数: Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近 `2^64` 个不同元素。这是真的厉害这就是数学的魅力么并且Redis 对 HyperLogLog 的存储结构做了优化,采用两种方式计数:
@ -141,11 +141,11 @@ Geospatial index地理空间索引简称 GEO 主要用于存储地理
### 常用命令 ### 常用命令
| 命令 | 介绍 | | 命令 | 介绍 |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | | ------------------------------------------------ |-----------------------------------------------------------------|
| GEOADD key longitude1 latitude1 member1 ... | 添加一个或多个元素对应的经纬度信息到 GEO 中 | | GEOADD key longitude1 latitude1 member1 ... | 添加一个或多个元素对应的经纬度信息到 GEO 中 |
| GEOPOS key member1 member2 ... | 返回给定元素的经纬度信息 | | GEOPOS key member1 member2 ... | 返回给定元素的经纬度信息 |
| GEODIST key member1 member2 M/KM/FT/MI | 返回两个给定元素之间的距离 | | GEODIST key member1 member2 M/KM/FT/MI | 返回两个给定元素之间的距离 |
| GEORADIUS key longitude latitude radius distance | 获取指定位置附近 distance 范围内的其他元素,支持 ASC(由近到远)、DESC由远到近、Count(数量) 等参数 | | GEORADIUS key longitude latitude radius distance | 获取指定位置附近 distance 范围内的其他元素,支持 ASC由近到远、DESC由远到近、Count数量等参数 |
| GEORADIUSBYMEMBER key member radius distance | 类似于 GEORADIUS 命令,只是参照的中心点是 GEO 中的元素 | | GEORADIUSBYMEMBER key member radius distance | 类似于 GEORADIUS 命令,只是参照的中心点是 GEO 中的元素 |
**基本操作** **基本操作**
@ -162,7 +162,7 @@ Geospatial index地理空间索引简称 GEO 主要用于存储地理
通过 Redis 可视化工具查看 `personLocation`,果不其然,底层就是 Sorted Set。 通过 Redis 可视化工具查看 `personLocation`,果不其然,底层就是 Sorted Set。
GEO 中存储的地理位置信息的经纬度数据通过 GeoHash 算法转换成了一个整数,这个整数作为 Sorted Set 的 score(权重参数)使用。 GEO 中存储的地理位置信息的经纬度数据通过 GeoHash 算法转换成了一个整数,这个整数作为 Sorted Set 的 score(权重参数)使用。
![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220721201545147.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/image-20220721201545147.png)
@ -212,15 +212,15 @@ user2
## 总结 ## 总结
| 数据类型 | 说明 | | 数据类型 | 说明 |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ---------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Bitmap | 你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量。通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。 | | Bitmap | 你可以将 Bitmap 看作是一个存储二进制数字0 和 1的数组数组中每个元素的下标叫做 offset偏移量。通过 Bitmap只需要一个 bit 位来表示某个元素对应的值或者状态key 就是对应元素本身。我们知道 8 个 bit 可以组成一个 byte所以 Bitmap 本身会极大的节省储存空间。 |
| HyperLogLog | Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近 `2^64` 个不同元素。不过HyperLogLog 的计数结果并不是一个精确值,存在一定的误差(标准误差为 `0.81%`)。 | | HyperLogLog | Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近 `2^64` 个不同元素。不过HyperLogLog 的计数结果并不是一个精确值,存在一定的误差(标准误差为 `0.81%`)。 |
| Geospatial index | Geospatial index地理空间索引简称 GEO主要用于存储地理位置信息基于 Sorted Set 实现。 | | Geospatial index | Geospatial index地理空间索引简称 GEO主要用于存储地理位置信息基于 Sorted Set 实现。 |
## 参考 ## 参考
- Redis Data Structures<https://redis.com/redis-enterprise/data-structures/> - Redis Data Structures<https://redis.com/redis-enterprise/data-structures/>
- 《Redis 深度历险核心原理与应用实践》1.6 四两拨千斤——HyperLogLog - 《Redis 深度历险核心原理与应用实践》1.6 四两拨千斤——HyperLogLog
- 布隆过滤器,位图,HyperLogLog<https://hogwartsrico.github.io/2020/06/08/BloomFilter-HyperLogLog-BitMap/index.html> - 布隆过滤器、位图、HyperLogLog<https://hogwartsrico.github.io/2020/06/08/BloomFilter-HyperLogLog-BitMap/index.html>
<!-- @include: @article-footer.snippet.md --> <!-- @include: @article-footer.snippet.md -->

View File

@ -7,8 +7,8 @@ tag:
基于 Redis 实现延时任务的功能无非就下面两种方案: 基于 Redis 实现延时任务的功能无非就下面两种方案:
1. Redis 过期事件监听 1. Redis 过期事件监听
2. Redisson 内置的延时队列 2. Redisson 内置的延时队列
面试的时候,你可以先说自己考虑了这两种方案,但最后发现 Redis 过期事件监听这种方案存在很多问题,因此你最终选择了 Redisson 内置的 DelayedQueue 这种方案。 面试的时候,你可以先说自己考虑了这两种方案,但最后发现 Redis 过期事件监听这种方案存在很多问题,因此你最终选择了 Redisson 内置的 DelayedQueue 这种方案。
@ -18,7 +18,7 @@ tag:
### Redis 过期事件监听实现延时任务功能的原理? ### Redis 过期事件监听实现延时任务功能的原理?
Redis 2.0 引入了发布订阅 (pub/sub) 功能。在 pub/sub 中,引入了一个叫做 **channel频道** 的概念,有点类似于消息队列中的 **topic主题** Redis 2.0 引入了发布订阅pub/sub功能。在 pub/sub 中,引入了一个叫做 **channel频道** 的概念,有点类似于消息队列中的 **topic主题**
pub/sub 涉及发布者publisher和订阅者subscriber也叫消费者两个角色 pub/sub 涉及发布者publisher和订阅者subscriber也叫消费者两个角色

View File

@ -25,7 +25,7 @@ Redis 内存碎片产生比较常见的 2 个原因:
> To store user keys, Redis allocates at most as much memory as the `maxmemory` setting enables (however there are small extra allocations possible). > To store user keys, Redis allocates at most as much memory as the `maxmemory` setting enables (however there are small extra allocations possible).
Redis 使用 `zmalloc` 方法(Redis 自己实现的内存分配方法)进行内存分配的时候,除了要分配 `size` 大小的内存之外,还会多分配 `PREFIX_SIZE` 大小的内存。 Redis 使用 `zmalloc` 方法Redis 自己实现的内存分配方法)进行内存分配的时候,除了要分配 `size` 大小的内存之外,还会多分配 `PREFIX_SIZE` 大小的内存。
`zmalloc` 方法源码如下(源码地址:<https://github.com/antirez/redis-tools/blob/master/zmalloc.c> `zmalloc` 方法源码如下(源码地址:<https://github.com/antirez/redis-tools/blob/master/zmalloc.c>
@ -55,7 +55,7 @@ void *zmalloc(size_t size) {
当 Redis 中的某个数据删除时Redis 通常不会轻易释放内存给操作系统。 当 Redis 中的某个数据删除时Redis 通常不会轻易释放内存给操作系统。
这个在 Redis 官方文档中也有对应的原话: 这个在 Redis 官方文档中也有对应的原话
![](https://oss.javaguide.cn/github/javaguide/redis-docs-memory-optimization.png) ![](https://oss.javaguide.cn/github/javaguide/redis-docs-memory-optimization.png)
@ -67,9 +67,9 @@ void *zmalloc(size_t size) {
![](https://oss.javaguide.cn/github/javaguide/redis-info-memory.png) ![](https://oss.javaguide.cn/github/javaguide/redis-info-memory.png)
Redis 内存碎片率的计算公式:`mem_fragmentation_ratio` (内存碎片率)= `used_memory_rss` (操作系统实际分配给 Redis 的物理内存空间大小)/ `used_memory`(Redis 内存分配器为了存储数据实际申请使用的内存空间大小) Redis 内存碎片率的计算公式:`mem_fragmentation_ratio`(内存碎片率)=`used_memory_rss`(操作系统实际分配给 Redis 的物理内存空间大小)/`used_memory`Redis 内存分配器为了存储数据实际申请使用的内存空间大小)。
也就是说,`mem_fragmentation_ratio` (内存碎片率)的值越大代表内存碎片率越严重。 也就是说,`mem_fragmentation_ratio`(内存碎片率)的值越大代表内存碎片率越严重。
一定不要误认为 `used_memory_rss` 减去 `used_memory` 值就是内存碎片的大小!!!这不仅包括内存碎片,还包括其他进程开销,以及共享库、堆栈等的开销。 一定不要误认为 `used_memory_rss` 减去 `used_memory` 值就是内存碎片的大小!!!这不仅包括内存碎片,还包括其他进程开销,以及共享库、堆栈等的开销。

View File

@ -12,13 +12,13 @@ head:
content: Redis 不同于 Memcached 的很重要一点就是Redis 支持持久化,而且支持 3 种持久化方式:快照snapshottingRDB、只追加文件append-only file, AOF、RDB 和 AOF 的混合持久化(Redis 4.0 新增)。 content: Redis 不同于 Memcached 的很重要一点就是Redis 支持持久化,而且支持 3 种持久化方式:快照snapshottingRDB、只追加文件append-only file, AOF、RDB 和 AOF 的混合持久化(Redis 4.0 新增)。
--- ---
使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。 使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。
Redis 不同于 Memcached 的很重要一点就是Redis 支持持久化,而且支持 3 种持久化方式: Redis 不同于 Memcached 的很重要一点就是Redis 支持持久化,而且支持 3 种持久化方式
- 快照snapshottingRDB - 快照snapshottingRDB
- 只追加文件append-only file, AOF - 只追加文件append-only file, AOF
- RDB 和 AOF 的混合持久化(Redis 4.0 新增) - RDB 和 AOF 的混合持久化(Redis 4.0 新增)
官方文档地址:<https://redis.io/topics/persistence> 官方文档地址:<https://redis.io/topics/persistence>
@ -45,7 +45,7 @@ save 60 10000 #在60秒(1分钟)之后如果至少有10000个key发生
Redis 提供了两个命令来生成 RDB 快照文件: Redis 提供了两个命令来生成 RDB 快照文件:
- `save` : 同步保存操作,会阻塞 Redis 主线程; - `save` : 同步保存操作,会阻塞 Redis 主线程;
- `bgsave` : fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。 - `bgsave` : fork 出一个子进程,子进程执行保存操作,不会阻塞 Redis 主线程,默认选项。
> 这里说 Redis 主线程而不是主进程的主要是因为 Redis 启动之后主要是通过单线程的方式完成主要的工作。如果你想将其描述为 Redis 主进程,也没毛病。 > 这里说 Redis 主线程而不是主进程的主要是因为 Redis 启动之后主要是通过单线程的方式完成主要的工作。如果你想将其描述为 Redis 主进程,也没毛病。
@ -59,7 +59,7 @@ Redis 提供了两个命令来生成 RDB 快照文件:
appendonly yes appendonly yes
``` ```
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令Redis 就会将该命令写入到 AOF 缓冲区 `server.aof_buf` 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( `fsync`策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中 开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令Redis 就会将该命令写入到 AOF 缓冲区 `server.aof_buf` 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式(`fsync`策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中。
只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。 只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。
@ -77,7 +77,7 @@ AOF 持久化功能的实现可以简单分为 5 步:
> Linux 系统直接提供了一些函数用于对文件和设备进行访问和控制,这些函数被称为 **系统调用syscall** > Linux 系统直接提供了一些函数用于对文件和设备进行访问和控制,这些函数被称为 **系统调用syscall**
这里对上面提到的一些 Linux 系统调用再一遍解释: 这里对上面提到的一些 Linux 系统调用再一遍解释:
- `write`写入系统内核缓冲区之后直接返回仅仅是写到缓冲区不会立即同步到硬盘。虽然提高了效率但也带来了数据丢失的风险。同步硬盘操作通常依赖于系统调度机制Linux 内核通常为 30s 同步一次,具体值取决于写出的数据量和 I/O 缓冲区的状态。 - `write`写入系统内核缓冲区之后直接返回仅仅是写到缓冲区不会立即同步到硬盘。虽然提高了效率但也带来了数据丢失的风险。同步硬盘操作通常依赖于系统调度机制Linux 内核通常为 30s 同步一次,具体值取决于写出的数据量和 I/O 缓冲区的状态。
- `fsync``fsync` 用于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。 - `fsync``fsync` 用于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。
@ -145,15 +145,15 @@ Redis 7.0 版本之前如果在重写期间有写入命令AOF 可能会使
Redis 7.0 版本之后AOF 重写机制得到了优化改进。下面这段内容摘自阿里开发者的 [从 Redis7.0 发布看 Redis 的过去与未来](https://mp.weixin.qq.com/s/RnoPPL7jiFSKkx3G4p57Pg) 这篇文章。 Redis 7.0 版本之后AOF 重写机制得到了优化改进。下面这段内容摘自阿里开发者的 [从 Redis7.0 发布看 Redis 的过去与未来](https://mp.weixin.qq.com/s/RnoPPL7jiFSKkx3G4p57Pg) 这篇文章。
> AOF 重写期间的增量数据如何处理一直是个问题,在过去写期间的增量数据需要在内存中保留,写结束后再把这部分增量数据写入新的 AOF 文件中以保证数据完整性。可以看出来 AOF 写会额外消耗内存和磁盘 IO这也是 Redis AOF 写的痛点,虽然之前也进行过多次改进但是资源消耗的本质问题一直没有解决。 > AOF 重写期间的增量数据如何处理一直是个问题,在过去写期间的增量数据需要在内存中保留,写结束后再把这部分增量数据写入新的 AOF 文件中以保证数据完整性。可以看出来 AOF 写会额外消耗内存和磁盘 IO这也是 Redis AOF 写的痛点,虽然之前也进行过多次改进但是资源消耗的本质问题一直没有解决。
> >
> 阿里云的 Redis 企业版在最初也遇到了这个问题,在内部经过多次迭代开发,实现了 Multi-part AOF 机制来解决,同时也贡献给了社区并随此次 7.0 发布。具体方法是采用 base全量数据+inc增量数据独立文件存储的方式彻底解决内存和 IO 资源的浪费,同时也支持对历史 AOF 文件的保存管理,结合 AOF 文件中的时间信息还可以实现 PITR 按时间点恢复(阿里云企业版 Tair 已支持),这进一步增强了 Redis 的数据可靠性,满足用户数据回档等需求。 > 阿里云的 Redis 企业版在最初也遇到了这个问题,不过在经过内部多次迭代开发阿里云实现了 Multi-part AOF 机制来解决该问题,同时也将其贡献给了社区并随此次 7.0 发布。具体方法是采用 base全量数据+inc增量数据独立文件存储的方式彻底解决内存和 IO 资源的浪费,同时也支持对历史 AOF 文件的保存管理,结合 AOF 文件中的时间信息还可以实现 PITR 按时间点恢复(阿里云企业版 Tair 已支持),这进一步增强了 Redis 的数据可靠性,满足用户数据回档等需求。
**相关 issue**[Redis AOF 重写描述不准确 #1439](https://github.com/Snailclimb/JavaGuide/issues/1439)。 **相关 issue**[Redis AOF 重写描述不准确 #1439](https://github.com/Snailclimb/JavaGuide/issues/1439)。
### AOF 校验机制了解吗? ### AOF 校验机制了解吗?
AOF 校验机制是 Redis 在启动时对 AOF 文件进行检查,以判断文件是否完整,是否有损坏或者丢失的数据。这个机制的原理其实非常简单,就是通过使用一种叫做 **校验和checksum** 的数字来验证 AOF 文件。这个校验和是通过对整个 AOF 文件内容进行 CRC64 算法计算得出的数字。如果文件内容发生了变化那么校验和也会随之改变。因此Redis 在启动时会比较计算出的校验和与文件末尾保存的校验和(计算的时候会把最后一行保存校验和的内容给忽略),从而判断 AOF 文件是否完整。如果发现文件有问题Redis 就会拒绝启动并提供相应的错误信息。AOF 校验机制十分简单有效,可以提高 Redis 数据的可靠性。 AOF 校验机制是 Redis 在启动时对 AOF 文件进行检查,以判断文件是否完整,是否有损坏或者丢失的数据。这个机制的原理其实非常简单,就是通过使用一种叫做 **校验和checksum** 的数字来验证 AOF 文件。这个校验和是通过对整个 AOF 文件内容进行 CRC64 算法计算得出的数字。如果文件内容发生了变化那么校验和也会随之改变。因此Redis 在启动时会比较计算出的校验和与文件末尾保存的校验和(计算的时候会把最后一行保存校验和的内容给忽略),从而判断 AOF 文件是否完整。如果发现文件有问题Redis 就会拒绝启动并提供相应的错误信息。AOF 校验机制十分简单有效,可以提高 Redis 数据的可靠性。
类似地RDB 文件也有类似的校验机制来保证 RDB 文件的正确性,这里就不重复进行介绍了。 类似地RDB 文件也有类似的校验机制来保证 RDB 文件的正确性,这里就不重复进行介绍了。
@ -163,7 +163,7 @@ AOF 校验机制是 Redis 在启动时对 AOF 文件进行检查,以判断文
如果把混合持久化打开AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。 如果把混合持久化打开AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
官方文档地址:<https://redis.io/topics/persistence> 官方文档地址:<https://redis.io/topics/persistence>
![](https://oss.javaguide.cn/github/javaguide/database/redis/redis4.0-persitence.png) ![](https://oss.javaguide.cn/github/javaguide/database/redis/redis4.0-persitence.png)
@ -173,7 +173,7 @@ AOF 校验机制是 Redis 在启动时对 AOF 文件进行检查,以判断文
**RDB 比 AOF 优秀的地方** **RDB 比 AOF 优秀的地方**
- RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集文件很小适合做数据的备份灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前如果在重写期间有写入命令AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。 - RDB 文件存储的内容是经过压缩的二进制数据,保存着某个时间点的数据集,文件很小,适合做数据的备份,用于灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样但体积更小。不过Redis 7.0 版本之前如果在重写期间有写入命令AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
- 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比恢复大数据集的时候RDB 速度更快。 - 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比恢复大数据集的时候RDB 速度更快。
**AOF 比 RDB 优秀的地方** **AOF 比 RDB 优秀的地方**

View File

@ -17,7 +17,7 @@ tag:
## 跳表在 Redis 中的运用 ## 跳表在 Redis 中的运用
这里我们需要先了解一下 Redis 用到跳表的数据结构有序集合的使用Redis 有个比较常用的数据结构叫**有序集合(sorted set简称 zset)**,正如其名它是一个可以保证有序且元素唯一的集合,所以它经常用于排行榜等需要进行统计排列的场景。 这里我们需要先了解一下 Redis 用到跳表的数据结构有序集合的使用Redis 有个比较常用的数据结构叫**有序集合sorted set简称 zset**,正如其名它是一个可以保证有序且元素唯一的集合,所以它经常用于排行榜等需要进行统计排列的场景。
这里我们通过命令行的形式演示一下排行榜的实现,可以看到笔者分别输入 3 名用户:**xiaoming**、**xiaohong**、**xiaowang**,它们的 **score** 分别是 60、80、60最终按照成绩升级降序排列。 这里我们通过命令行的形式演示一下排行榜的实现,可以看到笔者分别输入 3 名用户:**xiaoming**、**xiaohong**、**xiaowang**,它们的 **score** 分别是 60、80、60最终按照成绩升级降序排列。
@ -54,7 +54,7 @@ zset-max-ziplist-value 64
zset-max-ziplist-entries 128 zset-max-ziplist-entries 128
``` ```
一旦有序集合中的某个元素超出这两个其中的一个阈值它就会转为 **skiplist**(实际是 dict+skiplist还会借用字典来提高获取指定元素的效率 一旦有序集合中的某个元素超出这两个其中的一个阈值它就会转为 **skiplist**(实际是 dict+skiplist还会借用字典来提高获取指定元素的效率
我们不妨在添加一个大于 64 字节的元素,可以看到有序集合的底层存储转为 skiplist。 我们不妨在添加一个大于 64 字节的元素,可以看到有序集合的底层存储转为 skiplist。
@ -164,7 +164,7 @@ r=n/2^k
### 模板定义 ### 模板定义
有了整体的思路之后,我们可以开始实现一个跳表了,首先定义一下跳表中的节点**Node**,从上文的演示中可以看出每一个**Node**它都包含以下几个元素: 有了整体的思路之后,我们可以开始实现一个跳表了,首先定义一下跳表中的节点 **Node**,从上文的演示中可以看出每一个 **Node** 都包含以下几个元素:
1. 存储的 **value** 值。 1. 存储的 **value** 值。
2. 后继节点的地址。 2. 后继节点的地址。
@ -176,7 +176,7 @@ r=n/2^k
![](https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005347.png) ![](https://oss.javaguide.cn/javaguide/database/redis/skiplist/202401222005347.png)
于是我们的就有了这样一个代码定义,可以看出笔者对于数组的长度设置为固定的 16**(上文的推算最大高度建议是 16)**,默认**data**为-1节点最大高度**maxLevel**初始化为 1注意这个**maxLevel**的值代表原始链表加上索引的总高度。 于是我们的就有了这样一个代码定义,可以看出笔者对于数组的长度设置为固定的 16 **(上文的推算最大高度建议是 16**,默认 **data** 为 -1节点最大高度 **maxLevel** 初始化为 1注意这个 **maxLevel** 的值代表原始链表加上索引的总高度。
```java ```java
/** /**
@ -194,7 +194,7 @@ class Node {
### 元素添加 ### 元素添加
定义好节点之后,我们先实现以下元素的添加,添加元素时首先自然是设置**data**这一步我们直接根据将传入的**value**设置到**data**上即可。 定义好节点之后,我们先实现以下元素的添加,添加元素时首先自然是设置 **data**这一步我们直接根据将传入的 **value** 设置到 **data** 上即可。
然后就是高度 **maxLevel** 的设置 ,我们在上文也已经给出了思路,默认高度为 1即只有一个原始链表节点通过随机算法每次大于 0.5 索引高度加 1由此我们得出高度计算的算法 `randomLevel()` 然后就是高度 **maxLevel** 的设置 ,我们在上文也已经给出了思路,默认高度为 1即只有一个原始链表节点通过随机算法每次大于 0.5 索引高度加 1由此我们得出高度计算的算法 `randomLevel()`
@ -205,7 +205,7 @@ class Node {
* 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且: * 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且:
* 50%的概率返回 1 * 50%的概率返回 1
* 25%的概率返回 2 * 25%的概率返回 2
* 12.5%的概率返回 3 ... * 12.5%的概率返回 3......
* @return * @return
*/ */
private int randomLevel() { private int randomLevel() {
@ -361,7 +361,7 @@ public void delete(int value) {
### 完整代码以及测试 ### 完整代码以及测试
完整代码如下,读者可自行参阅: 完整代码如下,读者可自行参阅
```java ```java
public class SkipList { public class SkipList {
@ -602,7 +602,7 @@ Node{data=23, maxLevel=1}
**Redis 跳表的特点** **Redis 跳表的特点**
1. 采用**双向链表**,不同于上面的示例,存在一个回退指针。主要用于简化操作,例如删除某个元素时,还需要找到该元素的前驱节点,使用回退指针会非常方便。 1. 采用**双向链表**,不同于上面的示例,存在一个回退指针。主要用于简化操作,例如删除某个元素时,还需要找到该元素的前驱节点,使用回退指针会非常方便。
2. `score` 值可以重复,如果 `score` 值一样,则按照 ele节点存储的值为 sds字典排序 2. `score` 值可以重复,如果 `score` 值一样,则按照 ele节点存储的值为 sds字典排序
3. Redis 跳跃表默认允许最大的层数是 32被源码中 `ZSKIPLIST_MAXLEVEL` 定义。 3. Redis 跳跃表默认允许最大的层数是 32被源码中 `ZSKIPLIST_MAXLEVEL` 定义。
## 和其余三种数据结构的比较 ## 和其余三种数据结构的比较