17 | Lazy Free会影响缓存替换吗?
蒋德钧
你好,我是蒋德钧。
Redis 缓存淘汰算法的目的,其实是为了在 Redis server 内存使用量超过上限值的时候,筛选一些冷数据出来,把它们从 Redis server 中删除,以保证 server 的内存使用量不超出上限。我们在前两节课,已经分别学习了 Redis 源码对 LRU 算法和 LFU 算法的实现,这两种算法在最后淘汰数据的时候,都会删除被淘汰的数据。
不过,无论是 LRU 算法还是 LFU 算法,它们在删除淘汰数据时,实际上都会根据 Redis server 的 lazyfree-lazy-eviction 配置项,来决定是否使用 Lazy Free,也就是惰性删除。
惰性删除是 Redis 4.0 版本后提供的功能,它会使用后台线程来执行删除数据的任务,从而避免了删除操作对主线程的阻塞。但是,后台线程异步删除数据能及时释放内存吗?它会影响到 Redis 缓存的正常使用吗?
今天这节课,我就来给你介绍下惰性删除在缓存淘汰时的应用。了解这部分内容,你就可以掌握惰性删除启用后,会给 Redis 缓存淘汰和内存释放带来的可能影响。这样,当你在实际应用中,遇到 Redis 缓存内存容量的问题时,你就多了一条排查思路了。
好,那么接下来,我们就先来看下缓存淘汰时的数据删除的基本过程。不过在了解这个删除过程之前,我们需要先了解下 Redis server 启动惰性删除的配置。因为在 Redis 源码中,有不少地方都会根据 server 是否启动惰性删除,来执行不同的分支操作。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
Redis缓存淘汰算法中的惰性删除机制是本文的重点。文章详细介绍了惰性删除的设置和四个配置项,以及被淘汰数据的删除过程。通过对异步和同步删除操作的实现原理和技术细节的介绍,读者可以快速了解Redis缓存淘汰算法中惰性删除的工作原理。此外,文章还展示了在实际执行过程中,如何根据被淘汰键值对包含的元素个数来决定是使用后台线程还是主线程来进行删除操作。另外,文章还介绍了主线程如何知道后台线程释放的内存空间已经满足待释放空间的大小,以及在满足最大内存容量要求后如何停止淘汰数据的执行流程。通过这些内容,读者可以全面了解Redis缓存淘汰算法中惰性删除的实现过程和技术细节,为读者在实际应用中排查Redis缓存内存容量问题提供了一条思路。文章还介绍了基于同步删除和异步删除的数据淘汰过程,以及释放内存空间的具体操作。最后,文章提醒读者,虽然惰性删除是由后台线程异步完成的,但后台线程需要通过同步机制获取任务,这会引入一些额外的时间开销,导致内存释放不如同步删除那样及时。因此,Redis在被淘汰数据是小集合时仍然使用主线程进行内存释放。整体而言,本文深入剖析了Redis缓存淘汰算法中的惰性删除机制,为读者提供了深入了解和应用该算法的指导。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 源码剖析与实战》,新⼈⾸单¥59
《Redis 源码剖析与实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(6)
- 最新
- 精选
- 🤐置顶从源码可以体会到redis作者的精益求精2022-07-242
- Kaito1、lazy-free 是 4.0 新增的功能,默认是关闭的,需要手动开启 2、开启 lazy-free 时,有多个「子选项」可以控制,分别对应不同场景下,是否开启异步释放内存: a) lazyfree-lazy-expire:key 在过期删除时尝试异步释放内存 b) lazyfree-lazy-eviction:内存达到 maxmemory 并设置了淘汰策略时尝试异步释放内存 c) lazyfree-lazy-server-del:执行 RENAME/MOVE 等命令或需要覆盖一个 key 时,Redis 内部删除旧 key 尝试异步释放内存 d) replica-lazy-flush:主从全量同步,从库清空数据库时异步释放内存 3、即使开启了 lazy-free,但如果执行的是 DEL 命令,则还是会同步释放 key 内存,只有使用 UNLINK 命令才「可能」异步释放内存 4、Redis 6.0 版本新增了一个新的选项 lazyfree-lazy-user-del,打开后执行 DEL 就与 UNLINK 效果一样了 5、最关键的一点,开启 lazy-free 后,除 replica-lazy-flush 之外,其它选项都只是「可能」异步释放 key 的内存,并不是说每次释放 key 内存都是丢到后台线程的 6、开启 lazy-free 后,Redis 在释放一个 key 内存时,首先会评估「代价」,如果代价很小,那么就直接在「主线程」操作了,「没必要」放到后台线程中执行(不同线程传递数据也会有性能消耗) 7、什么情况才会真正异步释放内存?这和 key 的类型、编码方式、元素数量都有关系(详见 lazyfreeGetFreeEffort 函数): a) 当 Hash/Set 底层采用哈希表存储(非 ziplist/int 编码存储)时,并且元素数量超过 64 个 b) 当 ZSet 底层采用跳表存储(非 ziplist 编码存储)时,并且元素数量超过 64 个 c) 当 List 链表节点数量超过 64 个(注意,不是元素数量,而是链表节点的数量,List 底层实现是一个链表,链表每个节点是一个 ziplist,一个 ziplist 可能有多个元素数据) 只有满足以上条件,在释放 key 内存时,才会真正放到「后台线程」中执行,其它情况一律还是在主线程操作。 也就是说 String(不管内存占用多大)、List(少量元素)、Set(int 编码存储)、Hash/ZSet(ziplist 编码存储)这些情况下的 key,在释放内存时,依旧在「主线程」中操作。 8、可见,即使打开了 lazy-free,String 类型的 bigkey,在删除时依旧有「阻塞」主线程的风险。所以,即便 Redis 提供了 lazy-free,还是不建议在 Redis 存储 bigkey 9、Redis 在释放内存「评估」代价时,不是看 key 的内存大小,而是关注释放内存时的「工作量」有多大。从上面分析可以看出,如果 key 内存是连续的,释放内存的代价就比较低,则依旧放在「主线程」处理。如果 key 内存不连续(包含大量指针),这个代价就比较高,这才会放在「后台线程」中执行 课后题:freeMemoryIfNeeded 函数在使用后台线程,删除被淘汰数据的过程中,主线程是否仍然可以处理外部请求? 肯定是可以继续处理请求的。 主线程决定淘汰这个 key 之后,会先把这个 key 从「全局哈希表」中剔除,然后评估释放内存的代价,如果符合条件,则丢到「后台线程」中执行「释放内存」操作。 之后就可以继续处理客户端请求,尽管后台线程还未完成释放内存,但因为 key 已被全局哈希表剔除,所以主线程已查询不到这个 key 了,对客户端来说无影响。2021-09-02924
- 风轻扬回答一下课后题。 后台线程是将任务放进了队列中,在放的这个过程中,是主线程的同步操作,在这一瞬间,redis是不能对外处理请求的,但是一旦将任务放进了队列中,后面的释放工作就是由后台任务执行,主线程就可以处理客户端的请求了2023-11-15归属地:北京
- Geek_轻风异步删除后,一直要等到检查满足已释放足够内存的条件才会停止删除。所以,最开始的几次异步删除后不会立刻结束这个函数。后面还有异步删除的话,说不定之前的异步删除已经释放了足够的内存,所以退出了删除循环。这个函数退出后,Redis才能接着处理网络请求2023-03-06归属地:山西
- 云海惰性删除,每删除16个key后会计算一次内存。那么问题来了,计算内存的操作是否会消耗很多资源?2021-10-251
- 奕现在有个问题,如果配置的 后台线程删除,在删除前后计算 内存使用量的时候 (long long)zmalloc_used_memory(); 是不是就不准了?这时候 key 和 value 的内存还没有得到释放的,只是把全局和失效 哈希表里面的删除了2021-09-034
收起评论