redis的LRU算法(二)
前文再续,书接上一回。上次讲到redis的LRU算法,文章实在精妙,最近可能有机会用到其中的技巧,顺便将下半部翻译出来,实现的时候参考下。
搏击俱乐部的第一法则:用裸眼观测你的算法
Redis2.8的LRU实现已经上线了,在不同的负载环境下经过测试,用户没有抱怨Redis的清理机制。为了继续改进,我希望能观察到算法的性能,同时不会浪费大量CPU,不增加1比特空间占用。
我设计了一个测试用例。导入指定数量的key,然后顺序访问他们,好让他们的最近访问时间顺序递减。再添加50%的key,那么之前的key有50%就会被淘汰掉。理想情况下,被淘汰的应该是前50%的。如下图:
绿色的是新添加的key,灰色的是第一次添加的key,白色表示被移除的。
LRU v2:不要丢掉重要信息
当采用了N-key取样时,默认会建立16个key的池,将里面的key按空闲时间排序。新key只会在池不满或者空闲时间大于池里最小的,才能进池。
这个实现极大的提升了性能,实现又简单,没有大bug。只要一点点性能监控和一些memmove()就完成了。
同时,新的redis-cli模式(-lru-test)支持测试LRU精度,可以更接近真实的负载来看新算法的工况,尝试不同算法的时候,至少可以发现明显的速度退化。
最近最少访问(LFU)
我最近又部分重构了Redis cache的换页算法。这些工作源于一个issue:当你在Redis 3.2有多个数据库的时候,算法总是做局部选择。比如DB 0的所有key都用的很频繁,DB 1的所有key都用的很少。Redis会从每个DB里丢弃一个key。理性的选择应该是先丢弃DB 1的key,丢完以后再丢DB 0的
Redis用作cache的时候,通常不会跟不同DB混合用,但我还是开始着手改进,最后将db的id包括在池里,然后所有DB都共用一个池,这个实现比原始先快20%
这次改进激起了我对Redis这块子系统的好奇心。我花了好些天进行优化,如果我用一个大点的池子,会好点吗?如果选择key的时候考虑了流逝的时间,效果会不会更好?
最后,我终于明白到,LRU算法会受到取样数量限制,只要数量足够,效果就很好,很难再改进。正如上图所示,每次取样10个键,已经和理论上的LRU几乎一样准确了。
因为原始算法难以改进,我开始想其他办法。回顾前文,其实我们真正想要的,是保留未来最有可能访问的key,即是最常访问的key,而不是最新访问的key。这就是LFU算法。理论上LFU的实现很简单,只要给每个key挂一个计数器,我们就可以知道给定的key是不是比另一个key访问更多了。
当然,LFU的实现上有几个通用的难点:
1. LFU里没法使用链表法转移到头部的技巧了。因为完美LFU需要key严格按访问量排序。当访问量一致时,排序算法可能劣化为O(N),即使计数器只变了一点点
2. LFU没法简单的只在访问时对计数器加一。因为访问模式会随着时间发生变化,所以一个高分的key需要随着时间流逝而分数递减。
在Redis里第一个问题不是问题,我们可以沿用LRU的随机取样方法。第二个问题仍然存在,我们需要一个方法来递减分数,或者随着时间流逝将计数器折半。
24bit空间实现的LFU
在Redis里,我们可以用的就是LRU的24bit空间,需要一些奇技淫巧来实现。
在24bit空间里,需要塞下:
1. 某种类型的访问计数器
2. 足够的信息来决定何时折半计数器
我的解决方案如下:
bits bits
+----------------+--------+
+ Last decr time | LOG_C |
+----------------+--------+
8bit用来计数,16bit用来记录上次递减的时间
你可能会认为,8bit计数器很快就会溢出了吧?这就是技巧所在:我用的是对数计数器。具体代码如下:
uint8_t LFULogIncr(uint8_t counter) {
if (counter == ) return ;
double r = (double)rand()/RAND_MAX;
double baseval = counter - LFU_INIT_VAL;
if (baseval < ) baseval = ;
double p = 1.0/(baseval*server.lfu_log_factor+);
if (r < p) counter++;
return counter;
}
计数器的值越大,真正加一的概率越小。上述代码算出一个概率p,介乎0到1之间,计数器越大,p越小。然后生成0-1之间的随机数r,只有r<p的时候,计数器才会加一。
现在我们来看看计数器折半的问题。转成分钟为单位的unix时间,低16位会存在上面保留的16位空间内。当Redis进行随机取样,扫描key空间的时候,所有遇到的key都会被检查是否应该递减。如果上次递减实在N分钟之前(N是可配置的),并且计数器的值是高分值,那计数器就会被折半。如果计数器是低分值,则只会递减。(希望我们可以更好的分辨少访问量的key,因为我们的计数器精度比较低)
还有一个问题,新的key需要一个生存的机会。Redis里新key会从5分开始。上面的递减算法已经考虑到这个分数,如果key分数低于5分,更容易被丢弃(一般是长时间没访问的非活跃key)。
redis的LRU算法(二)的更多相关文章
- redis的LRU算法(一)
最近加班比较累,完全不想写作了.. 刚看到一篇有趣的文章,是redis的作者antirez对redis的LRU算法的回顾.LRU算法是Least Recently Used的意思,将最近最少使用的资源 ...
- Redis的LRU算法
Redis的LRU算法 LRU算法背后的的思想在计算机科学中无处不在,它与程序的"局部性原理"很相似.在生产环境中,虽然有Redis内存使用告警,但是了解一下Redis的缓存使用策 ...
- 【Redis 设置Redis使用LRU算法】
转自:http://ifeve.com/redis-lru/ 本文将介绍Redis在生产环境中使用的Redis的LRU策略,以及自己动手实现的LRU算法(php) 1.设置Redis使用LRU算法 L ...
- Redis内存回收:LRU算法
Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read Redis中采用两种算法进行内存回收,引用计数算法以及LRU ...
- Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?
在<Redis 数据缓存满了怎么办?>我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据. 淘汰策略如下所示: 设置过期时间的 key volatile-ttl.vo ...
- Redis - 作为 LRU 缓存
一.简介 LRU 实际上是被唯一支持的数据移除方法,同时也是 memcached 默认支持的缓存算法. 二.配置内存大小 在 redis.conf 文件中使用 maxmemory 指令能够配置内存大小 ...
- Redis学习笔记2-使用 Redis 作为 LRU 缓存
当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...
- Redis作为lru缓存作用
当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...
- 用redis当作LRU缓存
原文地址:https://redis.io/topics/lru-cache Redis可以用来作缓存,他可以很方便的淘汰(删除)旧数据添加新数据,类似memcached.LRU只是其中的一种置换算法 ...
随机推荐
- C#UDP广域网,局域网通信-原理分析
一.UDP局域网通信. 这个比较简单,关于局域网中的2台或者更多的计算机之间的UDP通信,网络上一大把,直接复制粘贴就可以使用,原理也非常简单.所以,本文不做详细介绍. 二.UDP广域通信(包括路由器 ...
- 本地代码上传到git
1.(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库 git init 2.把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点 ...
- Java内存溢出优化性能优化
高性能应用构成了现代网络的支柱.LinkedIn有许多内部高吞吐量服务来满足每秒数千次的用户请求.要优化用户体验,低延迟地响应这些请求非常重要. 比如说,用户经常用到的一个功能是了解动态信息——不断更 ...
- React 的坑
MemoryRouter 会缓存组件,导致有时候 componentDidMount 不会执行
- [CodeChef-CAPTCITI]Snakes capturing the Mongoose Cities
Problem 每个点都可以选择降落士兵,然后当一个点的子节点被攻占的数量超过读入中的限制后,这个城市也被占领. 每个点降落士兵都有一定的代价,问把这一个图全部攻占的最小代价. Solution 这显 ...
- IoT experitment
Abstract: In order to solve the problems of complex experiment management, complicated teaching task ...
- my goal
I want to get a good command of a programming language at the university, and I have to pass the tes ...
- 如何用ESP8266/8285做一个WIFI广告机(虚拟WiFi)
准备工作 准备一个深圳四博智联科技有限公司的ESP-F 模组.或者四博智联科技的NODEMCU 当我们拿到ESP-F模块后,可以按照以下接线进行测试: 即 VCC.EN 接 3.3v.GPIO15 G ...
- Mysql 存储过程查询结果赋值到变量的方法
drop table if exists test_tbl; create table test_tbl (name varchar(20), status int(2)); insert into ...
- error: `cout' was not declared in this scope
原因:C++ 1998 要求cout and endl被调用使用'std::cout'和'std::endl'格式,或using namespace std; 修改后:#include<iost ...