Kaito
2020-09-14
Redis的写操作(例如SET,HSET,SADD等)是在关键路径上吗? 我觉得这需要客户端根据业务需要来区分: 1、如果客户端依赖操作返回值的不同,进而需要处理不同的业务逻辑,那么HSET和SADD操作算关键路径,而SET操作不算关键路径。因为HSET和SADD操作,如果field或member不存在时,Redis结果会返回1,否则返回0。而SET操作返回的结果都是OK,客户端不需要关心结果有什么不同。 2、如果客户端不关心返回值,只关心数据是否写入成功,那么SET/HSET/SADD不算关键路径,多次执行这些命令都是幂等的,这种情况下可以放到异步线程中执行。 3、但是有种例外情况,如果Redis设置了maxmemory,但是却没有设置淘汰策略,这三个操作也都算关键路径。因为如果Redis内存超过了maxmemory,再写入数据时,Redis返回的结果是OOM error,这种情况下,客户端需要感知有错误发生才行。 另外,我查阅了lazy-free相关的源码,发现有很多细节需要补充下: 1、lazy-free是4.0新增的功能,但是默认是关闭的,需要手动开启。 2、手动开启lazy-free时,有4个选项可以控制,分别对应不同场景下,要不要开启异步释放内存机制: a) lazyfree-lazy-expire:key在过期删除时尝试异步释放内存 b) lazyfree-lazy-eviction:内存达到maxmemory并设置了淘汰策略时尝试异步释放内存 c) lazyfree-lazy-server-del:执行RENAME/MOVE等命令或需要覆盖一个key时,删除旧key尝试异步释放内存 d) replica-lazy-flush:主从全量同步,从库清空数据库时异步释放内存 3、即使开启了lazy-free,如果直接使用DEL命令还是会同步删除key,只有使用UNLINK命令才会可能异步删除key。 4、这也是最关键的一点,上面提到开启lazy-free的场景,除了replica-lazy-flush之外,其他情况都只是*可能*去异步释放key的内存,并不是每次必定异步释放内存的。 开启lazy-free后,Redis在释放一个key的内存时,首先会评估代价,如果释放内存的代价很小,那么就直接在主线程中操作了,没必要放到异步线程中执行(不同线程传递数据也会有性能消耗)。 什么情况才会真正异步释放内存?这和key的类型、编码方式、元素数量都有关系(详细可参考源码中的lazyfreeGetFreeEffort函数): a) 当Hash/Set底层采用哈希表存储(非ziplist/int编码存储)时,并且元素数量超过64个 b) 当ZSet底层采用跳表存储(非ziplist编码存储)时,并且元素数量超过64个 c) 当List链表节点数量超过64个(注意,不是元素数量,而是链表节点的数量,List的实现是在每个节点包含了若干个元素的数据,这些元素采用ziplist存储) 只有以上这些情况,在删除key释放内存时,才会真正放到异步线程中执行,其他情况一律还是在主线程操作。 也就是说String(不管内存占用多大)、List(少量元素)、Set(int编码存储)、Hash/ZSet(ziplist编码存储)这些情况下的key在释放内存时,依旧在主线程中操作。 可见,即使开启了lazy-free,String类型的bigkey,在删除时依旧有阻塞主线程的风险。所以,即便Redis提供了lazy-free,我建议还是尽量不要在Redis中存储bigkey。 个人理解Redis在设计评估释放内存的代价时,不是看key的内存占用有多少,而是关注释放内存时的工作量有多大。从上面分析基本能看出,如果需要释放的内存是连续的,Redis作者认为释放内存的代价比较低,就放在主线程做。如果释放的内存不连续(大量指针类型的数据),这个代价就比较高,所以才会放在异步线程中去执行。 如果我的理解有偏差,还请老师和大家指出!
展开
共 80 条评论
545
注定非凡
2020-10-21
1,作者讲了什么? Redis有哪些可能导致阻塞的操作,以及解决机制 2,作者是怎么把这件事讲明白的? 1,分类:通过Redis实例的交互关系,拆分了四类:客户端,磁盘,主从集群,分片集群 2,提出关键路径操作概念,作为是否可以异步处理的判断标准 3,为了讲明白,作者讲了哪些要点?有哪些亮点? 1,亮点1:将Redis的可能阻塞点划分了四块,与客户端交互,与磁盘交互,主从实例交互,分片集群实例交互 2,亮点2:通过关键路径上操作概念,建立了判断是否适合异步操作的标准 3,要点1:与客户端交互时的阻塞点:高复杂度的增删改操作(集合全量的查询和聚合操作,bigKey 删除操作,清空数据库) 4,要点2:与磁盘交互的阻塞点:aof日志实时同步写回 5,要点3:主从节点交互时的阻塞点:从库加载RDB文件 6,要点4:分片集群的阻塞点:哈希槽bigkey数据迁移 7,要点5:关键路径操作概念:客户端把请求发给Redis后,要等着Redis返回数据结果的操作 8,要点6:异步的子线程机制:主线程通过一个链表形式的任务队列和子线程进行交互 4,对于作者所讲,我有哪些发散性思考? 给自己提了几个问题: 1,Redis至少有几个线程? 2,网络IO有时会比较慢,网络IO是否为Redis的阻塞点? 3,删除操作为什么会阻塞Redis 5,将来在哪些场景里,我能够使用它? 6,留言收获 什么时候Redis会真正的异步释放内存?(答案来自@kaito 大佬) lazy free机制:Redis收到键值对删除和清空数据库的指令时,主线线程会把这个操作封装成一个任务,放入任务队列中,然后给客户端返回一个完成信息,但实际上,这个删除还没有执行,需要等待后台子线程从任务队列中读取到这个任务后,才开始实际删除键值对,并释放相应的内存空间。 但是:lazy-free是4.0新增功能,默认关闭。开启这个配置后, 除了replica-lazy-flush之外,其他情况都只是*可能*去异步释放key的内存,并不是每次必定异步释放内存的。是否会真正异步释放内存,取决于key的类型,编码方式,元素数量,所以 即使开启了lazy-free,String类型的bigkey,在删除时依旧有阻塞主线程的风险
展开
38
Spring4J
2020-09-14
Redis的异步子线程机制就跟java里面的线程池原理差不多,都是主线程封装任务到队列中,子线程到队列中拉取任务异步执行,运用了生产者消费者的模型
11
一步
2020-09-15
在删除一个 大 key 的时候,redis 把这个任务放到队列,实际还没有执行删除操作,这时候马上来个查询查刚才删除的 key. 这时候会查询到吗? Redis 是怎么处理这种情况的?
共 3 条评论
9
一步
2020-09-15
释放掉的内存块插入一个空闲内存块的链表 =================== 这个过程怎么会耗时间呢? 插入链表的时候,不就是在链表尾部一放不就可以了吗? 时间复杂度位 O(1)
共 5 条评论
6
漫步oo0云端
2020-09-14
今天学习了5种阻塞点,请问老师,后面会学习,当redis发生阻塞时如何分析是什么操作导致的这个技能吗?
共 1 条评论
6
Geek_1b4c8
2022-04-08
异步删除key时,如果删除操作在任务队列里面还未被子线程执行,此时如果来了get查询操作,是否会返回数据吗,异步删除时是否会先标识为删除吗
共 1 条评论
2
test
2020-09-14
写操作是否在关键路径,需要看使用方是否需要确认写入已经完成。
2
那时刻
2020-09-14
当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到任务队列中。后台子线程读取任务后,然后写入AOF日志。请问老师,如果写入操作比较频繁,是否也会引起redis延迟增大呢?
共 1 条评论
2
Geek_77c5bf
2021-03-31
老师我一直有个问题,你上面说,如果是读操作。读操作是主线程来处理查询并返回的吗?意思是同步的?我的理解,客户端的确是一直在等待结果,但是redis是异步的,是基于事件通知回调机制,等redis查到结果,再socket通知客户端。希望老师帮忙解答,困扰我很久
共 2 条评论
1