你知道 Redis 有哪些潜在的阻塞点吗

举报
芥末拌个饭吧 发表于 2022/11/14 13:40:53 2022/11/14
【摘要】 说到 redis,大家自然而然的会想到基于内存、单线程执行等。话说回来,Redis 真的只有单线程吗?这篇文章来说说 Redis 的异步机制。

前言

说到 redis,大家自然而然的会想到基于内存、单线程执行等。话说回来,Redis 真的只有单线程吗?这篇文章来说说 Redis 的异步机制。

有哪些影响 redis 性能的因素

我们从 Redis 内部及外部因素总结一下,主要有:

  1. redis 内部的阻塞式操作

  2. CPU 核和 NUMA 架构的影响

  3. Redis 关键系统配置

  4. Redis 内存碎片

  5. Redis 缓冲区

首先说说 Redis 实例的阻塞点:

  • 客户端:网络 IO,键值对的增删改查操作,数据库操作;

  • 磁盘:生成 RDB 快照,记录 AOF 日志,AOF 日志重写;

  • 主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;

  • 切片集群实例:向其他实例传输哈希槽信息,数据迁移。

1.客户端的阻塞

因 Redis 使用了 IO 多路复用机制,能避免主线程一直处于等待状态,网络 IO 不是导致 Redis 阻塞的因素。

而键值对的增删改查是主线程的主要工作,复杂度高的操作当然会阻塞 Redis 了。我们去判断复杂度高不高的标准就是看操作的复杂度是否为 O(N),也就是否要全表扫描。比如 hgetall, smembers 等操作就属于复杂度高的了。

然后还要注意的一个点就是数据的删除。删除本质上来说就是对键值对的内存空间进行释放。在释放内存时,操作系统需要将释放掉的内存块插入一个空闲内存块的链表,以便后续管理和再分配。这个过程会阻塞当前释放内存的应用程序。

如果这个键值对数据很大,比如一个 zset 包含大量元素,就会释放大量的内存。有测试过删除 100 万个元素的集合时,删除时间会达到 2s,要知道 Redis 的响应是毫秒级别的。所以这种 bigkey 的删除也会成为 Redis 的阻塞点。

清空数据库(flushdb、flushall)也涉及到删除和释放所有的键值对,也是 Redis 的阻塞点。

2. 磁盘带来的阻塞

AOF 重写和 RDB 快照,Redis 都用了子进程的方式操作,所以不会阻塞主线程。但 Redis 直接记录 AOF 日志,若有大量的写操作,并且配置的是同步写回的话,就会阻塞主线程了。

3. 主从节点带来的阻塞

在主从集群中,主库生成 RDB 文件,并传输给从库。主从复制过程的创建和传输 RDB 都是子进程处理的,不会阻塞主线程。 但是从库在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库,这又是一个阻塞点。而且,在从库清空数据库后,需要将 RDB 文件加载到内存,快慢和 rdb 文件大小相关。加载 RDB 文件又是一个阻塞点。

4. 切片集群的阻塞

切片集群的实例在负载均衡或者实例增加删除时,数据迁移是渐进式操作的,所以不会阻塞主线程。

总结一下,Redis 就有 5 个阻塞点:

  • 集合全量查询和聚合操作;

  • bigkey 删除;

  • 清空数据库;

  • AOF 日志同步写;

  • 从库加载 RDB 文件。

异步机制解决阻塞

我们通过异步的方式,去解决可能阻塞的场景。但也不是每个操作都能用异步的方式去解决。如果一个操作能够异步执行,说明客户端不需要马上得到具体值,在 Redis 中描述为若一个操作能异步执行,就意味着它不是主线程的关键路径的操作。

对于第一个阻塞点,因为读操作需要等待数据的返回,所以第一个阻塞点不能异步执行。第二个阻塞点和第三个阻塞点,因为删除不需要返回具体的结果,因此都可以用子线程去异步执行。第四个阻塞点“AOF 日志同步写”,也可启动子线程操作,不用让主线程等待 AOF 日志的写完成。第五个阻塞点 “从库加载 RDB 文件”,从库要想对客户端提供数据存取服务,就必须把 RDB 文件加载完成,不能启用子进程。

异步是如何进行的

Redis 主线程启动后,会使用操作系统提供的 pthread_create 函数创建 3 个子线程,分别由它们负责 AOF 日志写操作、键值对删除以及文件关闭的异步执行。

异步删除 lazy-free

lazy-free 机制是 Redis 收到删除指令后,主线程会将这个操作放入队列,然后马上给客户端返回一个完成信息。实际上删除还没执行呢。lazy-free 是 Redis4.0 之后才有的功能,需要手动开启。需要注意的是,即使开启了 lazy-free,如果直接使用 DEL 命令还是会同步删除 key,只有使用 UNLINK 命令才会可能异步删除 key。而且 Redis 在删除一个 key 时,首先会评估删除的时间成本,如果成本小,也不会异步执行,直接用主线程就完成返回了。

小结

本文总结 Redis 有哪些阻塞点,以及这些阻塞点是否可用异步机制去解决。但我们在使用 Redis 时,还是要避免 bigkey 的使用。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。