Redis 其他知识点

一、事务

(待补充内容)


二、多线程

2.1 多线程 I/O 的引入

因为 Redis 的性能瓶颈有时会出现在网络 I/O 的处理上,为了提高网络 I/O 的并行度,采用多个 I/O 线程来处理网络请求。但是对于命令的执行,Redis 仍然使用单线程来处理,引入了多线程 I/O 之后,Redis 的性能至少提升了一倍以上。

2.2 多线程配置

不过默认的多线程 I/O 只针对发送响应数据(write client socket),并不会以多线程的方式去处理读请求(read client socket)。

若要开启多线程处理客户端读请求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置项设为 yes,当然还有 IO 多线程个数的配置项。

注意: 主线程也算是一个 I/O 线程。

1
io-threads N

2.3 线程模型

在 Redis 6.0 版本之后,Redis 在启动的时候,默认会额外创建 4 个 IO 线程。

Redis-server 是 Redis 的主线程,平时我们使用的启动命令就是启动它,主要负责执行命令。


三、大 Key 问题

3.1 什么是 Redis 大 Key?

这里的大 key 并不是指 key 的值很大,而是指 key 对应的 value 很大。

一般而言,下面两种情况被称为大 key:

  • String 类型:值大于 10 KB
  • Hash、List、Set、Zset 类型:元素超过 5000 个

3.2 如何找到大 Key?

3.2.1 使用 redis-cli –bigkeys 命令

可以使用 redis-cli --bigkeys 命令查找大 key(自动扫描,快速报告大 key)。

1
redis-cli --bigkeys

3.2.2 使用 SCAN 命令

使用 SCAN 命令查找大 key(需要写脚本,自行判断大小)。

3.2.3 使用 RdbTools 工具

使用 RdbTools 工具查找大 key。


3.3 大 Key 会造成什么问题?

一般大 key 会带来下面四种影响:

3.3.1 客户端超时阻塞

因为 Redis 执行命令是单线程处理,所以在操作大 key 的时候会比较耗时,就会一直阻塞 Redis。

3.3.2 引发网络阻塞

每次获取大 key 的时候产生的网络流量较大。

比如说一个 key 的大小是 1 MB,每秒的访问量是 1000,那么每秒就会产生大概一个 G 的流量,这对于只有普通千兆网卡的服务器来说很明显压力过大了。

3.3.3 阻塞工作线程

如果使用 del 删除大 key,会阻塞工作线程,这样就不能执行后续其他的命令了。

3.3.4 内存分配不均

集群模型在 slot 分片均匀的情况下,会出现数据和查询倾斜的情况。部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。


3.4 如何删除大 Key?

3.4.1 删除大 Key 的原理

删除操作的本质就是要释放键值对占用的内存空间。

当然,释放内存只是第一步,为了可以更好地管理内存空间,在应用程序释放内存的时候,操作系统需要将释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配,这个过程是需要一定的时间的,而且会阻塞当前释放内存的应用程序。

所以说,如果我们一下子释放了大量的内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。

所以,我们在删除大 key 的时候,一定要小心。

3.4.2 删除方案

下面给出了两种方案来删除大 key:

  • 分批次删除
  • 异步删除(Redis 4.0 版本以上)

3.4.3 方案一:分批次删除

删除大 key,我们可以针对具体大 key 的类型定制不一样的删除策略:

删除大 Hash:

使用 hscan 命令获取一定的字段数量,然后再使用 hdel 命令。

1
2
3
# 示例:每次扫描100个字段并删除
HSCAN big_hash 0 COUNT 100
HDEL big_hash field1 field2 ...

删除大 List:

使用 ltrim 命令每次删除少量的元素。

1
2
# 示例:每次从右侧删除100个元素
LTRIM big_list 0 -101

删除大 Set:

使用 sscan 命令每次扫描一定的元素,然后再用 srem 命令删除。

1
2
3
# 示例:每次扫描100个元素并删除
SSCAN big_set 0 COUNT 100
SREM big_set member1 member2 ...

删除大 Zset:

使用 zremrangebyrank 每次删除一定的元素。

1
2
# 示例:每次删除前100个元素
ZREMRANGEBYRANK big_zset 0 99

3.4.4 方案二:异步删除(推荐)

从 Redis 4.0 版本之后,更推荐使用异步删除法,也就是使用 unlink 命令替代 del 来删除,这样 Redis 会将这个 key 放入到一个异步线程中进行删除,不会阻塞主线程。

1
2
# 使用 unlink 替代 del
UNLINK big_key

unlink 与 del 的区别:

  • del 命令:同步删除,会阻塞主线程
  • unlink 命令:异步删除,不会阻塞主线程,由后台线程处理

四、总结

4.1 多线程特性

特性 说明 配置项
多线程 I/O 提升网络 I/O 并行度 默认开启(Redis 6.0+)
命令执行 仍然是单线程 无需配置
读请求多线程 需要手动开启 io-threads-do-reads yes
I/O 线程数 默认 4 个 io-threads N

4.2 大 Key 问题总结

问题类型 影响 解决方案
客户端超时 阻塞 Redis 主线程 分批删除或异步删除
网络阻塞 占用大量带宽 避免频繁访问大 key
工作线程阻塞 影响其他命令执行 使用 unlink 异步删除
内存分配不均 集群数据倾斜 拆分大 key 或重新分片

4.3 大 Key 删除方案对比

删除方案 优点 缺点 适用版本
分批次删除 不阻塞主线程 需要编写脚本,操作复杂 所有版本
异步删除(unlink) 简单高效,不阻塞主线程 需要 Redis 4.0+ Redis 4.0+

4.4 最佳实践

  1. 多线程配置

    • Redis 6.0+ 默认开启多线程 I/O
    • 根据服务器 CPU 核心数合理配置 I/O 线程数
    • 命令执行仍然是单线程,保证数据一致性
  2. 避免大 Key

    • 设计时避免单个 key 存储过多数据
    • String 类型控制在 10 KB 以内
    • 集合类型控制在 5000 个元素以内
  3. 监控大 Key

    • 定期使用 redis-cli --bigkeys 扫描
    • 使用监控工具实时监控 key 的大小
    • 设置告警阈值及时发现问题
  4. 删除大 Key

    • Redis 4.0+ 优先使用 unlink 命令
    • Redis 4.0 以下版本使用分批删除
    • 避免在业务高峰期删除大 key
  5. 拆分大 Key

    • 将大 Hash 拆分成多个小 Hash
    • 将大 List 拆分成多个小 List
    • 使用合理的 key 命名规范便于管理

五、总结

Redis 的多线程特性和大 Key 问题是实际应用中需要重点关注的两个方面:

  1. 多线程 I/O:Redis 6.0 引入多线程 I/O 提升了网络处理性能,但命令执行仍是单线程,保证了数据一致性
  2. 大 Key 问题:会导致阻塞、网络拥塞、内存分配不均等问题,需要通过合理设计、监控和删除策略来避免
  3. 最佳实践:合理配置多线程参数,避免产生大 key,使用异步删除,建立监控告警机制

理解这些知识点,有助于我们更好地使用 Redis,构建高性能、高可用的应用系统。