浅谈Redis的异步机制
来源:脚本之家
时间:2022-12-30 12:07:56 117浏览 收藏
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《浅谈Redis的异步机制》,聊聊Redis异步,我们一起来看看吧!
Redis 内部的阻塞式操作:
- CPU 核和 NUMA 架构的影响;
- Redis 关键系统配置;
- Redis 内存碎片;
- Redis 缓冲区。
一、Redis 的阻塞点
和 Redis 实例交互的对象,以及交互时会发生的操作:
- 客户端:网络 IO,键值对增删改查操作,数据库操作;
- 磁盘:生成 RDB 快照,记录 AOF 日志,AOF 日志重写;
- 主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;
- 切片集群实例:向其他实例传输哈希槽信息,数据迁移。
4 类交互对象和具体的操作之间的关系:
和客户端交互时的阻塞点:
网络 IO 有时候会比较慢,但是 Redis 使用了 IO 多路复用机制,避免了主线程一直处在等待网络连接或请求到来的状态,所以网络 IO 不是导致 Redis 阻塞的因素。
键值对的增删改查操作是 Redis 和客户端交互的主要部分,也是 Redis 主线程执行的主要任务。复杂度高的增删改查操作肯定会阻塞 Redis。
判断操作复杂度高低的标准:看操作的复杂度是否为 O(N)。
Redis 的第一个阻塞点:集合全量查询和聚合操作:
Redis 中涉及集合的操作复杂度通常为 O(N),使用时需重视起来。
例如集合元素全量查询操作 HGETALL、SMEMBERS,以及集合的聚合统计操作,例如求交、并和差集。
Redis 的第二个阻塞点 :bigkey 删除操作
集合自身的删除操作同样也有潜在的阻塞风险。删除操作的本质是要释放键值对占用的内存空间。 释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。
这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。
释放大量内存的时机:在删除大量键值对数据的时候,删除包含了大量元素的集合,也称为 bigkey 删除。
不同元素数量的集合在进行删除操作时所消耗的时间:
得出三个结论:
- 当元素数量从 10 万增加到 100 万时,4 大集合类型的删除时间的增长幅度从 5 倍上升到了近 20 倍;
- 集合元素越大,删除所花费的时间就越长;
- 当删除有 100 万个元素的集合时,最大的删除时间绝对值已经达到了 1.98s(Hash 类 型)。Redis 的响应时间一般在微秒级别,一个操作达到了近 2s,不可避免地会阻塞主线程。
Redis 的第三个阻塞点:清空数据库
既然频繁删除键值对都是潜在的阻塞点了,在 Redis 的数据库级别操作中,清空数据库(例如 FLUSHDB 和 FLUSHALL 操作)也是一个潜在的阻塞风险,因为它涉及到删除和释放所有的键值对。
Redis 的第四个阻塞点:AOF 日志同步写
磁盘 IO 一般都是比较费时费力的,需要重点关注。 Redis 开发者早已认识到磁盘 IO 会带来阻塞,所以把 Redis 设计为采用子进程的方式生成 RDB 快照文件、执行 AOF 日志重写操作。由子进程负责执行,慢速的磁盘 IO 就不会阻塞主线程了。
Redis 直接记录 AOF 日志时,会根据不同的写回策略对数据做落盘保存。一个同步写磁盘的操作的耗时大约是 1~2ms,如果有大量的写操作需要记录在 AOF 日志中,并同步写回的话,会阻塞主线程。
Redis 的第五个阻塞点:从库加载 RDB 文件
在主从集群中,主库需要生成 RDB 文件,并传输给从库。
主库在复制的过程中,创建和传输 RDB 文件都是由子进程来完成的,不会阻塞主线程。
但是从库在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库,正好撞上了第三个阻塞点。
从库在清空当前数据库后,需要把 RDB 文件加载到内存,这个过程的快慢和 RDB 文件的大小密切相关,RDB 文件越大,加载过程越慢。
切片集群实例交互时的阻塞点
部署 Redis 切片集群时,每个 Redis 实例上分配的哈希槽信息需要在不同实例间进行传递,当需要进行负载均衡或者有实例增删时,数据会在不同的实例间进行迁移。不过哈希槽的信息量不大,而数据迁移是渐进式执行的,这两类操作对 Redis 主线程的阻塞风险不大。
如果使用了 Redis Cluster 方案,而且同时正好迁移的是 bigkey 的话,就会造成主线程的阻塞,因为 Redis Cluster 使用了同步迁移。
五个阻塞点:
- 集合全量查询和聚合操作;
- bigkey 删除;
- 清空数据库;
- AOF 日志同步写;
- 从库加载 RDB 文件。
二、可以异步执行的阻塞点
为了避免阻塞式操作,Redis 提供了异步线程机制:
Redis 会启动一些子线程,然后把一些任务交给这些子线程,让它们在后台完成,而不再由主线程来执行这些任务。可以避免阻塞主线程。
异步执行对操作的要求:
一个能被异步执行的操作并不是 Redis 主线程的关键路径上的操作(客户端把请求发送给 Redis 后,等着 Redis 返回数据结果的操作)。
主线程接收到操作 1 后,操作 1 并不用给客户端返回具体的数据,主线程可以把它交给后台子线程来完成,同时只要给客户端返回一个“OK”结果就行。
在子线程执行操作 1 的时候,客户端又向 Redis 实例发送了操作 2,客户端是需要使用操作 2 返回的数据结果的,如果操作 2 不返回结果,那么客户端将一直处于等待状态。
操作 1 就不算关键路径上的操作,因为它不用给客户端返回具体数据,所以可以由后台子线程异步执行。
操作 2 需要把结果返回给客户端,它就是关键路径上的操作,所以主线程必须立即把这个操作执行完。
- Redis 读操作是典型的关键路径操作,因为客户端发送了读操作之后,就会等待读取的数据返回,以便进行后续的数据处理。而 Redis 的第一个阻塞点“集合全量查询 和聚合操作”都涉及到了读操作,不能进行异步操作。
- 删除操作并不需要给客户端返回具体的数据结果,不算是关键路径操作。“bigkey 删除”和“清空数据库”都是对数据做删除,并不在关键路径上。可以使用后台子线程来异步执行删除操作。
- “AOF 日志同步写”,为了保证数据可靠性,Redis 实例需要保证 AOF 日志中的操作记录已经落盘,这个操作虽然需要实例等待,但它并不会返回具体的数据结果给实例。所以可以启动一个子线程来执行 AOF 日志的同步写。
- “从库加载 RDB 文件”要想对客户端提供数据存取服务,就必须把 RDB 文件加载完成。这个操作也属于关键路径上的操作,必须让从库的主线程来执行。
除了“集合全量查询和聚合操作”和“从库加载 RDB 文 件”,其他三个阻塞点涉及的操作都不在关键路径上,可以使用 Redis 的异步子线程机制来实现 bigkey 删除,清空数据库,以及 AOF 日志同步写。
三、异步的子线程机制
Redis 主线程启动后,会使用操作系统提供的 pthread_create 函数创建 3 个子线程,负责AOF 日志写操作、键值对删除、文件关闭的异步执行。
主线程通过一个链表形式的任务队列和子线程进行交互。
当收到键值对删除和清空数据库的操作时,主线程会把这个操作封装成一个任务,放入到任务队列中,然后给客户端返回一个完成信息,表明删除已经完成。
但实际上,这个时候删除还没有执行,等到后台子线程从任务队列中读取任务后,才开始实际删除键值对,并释放相应的内存空间。这种异步删除也称为惰性删除 (lazy free)。
当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到任务队列中。后台子线程读取任务后,开始自行写入 AOF 日志,主线程就不用一直等待 AOF 日志写完了。
Redis 中的异步子线程执行机制:
异步的键值对删除和数据库清空操作是 Redis 4.0 后提供的功能,Redis 也提供了新的命令来执行这两个操作:
- 键值对删除:集合类型中有大量元素(例如有百万级别或千万级别元素)需要删除时,建议使用 UNLINK 命令;
- 清空数据库:可以在 FLUSHDB 和 FLUSHALL 命令后加上 ASYNC 选项,让后台子线程异步地清空数据库。
FLUSHDB ASYNC FLUSHALL AYSNC
总结
Redis 实例运行时的 4 大类交互对象:客户端、磁盘、主从库实例、 切片集群实例。
基于这 4 大类交互对象导致 Redis 性能受损的 5 大阻塞点:集合全量查询和聚合操作、bigkey 删除、清空数据库、AOF 日志同步写、从库加载 RDB 文件。
bigkey 删除、清空数据库、AOF 日志同步写不属于关键路径操作, 可以使用异步子线程机制来完成。
Redis 在运行时会创建三个子线程,主线程会通过一个任务队列和三个子线程进行交互。子线程会根据任务的具体类型,来执行相应的异步操作。
异步删除操作是 Redis 4.0 以后才有的功能,如果使用的是 4.0 之前的版本,遇到 bigkey 删除时,建议:先使用集合类型提供的 SCAN 命令读取数据, 然后再进行删除。因为用 SCAN 命令可以每次只读取一部分数据并进行删除,这样可以避免一次性删除大量 key 给主线程带来的阻塞。
例如,对于 Hash 类型的 bigkey 删除,使用 HSCAN 命令,每次从 Hash 集合中获取一部分键值对(例如 200 个),再使用 HDEL 删除这些键值对,可以把删除压力分摊到多次操作中,每次删除操作的耗时就不会太长,也就不会阻塞主线程了。
集合全量查询和聚合操作、从库加载 RDB 文件是在关键路径上,无法使用异步操作来完成。
建议:
- 集合全量查询和聚合操作:使用 SCAN 命令,分批读取数据,再在客户端进行聚合计算;
- 从库加载 RDB 文件:把主库的数据量大小控制在 2~4GB 左右,以保证 RDB 文件能以较快的速度加载。
今天关于《浅谈Redis的异步机制》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
342 收藏
-
361 收藏
-
159 收藏
-
164 收藏
-
221 收藏
-
156 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 清秀的老鼠
- 很详细,码起来,感谢楼主的这篇技术贴,我会继续支持!
- 2023-03-28 15:10:57
-
- 鲤鱼花生
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享文章内容!
- 2023-03-11 13:04:13
-
- 闪闪的百褶裙
- 这篇技术文章太及时了,太细致了,受益颇多,已收藏,关注作者大大了!希望作者大大能多写数据库相关的文章。
- 2023-03-09 05:32:15
-
- 忧虑的乌冬面
- 这篇文章真是及时雨啊,很详细,很有用,mark,关注up主了!希望up主能多写数据库相关的文章。
- 2023-01-08 19:28:45
-
- 激情的项链
- 很详细,码起来,感谢作者大大的这篇文章,我会继续支持!
- 2023-01-08 04:12:39
-
- 强健的夕阳
- 这篇技术文章太及时了,很详细,很有用,mark,关注博主了!希望博主能多写数据库相关的文章。
- 2023-01-06 12:27:28
-
- 包容的鼠标
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享文章内容!
- 2023-01-05 02:07:15
-
- 贪玩的唇膏
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享文章!
- 2023-01-04 17:57:00
-
- 彩色的日记本
- 这篇博文真是及时雨啊,作者加油!
- 2023-01-04 05:06:24
-
- 要减肥的冰淇淋
- 好细啊,已加入收藏夹了,感谢作者的这篇文章内容,我会继续支持!
- 2023-01-03 20:03:30
-
- 舒服的大地
- 这篇博文太及时了,好细啊,很棒,已收藏,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-01-02 06:11:39
-
- 单纯的雪糕
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢楼主分享文章!
- 2023-01-01 12:44:07