1. LRU cache是什么?
LRU cache是一个缓存系统,特殊之处在于缓存的容量是有限的,当缓存内容超出预设容量之后,会淘汰最久未访问的缓存内容。
在很多架构设计中,常常出现LRU cache的身影,它是用来平衡成本和缓存命中率矛盾问题,采取的折中思路之一。在有限的缓存容量下,尽可能多的命中缓存内容。
2. 基于redis实现LRU cache
redis是开源的、内存型的存储系统,其内置了多种常见的数据类型和丰富的淘汰策略,可以很方便的基于redis实现一个LRU cache系统。redis基本介绍见:https://zhuanlan.zhihu.com/p/132353640
2.1 redis设置cache最大容量
假设限制redis最大cache容量为100mb,那么在redis.conf以下配置:
1 | maxmemory 100mb |
当缓存内容持续上涨,突破到100mb之后,就会触发redis的数据驱除策略。
2.2 redis数据驱除策略
目前redis的数据驱除策略有以下几种:
- noeviction:不驱除,新增数据会失败,无法再新增缓存。
- allkeys-lru: LRU方式驱除,将最久未访问的key清掉、释放空间。
- volatile-lru: LRU方式驱除,不同之处在于:只从已经过期的key列表中驱除最久未访问的key。
- allkeys-random: 随机驱除。
- volatile-random: 从过期的key列表中,随机驱除。
- volatile-ttl: 不立马驱除key,只是给key一个更小的ttl(延迟删除)。
备注:对于volatile-lru, volatile-random, volatile-ttl如果没有找到符合条件的key,就不会驱除。
需要根据业务访问场景选择合适的驱除策略,值得一提的是,我们可以在redis运行期间动态修改驱除策略。
不同策略选择原则大致为:
- allkeys-lru: 请求符合幂律分布,最近访问的元素有大概率被再次访问的场景。
- allkeys-random: 所有元素访问频率是相同的场景。
- volatile-ttl: 缓存内容均拥有不同的ttl的场景。
2.3 小结
通过为redis设置最大容量和数据LRU驱除策略,就可以很方便的实现一个LRU系统。
3. redis LRU算法简介
redis的LRU算法并不是完全意义上的LRU,它只是一个近似实现,这再一次的体现了工程架构设计中的折中思想。
3.1 redis近似LRU算法
由于LRU算法需要保存每一个元素的访问时间,如果元素数量特别多、频繁驱除的情况下,是非常消耗计算和内存的。为了成本和效果之间的折中,redis实现一个近似的LRU算法。
redis为对key集合进随机抽样,只对抽样集合中的key做LRU算法。样本集合小的情况下,计算和内存会极大降低,但是也会损失一点精度。可以通过这个配置来控制精度:
1 | # 每次驱除抽样的key数目,越大LRU越精准 |
3.2 redis更高级的LFU算法
当maxmemory-samples=10
之后就接近真实LRU的效果了,无法继续提升。为了进一步提升命中效果,redis的大佬们又想到了一种办法:LFU(最近不频繁访问的驱除算法,在redis 4.0版本)
LRU vs LFU:
- LRU: 只是针对访问时间,对于最近刚刚访问的冷门数据来说,是需要一段时间才能被驱除掉。
- LFU: 实时记录每个key的访问次数,当触发驱除时,只驱除访问频度最低的那些key。
LFU同样提供了两种数据过期策略:volatile-lfu 和 allkeys-lfu,分别是过期集合和全集中选择目标。
除此之外为了控制访问频率计数器的增长和衰弱速度,redis又引入了两个配置:
1 | # 增长速度:越大越慢 |
LFU思想还是非常值得借鉴的,后面有机会再深入了解一下LFU算法。
4. 总结
redis非常强大,在很多场景中均有不俗的表现,今天主要介绍了LRU cache场景。
redis的配置参数也非常的多,给予了用户非常大的灵活性,如果redis集群很大的情况下,值得折腾一下各个配置,整个玄学调参。