Redis 对过期数据的处理
Redis 对过期数据的处理
在 redis 中,对于已经过期的数据,Redis 采用两种策略来处理这些数据,分别是惰性删除和定期删除
惰性删除
惰性删除不会去主动删除数据,而是在访问数据的时候,再检查当前键值是否过期,如果过期则执行删除并返回 null 给客户端,如果没有过期则返回正常信息给客户端。
它的优点是简单,不需要对过期的数据做额外的处理,只有在每次访问的时候才会检查键值是否过期,缺点是删除过期键不及时,造成了一定的空间浪费。
源码
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
robj *val;
if (expireIfNeeded(db,key) == 1) {
/* Key expired. If we are in the context of a master, expireIfNeeded()
* returns 0 only when the key does not exist at all, so it's safe
* to return NULL ASAP. */
if (server.masterhost == NULL) {
server.stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
}
/* However if we are in the context of a slave, expireIfNeeded() will
* not really try to expire the key, it only returns information
* about the "logical" status of the key: key expiring is up to the
* master in order to have a consistent view of master's data set.
*
* However, if the command caller is not the master, and as additional
* safety measure, the command invoked is a read-only command, we can
* safely return NULL here, and provide a more consistent behavior
* to clients accessign expired values in a read-only fashion, that
* will say the key as non existing.
*
* Notably this covers GETs when slaves are used to scale reads. */
if (server.current_client &&
server.current_client != server.master &&
server.current_client->cmd &&
server.current_client->cmd->flags & CMD_READONLY)
{
server.stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
return NULL;
}
}
val = lookupKey(db,key,flags);
if (val == NULL) {
server.stat_keyspace_misses++;
notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
}
else
server.stat_keyspace_hits++;
return val;
}
定期删除
定期删除:Redis会周期性的随机测试一批设置了过期时间的key并进行处理。测试到的已过期的key将被删除。
具体的算法如下:
Redis配置项hz定义了serverCron任务的执行周期,默认为10,代表了每秒执行10次;
每次过期key清理的时间不超过CPU时间的25%,比如hz默认为10,则一次清理时间最大为25ms;
清理时依次遍历所有的db;
从db中随机取20个key,判断是否过期,若过期,则逐出;
若有5个以上key过期,则重复步骤4,否则遍历下一个db;
在清理过程中,若达到了25%CPU时间,退出清理过程;
虽然redis的确是不断的删除一些过期数据,但是很多没有设置过期时间的数据也会越来越多,那么redis内存不够用的时候是怎么处理的呢?这里我们就会谈到淘汰策略
Redis内存淘汰策略
当redis的内存超过最大允许的内存之后,Redis会触发内存淘汰策略,删除一些不常用的数据,以保证redis服务器的正常运行
在redis 4.0以前,redis的内存淘汰策略有以下6种
- noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
- allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
- volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
- allkeys-random:加入键的时候如果过限,从所有key随机删除
- volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
- volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
在redis 4.0以后,又增加了以下两种 - volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
- allkeys-lfu:从所有键中驱逐使用频率最少的键
内存淘汰策略可以通过配置文件来修改,redis.conf对应的配置项是maxmemory-policy 修改对应的值就行,默认是noeviction
LRU(the least recently used 最近最少使用)算法
如果一个数据在最近没有被访问到,那么在未来被访问的可能性也很小,因此当空间满的时候,最久没有被访问的数据最先被置换(淘汰)
LRU算法通常通过双向链表来实现,添加元素的时候,直接插入表头,访问元素的时候,先判断元素是否在链表中存在,如果存在就把该元素移动至表头,所以链表的元素排列顺序就是元素最近被访问的顺序,当内存达到设置阈值时,LRU队尾的元素由于被访问的时间线较远,会优先踢出
但是在redis中,并没有严格实行LRU算法,之所以这样是因为LRU需要消耗大量的额外内存,需要对现有的数据结构进行较大的改造,近似LRU算法采用在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和LRU算法非常近似的效果。Redis的 LRU算法给每个key增加了一个额外的长度为24bit的小字段,记录最后一次被访问的时间戳。
redis通过maxmemory-samples 5配置,对key进行采样淘汰。同时在Redis3.0以后添加了淘汰池进一步提升了淘汰准确度。
但是LRU算法是存在一定的问题
例如,这表示随着时间的推移,四个不同的键访问。每个“〜”字符为一秒钟,而“ |” 最后一行是当前时刻。
~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B ~~ B〜|
~~~~~~~~~~ C ~~~~~~~~ C ~~~~~~~~~ C ~~~~~~ |
~~~~~ D ~~~~~~~~~ D ~~~~~~~ D ~~~~~~~~ D |
在上图中,按照LRU机制删除的话删除的顺序应该是C->A->B->D 其实这并不是我们想要的,因为B被访问的频率是最高的,而D被访问的频率比较低,所以我们更想让B保留,把D删除,所以我们接下来看另一种策略 LFU
**LFU(leastFrequently used 最不经常使用)**
如果一个数据在最近一段时间内很少被访问到,那么可以认为在将来他被访问到的概率也很小。所以,当空间满时,最小频率访问的数据最先被淘汰
Redis使用redisObject中的24bit lru字段来存储lfu字段, 这24bit被分为两部分:
1:高16位用来记录访问时间(单位为分钟)
2:低8位用来记录访问频率,简称counter
16 bits 8 bits
+----------------+--------+
Last decr time | LOG_C |
但是counter 8bit很容易就溢出了,技巧是用一个逻辑计数器,给予概率的对数计数器,而不是一个普通的递增计数器
```
uint8_t LFULogIncr(uint8_t counter) {
if (counter == 255) return 255;
double r = (double)rand()/RAND_MAX;
double baseval = counter - LFU_INIT_VAL;
if (baseval < 0) baseval = 0;
double p = 1.0/(baseval*server.lfu_log_factor+1);
if (r < p) counter++;
return counter;
}
```
对应的概率分布计算公式为
```
1.0/((counter - LFU_INIT_VAL)*server.lfu_log_factor+1);
```
其中LFU_INIT_VAL为5,其实简单说就是,越大的数,递增的概率越低
严格按照LFU算法,时间越久的key,counter越有可能越大,被剔除的可能性就越小。counter只增长不衰减就无法区分热点key。为了解决这个问题,redis提供了衰减因子server.lfu_decay_time,其单位为分钟,计算方法也很简单,如果一个key长时间没有访问那么他的计数器counter就要减少,减少的值由衰减因子来控制
> 关注我的技术公众号,每周都有优质技术文章推送。
微信扫一扫下方二维码即可关注:
![file](https://img2020.cnblogs.com/other/1094508/202010/1094508-20201018224534910-39047834.png)
Redis 对过期数据的处理的更多相关文章
- Redis系列-冷知识
下面是一些看了,但觉得用处不大,不记下又可惜的东西. Redis删除过期数据 redis通过expire/expireat(秒为单位)或者pexpire/pexpireat(毫秒为单位)来设置key的 ...
- 高可用Redis(八):Redis主从复制
1.Redis复制的原理和优化 1.1 Redis单机的问题 1.1.1 机器故障 在一台服务器上部署一个Redis节点,如果机器发生主板损坏,硬盘损坏等问题,不能在短时间修复完成,就不能处理Redi ...
- [Redis] 万字长文带你总结Redis,助你面试升级打怪
文章目录 Redis的介绍.优缺点.使用场景 Linux中的安装 常用命令 Redis各个数据类型及其使用场景 Redis字符串(String) Redis哈希(Hash) Redis列表(List) ...
- 【Redis】入门
Redis概述 Redis常用数据结构 Redis删除过期数据策略 Redis内存淘汰机制 Redis持久化机制 缓存问题及解决方案 Redis概述 Redis是一个开源的.基于内存的数据结构存储器 ...
- Redis详解(十一)------ 过期删除策略和内存淘汰策略
在介绍这篇文章之前,我们先来看如下几个问题: ①.如何设置Redis键的过期时间? ②.设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗? ③.如何设置R ...
- 深度评测丨 GaussDB(for Redis) 大 Key 操作的影响
本文分享自华为云社区<墨天轮评测:GaussDB(for Redis)大Key操作的影响>,作者: 高斯 Redis 官方博客. 在前一篇文章<墨天轮评测:GaussDB(for R ...
- Redis为什么变慢了?透彻解读如何排查Redis性能问题
Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右.但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符. 你也 ...
- 面渣逆袭:Redis连环五十二问,图文详解,这下面试稳了!
大家好,我是老三,面渣逆袭系列继续,这节我们来搞定Redis--不会有人假期玩去了吧?不会吧? 基础 1.说说什么是Redis? Redis是一种基于键值对(key-value)的NoSQL数据库. ...
- Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?
在<Redis 数据缓存满了怎么办?>我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据. 淘汰策略如下所示: 设置过期时间的 key volatile-ttl.vo ...
随机推荐
- Java——集合框架之ArrayList,LinkedList,迭代器Iterator
概述--集合框架 Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类).所有抽象出来的数据结构和操作(算法)统称为Java集合框架(Java Collection ...
- mysql在8.0版本下修改密码的命令
ubuntu20.04上边部署了一个新的mysql服务,默认没密码,想要修改密码. 使用如下手段 第一种:ALTER USER 'root'@'localhost' IDENTIFIED BY '密码 ...
- hadoop(集群)完全分布式环境搭建
一,环境 主节点一台: ubuntu desktop 16.04 zhoujun 172.16.12.1 从节点(slave)两台:ubuntu server 16.04 hadoop2 ...
- 3. Linux常用系统状态检测命令
1.ifconfig :于获取网卡配置与网络状态等信息,如网卡名称.IP.MAC等 2.uname -a :完整地查看当前系统的内核名称.主机名.内核发行版本.节点名.系统时间.硬件名称.硬件平台.处 ...
- Deep Learning论文翻译(Nature Deep Review)
原论文出处:https://www.nature.com/articles/nature14539 by Yann LeCun, Yoshua Bengio & Geoffrey Hinton ...
- 【noi 2.5_7834】分成互质组(dfs)
有2种dfs的方法: 1.存下每个组的各个数和其质因数,每次对于新的一个数,与各组比对是否互质,再添加或不添加入该组. 2.不存质因数了,直接用gcd,更加快.P.S.然而我不知道为什么RE,若有好心 ...
- 牛客小白月赛28 J.树上行走 (并查集,dfs)
题意:有\(n\)个点,\(n-1\)条边,每个点的类型是\(0\)或\(1\),现在让你选一个点,然后所有与该点类型不同的点直接消失,问选哪些点之后,该点所在的联通块最大. 题解: 因为选完之后两个 ...
- 洛谷P2241-统计方形-矩形内计算长方形和正方形的数量
洛谷P2241-统计方形 题目描述: 有一个 \(n \times m\) 方格的棋盘,求其方格包含多少正方形.长方形(不包含正方形). 思路: 所有方形的个数=正方形的个数+长方形的个数.对于任意一 ...
- Playbook 角色(Roles) 和 Include 语句
简介 当我们刚开始学习运用 playbook 时,可能会把 playbook 写成一个很大的文件,到后来可能你会希望这些文件是可以方便去重用的,所以需要重新去组织这些文件. Include 语句 基本 ...
- ArcGIS Mobile 自定义图层在绘制面时节点未绘制完全的问题
ArcGIS Mobile 自定义图层在绘制面时节点未绘制完全,如下图: 面的绘制代码如下: public void Draw(Display dis, Pen p1, Pen p2,Pen p3 , ...