Redis阻塞的情况有哪些

命令阻塞

使用不当的命令造成客户端阻塞:

  • keys * :获取所有的 key 操作;

  • Hgetall:返回哈希表中所有的字段和;

  • Redis阻塞的情况有哪些

    smembers:返回集合中的所有成员;

这些命令时间复杂度是O(n),有时候也会全表扫描,随着n的增大耗时也会越大从而导致客户端阻塞。

SAVE 阻塞

大家都知道 Redis 在进行 RDB 快照的时候,会调用系统函数 fork() ,创建一个子线程来完成临时文件的写入,而触发条件正是配置文件中的 save 配置。

当达到我们的配置时,就会触发 bgsave 命令创建快照,这种方式是不会阻塞主线程的,而手动执行 save 命令会在主线程中执行,阻塞主线程。

同步持久化

当 Redis 直接记录 AOF 日志时,如果有大量的写操作,并且配置为同步持久化

appendfsync always

即每次发生数据变更会被立即记录到磁盘,因为写磁盘比较耗时,性能较差,所以有时会阻塞主线程。

AOF 重写
  • fork 出一条子线程来将文件重写,在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子线程创建新 AOF 文件期间,记录服务器执行的所有写命令。

  • 当子线程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。

  • 最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

阻塞就是出现在第2步的过程中,将缓冲区中新数据写到新文件的过程中会产生阻塞。

AOF 日志

AOF 的日志记录不像关系型数据库那样在执行命令之前记录日志(方便故障恢复),而是采用先执行命令后记录日志的方式。

原因就是 AOF 记录日志是不会对命令进行语法检查的,这样就能减少额外的检查开销,不会对当前命令的执行产生阻塞,但可能会给下一个操作带来阻塞风险。

这是因为 AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。

大 Key 问题

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

大 key 造成的阻塞问题如下:

  • 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。

  • 引发网络阻塞:每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。

  • 阻塞工作线程:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。

查找大 key

当我们在使用 Redis 自带的 --bigkeys 参数查找大 key 时,最好选择在从节点上执行该命令,因为主节点上执行时,会阻塞主节点。

  • 我们还可以使用 SCAN 命令来查找大 key;

  • 为了识别大键,需要先确保 Redis 使用了 RDB 持久化,并分析对应的 RDB 文件。网上有现成的工具:

    • redis-rdb-tools:Python 语言写的用来分析 Redis 的 RDB 快照文件用的工具

    • 这个工具叫做 rdb_bigkeys,是由 Go 语言编写的,可用于分析 Redis 的 RDB 快照文件,具有更高的性能。

删除大 key

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

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

所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞,如果主线程发生了阻塞,其他所有请求可能都会超时,超时越来越多,会造成 Redis 连接耗尽,产生各种异常。

删除大 key 时建议采用分批次删除和异步删除的方式进行。

清空数据库

清空数据库和上面 bigkey 删除也是同样道理,flushdb、flushall 也涉及到删除和释放所有的键值对,也是 Redis 的阻塞点。

集群扩容

Redis 集群可以进行节点的动态扩容缩容,这一过程目前还处于半自动状态,需要人工介入。

在扩缩容的时候,需要进行数据迁移。而 Redis 为了保证迁移的一致性,迁移所有操作都是同步操作。

执行迁移时,两端的 Redis 均会进入时长不等的阻塞状态,对于小Key,该时间可以忽略不计,但如果一旦 Key 的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换。



Redis作为一种内存数据库,在实际使用中,不免会遇到阻塞的情况,这对于很多开发者来说是一个极大的困扰。那么,Redis阻塞的情况有哪些呢?本文将从三个方面进行阐述。
一、阻塞操作的分类和描述
在Redis中,阻塞操作分为以下几种:
1.写操作阻塞:当Redis中的写缓冲区被耗尽时,所有新的写操作都会阻塞,直到写缓冲区有足够的空间来容纳它们。
2.列表的阻塞读写:当一个空的列表执行阻塞操作时,Redis会将阻塞操作放入阻塞列表中,并让列表处于一个阻塞状态,直到有新元素被添加到列表中。
3.阻塞订阅和发布:当Redis遇到阻塞订阅和发布时,所有未完整执行的订阅和发布操作都会被阻塞,直到完整执行此操作为止。
二、解决Redis阻塞的方法
1.使用集群模式:Redis集群模式可以很好地解决Redis单点阻塞的问题,集群模式中可以将Redis分散在多个节点上,分别运行不同的读写等操作,从而分担压力,降低Redis单点阻塞的概率。
2.分片技术:Redis可以使用分片技术,将数据分散在多个节点上,从而分配更多的资源给每个节点并减轻Redis的单点压力。
3.设置最大限制:在Redis中,可以设置写缓冲区的最大限制,当写入达到最大限制时,将不再写入更多的数据,从而减轻Redis的阻塞。
三、Redis阻塞对业务的影响
Redis阻塞可能会对业务产生一些负面影响:
1.增加系统负载:在Redis阻塞的情况下,系统通过重试等手段试图解除阻塞,导致系统负载增加。
2.延迟请求处理:Redis阻塞会导致请求响应延迟,增加请求的处理时间,从而影响业务。
3.可能出现数据不一致:当Redis阻塞时,前端命令执行的不确定性可能会导致数据的不一致,从而影响业务的正确性。
总之,要确保Redis的性能和稳定性,我们必须认真关注Redis阻塞的问题,找到解决方法,以确保业务的正确性和稳定性。