主线程阻塞
Redis 实例有哪些阻塞点
和客户端交互时的阻塞点和磁盘交互时的阻塞点主从节点交互时的阻塞点集群实例交互时的阻塞点 主线程阻塞
如果在主线程上执行的操作消耗的时间太长,就会引起主线程阻塞。但是,Redis 既有服务客户端请求的键值对增删改查操作,也有保证可靠性的持久化操作,还有进行主从复制时的数据同步操作,等等。操作这么多,究竟哪些会引起阻塞
Redis 实例有哪些阻塞点客户端:网络 IO,键值对增删改查操作,数据库操作磁盘:生成 RDB 快照,记录 AOF 日志,AOF 日志重写;主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;切片集群实例:向其他实例传输哈希槽信息,数据迁移。 和客户端交互时的阻塞点
集合全量查询和聚合操作。
Redis 中涉及集合的操作复杂度通常为 O(N),复杂度高的增删改查操作肯定会阻塞 Redis,例如集合元素全量查询操作 HGETALL、SMEMBERS,以及集合的聚合统计操作,例如求交、并和差集。bigkey 删除操作
删除操作的本质是要释放键值对占用的内存空间,释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序,所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。 集合类型 10W (8byte) 100W(8byte) 10W (128byte) 100W(128byte) hash50ms962ms91ms1980msList25ms133ms29ms283msset42ms821ms75ms1347mszset53ms809ms61ms991ms
当元素数量从 10 万增加到 100 万时,4 大集合类型的删除时间的增长幅度从 5 倍上升到了近 20 倍;
集合元素越大,删除所花费的时间就越长;
当删除有 100 万个元素的集合时,最大的删除时间绝对值已经达到了 1.98s(Hash 类型)。Redis 的响应时间一般在微秒级别,所以,一个操作达到了近 2s,不可避免地会阻塞主线程。
清空数据库 和磁盘交互时的阻塞点
Redis 进一步设计为采用子进程的方式生成 RDB 快照文件,以及执行 AOF 日志重写操作。这样一来,这两个操作由子进程负责执行,慢速的磁盘 IO 就不会阻塞主线程了。
但是Redis 直接记录 AOF 日志时,会根据不同的写回策略对数据做落盘保存。一个同步写磁盘的操作的耗时大约是 1~2ms,如果有大量的写操作需要记录在 AOF 日志中,并同步写回的话,就会阻塞主线程了
主从集群中,主库需要生成 RDB 文件,并传输给从库。主库在复制的过程中,创建和传输 RDB 文件都是由子进程来完成的,不会阻塞主线程。但是,对于从库来说,它在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库
集群实例交互时的阻塞点使用了 Redis Cluster 方案,而且同时正好迁移的是 bigkey 的话,就会造成主线程的阻塞
bigkey 删除、清空数据库、AOF 日志同步写不属于关键路径操作,可以使用异步子线程机制来完成。Redis 在运行时会创建三个子线程,主线程会通过一个任务队列和三个子线程进行交互。子线程会根据任务的具体类型,来执行相应的异步操作。
异步删除操作是 Redis 4.0 以后才有的功能,如果你使用的是 4.0 之前的版本,当你遇到 bigkey 删除时,可以先使用集合类型提供的 SCAN 命令读取数据,然后再进行删除。因为用 SCAN 命令可以每次只读取一部分数据并进行删除,这样可以避免一次性删除大量 key 给主线程带来的阻塞。