如何避免单线程模型的阻塞?
Redis 性能的 5 大方面的潜在因素,分别是:
- Redis 内部的阻塞式操作;
- CPU 核和 NUMA 架构的影响;
- Redis 关键系统配置;
- Redis 内存碎片;
- Redis 缓冲区。
Redis 内部的阻塞式操作
Redis 实例有哪些阻塞点
和客户端交互时的阻塞点
Redis 使用了 IO 多路复用机制,网络 IO 不是导致 Redis 阻塞的因素。复杂度高的增删改查操作肯定会阻塞 Redis。
第一个阻塞点:集合全量查询和聚合操作
第二个阻塞点:bigkey 删除操作
第三个阻塞点:清空数据库
和磁盘交互时的阻塞点
因为磁盘 IO 一般都是比较费时费力,Redis 采用子进程方式生成RDB快照,以及执行 AOF 日志重写操作。
Redis 直接记录 AOF 日志时,会根据不同的写回策略对数据做落盘保存。(AOF日志的几个选项:always,同步落盘;everysec,每秒保存;No,数据保存在内存缓冲区,由操作系统决定何时写回)
第四个阻塞点:AOF 日志同步写。
主从节点交互时的阻塞点
在主从集群中,主库需要生成 RDB 文件,并传输给从库。库来接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库,然后把 RDB 文件加载到内存。
第五个阻塞点:加载 RDB 文件
切片集群实例交互时的阻塞点
部署 Redis 切片集群时,每个 Redis 实例上分配的哈希槽信息需要在不同实例间进行传递,同时,当需要进行负载均衡或者有实例增删时,数据会在不同的实例间进行迁移。不过,哈希槽的信息量不大,而数据迁移是渐进式执行的,所以,一般来说,这两类操作对 Redis 主线程的阻塞风险不大。
如果你使用了 Redis Cluster 方案,而且同时正好迁移的是 bigkey 的话,就会造成主线程的阻塞,因为 Redis Cluster 使用了同步迁移。当没有 bigkey 时,切片集群的各实例在进行交互时不会阻塞主线程。
总结
Redis 实例五个阻塞点:
- 集合全量查询和聚合操作;
- bigkey 删除;
- 清空数据库;
- AOF 日志同步写;
- 从库加载 RDB 文件。
阻塞点异步执行
集合全量查询和聚合操作和从库加载 RDB 文件是没有办法异步执行操作。
第四个阻塞点“AOF 日志同步写”来说,为了保证数据可靠性,Redis 实例需要保证 AOF 日志中的操作记录已经落盘,这个操作虽然需要实例等待,但它并不会返回具体的数据结果给实例。所以,我们也可以启动一个子线程来执行 AOF 日志的同步写,而不用让主线程等待 AOF 日志的写完成。
删除操作并不需要给客户端返回具体的数据结果,删除key 引用关系使用后台子线程来异步执行删除操作也称为惰性删除(lazy free)。