Lever's Castle

Redis 6.0 的一些新特性

July 16, 2021

多线程io

Redis 一直是单线程的架构,虽然有些命令可以直接用后台线程或者子线程(比如数据删除,生成快照,AOF 重写),但是,从网络 io 到实际的读写命令处理,都是由单线程执行的。

随着网络硬件的性能提升,redis 的性能瓶颈可能会出现在网络 io 的处理上,也就是说,单线程处理网络请求的速度跟不上底层网络硬件接收请求的速度。

有两种方法面对这种问题:

1.用户态网络协议栈 DPDK 取代内核网络协议栈

2.采用多个 io 线程来处理网络请求

Redis 采用了方案二。

方案一虽然能避免内核频繁的处理网络请求,但是对 Redis 现有网络相关的代码改动太大,有很大的开发量,也容易引入新的 bug。

主线程和多 io 线程的协作可以分成四个阶段:

  1. 服务端和客户端建立 socket 连接,并分配处理线程。主线程负责创建 socket 连接,并将 socket 连接放到一个全局等待队列中,再通过轮询的方式分配给 io 线程
  2. io 线程读取并解析请求。主线程把 socket 分配完就进入阻塞状态,等待 io 线程的工作成果
  3. 主线程处理 io 线程解析好的请求
  4. io 线程将响应内容回写 socket,主线程清空全局请求队列,等待客户端的后续请求。

Redis 6.0 中多线程机制是默认关闭的,需要在配置中开启

io-threads-do-reads yes
io-threads 6

开启的 io 线程数与 cpu 核数相关,线程个数要小于 cpu 核数,对于 8 核的机器,redis 建议 io 线程设置为 6。我猜留两个线程,一个是主线程,一个是用来处理数据清理或者备份的子线程。

服务端协助的客户端缓存

称之为 tracking 功能。

如果客户端的缓存失效了,如何通知客户端?

Tracking 有两种模式解决这个问题:

  1. 普通模式,服务端记录被读取的 key,并监测 key 是否被修改,一旦 key 失效,服务端会给客户端发送 invalidate 消息,通知客户端缓存失效了。某个 key 的 invalidate 消息只会发送一次,即使被多次修改也只发一次。
  2. 广播模式,服务端会给客户端广播所有 key 失效的情况,key 如果被频繁修改,会产生大量消息,容易堵塞网络

    细粒度的权限控制

以前没办法控制权限,一些敏感操作,比如清空、遍历 key 等,只能通过 rename 来避免。

redis 6.0 支持创建不同的用户,对不同的用户访问权限进行控制(ACL)。甚至能够控制到不同用户能够访问的 key 范围:

# normaluser 只能访问 user: 为前缀的 key
ACL SETUSER normaluser ~user:* +@all

# normaluser 只能调用 hash 类型的命令操作,不能调用 string 类型的命令操作
ACL SETUSER normaluser +@hash -@string

启用 RESP3 协议

之前都是使用 RESP2 协议进行通信,通信内容通过字节数组进行编码,客户端需要根据操作的命令和数据类型自行对传输的数据解码,客户端开发的难度会比较高。

RESP3 增加了多种类型的区分编码,能够区分大部分数据类型。区分编码是通过在编码头部增加特殊标识来完成的。


Lever

痕迹
没有过去,就没法认定现在的自己