Redis 6.0 新特性:多线程、客户端缓存与安全

文章目录

Redis 6.0 是一次相当重要的版本升级。从 1.0 一路发展到 5.x,单线程几乎是 Redis 的"招牌"。6.0 第一次在网络 IO 层引入了多线程,配合客户端缓存、ACL 权限、RESP3 协议等新特性,让 Redis 在性能、安全和易用性上都迈出了一大步。理解这些新特性背后的设计动机,能帮我们判断什么时候该升级、怎么用好它们。

多线程 IO:突破单线程的吞吐瓶颈

Redis 一直坚持单线程的核心理由是:内存访问已经够快,多线程带来的复杂度(锁、原子操作、上下文切换)反而拖慢性能。但随着网络速度的提升和客户端数量增长,单线程处理 IO 的能力开始成为瓶颈。

实测数据:单线程 Redis 在 10Gbps 网络下,QPS 在 10-20 万左右就触顶,瓶颈不在命令处理,而在网络 IO。

6.0 多线程的设计

6.0 的多线程只用在网络 IO 层:读取请求、解析协议、回写响应。命令的实际执行还是单线程。这样既享受了多线程的吞吐提升,又不破坏 Redis 的单线程心智模型。

工作流程:

java 复制代码
主线程:accept 连接,把就绪的客户端任务分配给 IO 线程
   ↓
IO 线程:并行读取请求、解析协议
   ↓
主线程:单线程串行执行命令
   ↓
IO 线程:并行回写响应
   ↓
主线程:清理状态

主线程仍然是命令执行的"独木桥",但前后的 IO 工作被并行化了。

配置开关

conf 复制代码
io-threads 4                # IO 线程数
io-threads-do-reads yes     # 是否用多线程做读

io-threads 一般设置为 CPU 核数的一半到核数本身。Redis 官方建议:4 核机器设 2-3,8 核设 4-6。

io-threads-do-reads 默认 no,只在写回响应时启用多线程。如果想读也并行,设为 yes

什么时候开多线程

不是所有场景都该开。判断标准:

  • 请求量大、value 小:QPS 高但单次处理快,IO 比例高,开了有明显收益。
  • value 大:网络读写本身就慢,多线程能并行处理。
  • 请求量小:开了反而引入线程切换开销,不划算。
  • 单核机器:完全没必要。

实测一般在 4 核以上的机器,IO 密集场景,开多线程能带来 50%-100% 的吞吐提升。

客户端缓存:减少 Redis 访问

应用层做本地缓存能大幅减轻 Redis 压力,但难点在于"什么时候让本地缓存失效"。如果完全靠 TTL,要么过短失效频繁、要么过长读到旧数据。Redis 6.0 引入的 Tracking 机制解决了这个问题。

Tracking 的工作模式

服务端记住每个客户端读过哪些 key。当某个 key 被修改,服务端主动通知所有读过这个 key 的客户端"你的本地缓存过期了"。客户端收到通知后失效本地副本,下次读取重新拉。

java 复制代码
CLIENT TRACKING ON

GET user:1001
# 服务端记录:客户端 X 读过 user:1001

# 另一个客户端
SET user:1001 "new value"

# 服务端推送给客户端 X
INVALIDATE user:1001

两种 Tracking 模式

默认模式(精确跟踪)

服务端为每个客户端维护一份"读过哪些 key"的列表。这个列表叫 Invalidation Table,存在服务端,吃内存。

优点:精确,只通知真正读过这个 key 的客户端。

缺点:内存开销大,客户端越多、读的 key 越多,开销越大。

广播模式(Broadcast Mode)

客户端订阅特定 key 前缀,服务端不记录读取关系,key 变更时广播给所有订阅了对应前缀的客户端。

bash 复制代码
CLIENT TRACKING ON BCAST PREFIX user:

优点:服务端内存开销小,对前缀订阅的客户端共享通知。

缺点:客户端会收到自己没读过的 key 的失效通知,浪费一些处理开销。

适合用客户端缓存的场景

  • 读多写少:变化不频繁的数据,缓存命中率高。
  • 应用本地有充足内存:能放下缓存。
  • 延迟敏感:避免每次 Redis 网络往返。

热点商品详情、用户配置、系统参数都是典型受益场景。

ACL:精细化权限控制

6.0 之前 Redis 只有一个全局密码(requirepass),一旦有人拿到密码就能做所有事。这在多租户、多业务共享一个 Redis 实例的场景下风险很大。

6.0 引入了 ACL(Access Control List),支持创建多个用户,每个用户可以单独设置:

  • 密码
  • 允许执行的命令(白名单或黑名单)
  • 可以访问的 key 模式
  • 可以订阅的 channel
bash 复制代码
# 创建一个只读用户,只能访问 user:* 的 key
ACL SETUSER readonly_user on >password123 ~user:* +get +mget +exists -@all

各部分含义:

  • on:启用此用户
  • >password123:设置密码
  • ~user:*:只能访问匹配 user:* 的 key
  • +get +mget +exists:允许这些命令
  • -@all:禁止所有其他命令

ACL 的实践场景

  • 微服务隔离:每个服务一个用户,只能访问自己的 key 前缀。
  • 运维与开发分离:运维有完整权限,开发只能读写指定 key。
  • 防止误操作 :业务用户禁用 FLUSHDBFLUSHALLKEYS 等危险命令。
bash 复制代码
# 业务用户:禁用危险命令
ACL SETUSER app on >secret ~* +@all -flushdb -flushall -keys -config

RESP3 协议:表达力更强的协议

RESP(Redis Serialization Protocol)从 1.0 用到 5.x,简单文本格式。但它的表达力有限------比如返回值只能是 String、Array、Number 这几种基础类型,复杂结构要在客户端解析。

RESP3 引入了更多原生类型:

  • Map(键值对)
  • Set
  • Boolean
  • Double
  • Big Number
  • Null
  • Verbatim string(带格式的字符串)

这让 Redis 能直接返回结构化数据,客户端不用再做二次组装。

但 RESP3 是可选的,要客户端主动协商:

bash 复制代码
HELLO 3

老客户端继续用 RESP2,新客户端可以用 RESP3 享受更好的表达力。

其他改进

Cluster Proxy

6.0 后期版本提供了集群代理,让客户端不需要 Smart Client 就能访问 Cluster。这降低了 Cluster 的接入门槛。

SSL/TLS 支持

可以为 Redis 启用 TLS 加密通信,公网部署、跨机房部署时有用。

跨集群复制(CRDB)

商业版本提供,开源版本暂无。但社区已有讨论,未来值得关注。

升级到 6.0 的考虑

兼容性

6.0 在协议和命令层基本兼容 5.x,老应用可以平滑升级。但有几个点要注意:

  • 配置文件多了一些新选项。
  • 默认行为有些微调(比如 protected-mode)。
  • 模块兼容性需要测试。

性能影响

不开新特性的情况下,6.0 的性能和 5.x 相当或略好。开了多线程 IO 后,QPS 有显著提升。

风险与回退

任何主版本升级都有风险,建议:

  • 先在测试环境跑足够长的时间。
  • 灰度上线,先在非核心业务试用。
  • 准备好回退方案(5.x 的备份)。

实践建议

  1. 新项目直接上 6.x 或 7.x,享受新特性带来的红利。
  2. 多线程 IO 在 4 核以上才开,小机器开了反而慢。
  3. 客户端缓存选择广播模式,对服务端内存友好。
  4. 必用 ACL:哪怕是单用户场景,禁用危险命令也很值。
  5. 关注 RESP3 客户端支持情况,主流语言的客户端库都已支持。
  6. 持续关注 7.x 新特性:Function、Sharded Pub/Sub 等都是值得了解的演进方向。

Redis 6.0 标志着 Redis 从"简单 KV 存储"向"企业级数据平台"演进的转折点。多线程突破吞吐天花板、客户端缓存重新定义性能上限、ACL 提供安全保障------这些特性合在一起,让 Redis 能胜任更广泛、更严格的生产场景。