[转帖]Redis 内存淘汰策略 (史上最全)
1.前言
Redis内存淘汰策略,是被很多小伙伴忽略的知识盲区,注意,是盲区。
注意,Redis如果内存淘汰策略配置不合理,可能会导致Redis无法服务。
所以,使用此文,对Redis内存淘汰策略专门进行介绍。
此文内容,将收入电子书《Java面试红宝书》的0.2版本中。看完此书,您一定会Java内力大增。
首先,介绍一下Redis过期删除策略,然后,再介绍Redis淘汰策略.
1 Redis过期删除策略
Redis对于过期的key,有两种删除策略:
- 定期删除
- 惰性删除
定期删除
redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
Redis 默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。
1.从过期字典中随机 20 个 key;
2.删除这 20 个 key 中已经过期的 key;
3.如果过期的 key 比率超过 1/4,那就重复步骤 1;
redis默认是每隔 100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载。
惰性删除
所谓惰性策略就是在客户端访问这个key的时候,redis对key的过期时间进行检查,如果过期了就立即删除,不会给你返回任何东西。
为啥需要两种删除策略呢?
定期删除可能会导致很多过期key到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,即当你主动去查过期的key时,如果发现key过期了,就立即进行删除,不返回任何东西.
总结:定期删除是集中处理,惰性删除是零散处理。
2.配置Redis内存
Redis是基于内存key-value键值对的内存数据库,我们安装完数据库之后,内存往往会受到系统内存大小的限制,我们也可以配置redis能使用的最大的内存大小.
两种方式配置redis的内存:
通过配置文件修改
通过客户端修改
(1)通过配置文件修改
我们可以通过配置文件修改,安装完redis之后我们可以在redis根目录中找到redis.conf文件,在配置文件中添加一下参数就可以设置redis的内存大小了
# 设置 Redis 最大使用内存大小为100M
maxmemory 100mb //指定最大内存为100mb
上面的配置设置了,当 Redis 使用的内存超过 100Mb 时,就开始对数据进行淘汰。
下面是更加详细的例子:
#配置文件
maxmemory <bytes>
下面的写法均合法:
maxmemory 1024000
maxmemory 1GB
maxmemory 1G
maxmemory 1024KB
maxmemory 1024K
maxmemory 1024MB
...
(2)通过客户端修改
在服务器上输入redis-cli之后进入redis客户端,通过命令动态修改redis内存大小
//设置Redis最大占用内存大小为100M
127.0.0.1:6379> config set maxmemory 100mb
//获取设置的Redis能使用的最大内存大小
127.0.0.1:6379> config get maxmemory
如果不设置,或者设置最大内存大小为0,在64位操作系统下,Redis不限制内存大小,在32位操作系统,Redis最多使用3GB内存
下面是更加详细的例子:
#命令行
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> config set maxmemory 1GB
OK
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "1073741824"
3.redis内存淘汰策略
我们设置完redis内存之后,我们就像里面放数据,但是内存总有满的时候,满的时候redis又是怎么处理的呢?
每进行一次redis操作的时候,redis都会检测可用内存,判断是否要进行内存淘汰,当超过可用内存的时候,redids 就会使用对应淘汰策略。
redis内存淘汰策略,具体如下:
1.no-envicition:
该策略对于写请求不再提供服务,会直接返回错误,当然排除del等特殊操作,redis默认是no-envicition策略。
2.allkeys-random:
从redis中随机选取key进行淘汰
3.allkeys-lru:
使用LRU(Least Recently Used,最近最少使用)算法,从redis中选取使用最少的key进行淘汰
4.volatile-random:
从redis中设置过过期时间的key,进行随机淘汰
5.volatile-ttl:
从redis中选取即将过期的key,进行淘汰
6.volatile-lru:
使用LRU(Least Recently Used,最近最少使用)算法,从redis中设置过过期时间的key中,选取最少使用的进行淘汰
7.volatile-lfu:
使用LFU(Least Frequently Used,最不经常使用),从设置了过期时间的键中选择某段时间之内使用频次最小的键值对清除掉
8.allkeys-lfu:
使用LFU(Least Frequently Used,最不经常使用),从所有的键中选择某段时间之内使用频次最少的键值对清除
如何设置淘汰策略呢?
我们自己可以命令查看当前redis使用的淘汰策略
config get maxmemory-policy //获取当前内存淘汰策略
config set maxmemory-policy valatile-lru //通过命令修改淘汰策略
4.Redis 内存淘汰算法
而 Redis 的淘汰算法有多种,如下:
- 随机
- TTL
- LRU(Least Recently Used,最近最少使用)
- LFU(Least Frequently Used,最不经常使用)
随机算法很好理解,就是从数据库中随机淘汰一些 Keys。而 TTL 算法就是从设置了过期时间的 Keys 中获取最早过期的 一批 Keys,然后淘汰这些 Keys。而 LRU 和 LFU 这两种算法在名字上比较容易混淆,所以这里介绍一下这两种算法的区别。
在缓存的内存淘汰策略中有FIFO、LRU、LFU三种,其中LRU和LFU是Redis在使用的。FIFO是最简单的淘汰策略,遵循着先进先出的原则,这里简单提一下:

4.1. LRU算法
LRU 主要是通过 Key 的最后访问时间来判定哪些 Key 更适合被淘汰,如下图所示:

如上图所示,所有的 Keys 都根据最后被访问的时间来进行排序的,所以在淘汰时只需要按照所有 Keys 的最后被访问时间,由小到大来进行即可

LRU算法的特点:
新增key value的时候首先在链表结尾添加Node节点,如果超过LRU设置的阈值就淘汰队头的节点并删除掉HashMap中对应的节点。
修改key对应的值的时候先修改对应的Node中的值,然后把Node节点移动队尾。
访问key对应的值的时候把访问的Node节点移动到队尾即可。
Redis的LRU实现
Redis维护了一个24位时钟,可以简单理解为当前系统的时间戳,每隔一定时间会更新这个时钟。每个key对象内部同样维护了一个24位的时钟,当新增key对象的时候会把系统的时钟赋值到这个内部对象时钟。比如我现在要进行LRU,那么首先拿到当前的全局时钟,然后再找到内部时钟与全局时钟距离时间最久的(差最大)进行淘汰,这里值得注意的是全局时钟只有24位,按秒为单位来表示才能存储194天,所以可能会出现key的时钟大于全局时钟的情况,如果这种情况出现那么就两个相加而不是相减来求最久的key。
struct redisServer {
pid_t pid;
char *configfile;
//全局时钟
unsigned lruclock:LRU_BITS;
...
};
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
/* key对象内部时钟 */
unsigned lru:LRU_BITS;
int refcount;
void *ptr;
} robj;
Redis中的LRU与常规的LRU实现并不相同,常规LRU会准确的淘汰掉队头的元素,但是Redis的LRU并不维护队列,只是根据配置的策略要么从所有的key中随机选择N个(N可以配置)要么从所有的设置了过期时间的key中选出N个键,然后再从这N个键中选出最久没有使用的一个key进行淘汰。
下图是常规LRU淘汰策略与Redis随机样本取一键淘汰策略的对比,浅灰色表示已经删除的键,深灰色表示没有被删除的键,绿色表示新加入的键,越往上表示键加入的时间越久。从图中可以看出,在redis 3中,设置样本数为10的时候能够很准确的淘汰掉最久没有使用的键,与常规LRU基本持平。

为什么要使用近似LRU?
1、性能问题,由于近似LRU算法只是最多随机采样N个key并对其进行排序,如果精准需要对所有key进行排序,这样近似LRU性能更高
2、内存占用问题,redis对内存要求很高,会尽量降低内存使用率,如果是抽样排序可以有效降低内存的占用
3、实际效果基本相等,如果请求符合长尾法则,那么真实LRU与Redis LRU之间表现基本无差异
4、在近似情况下提供可自配置的取样率来提升精准度,例如通过 CONFIG SET maxmemory-samples 指令可以设置取样数,取样数越高越精准,如果你的CPU和内存有足够,可以提高取样数看命中率来探测最佳的采样比例。
4.2. LFU算法
LFU(Least Frequently Used)表示最不经常使用,它是根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。
LFU算法反映了一个key的热度情况,不会因LRU算法的偶尔一次被访问被误认为是热点数据。
LFU算法的常见实现方式为链表:
新数据放在链表尾部 ,链表中的数据按照被访问次数降序排列,访问次数相同的按最近访问时间降序排列,链表满的时候从链表尾部移出数据。
[转帖]Redis 内存淘汰策略 (史上最全)的更多相关文章
- Redis内存淘汰策略
目录 一.内存淘汰策略重要性 二.Key值过期策略 三.内存淘汰策略 三.Redis内存淘汰策略配置 一.内存淘汰策略重要性 我们都知道redis的性能很高,最主要的原因之一就是redis的数据都在内 ...
- LRU工程实现源码(一):Redis 内存淘汰策略
目录 内存淘汰是什么?什么时候内存淘汰 内存淘汰策略 Redis中的LRU淘汰算法 源码剖析 第一步:什么时候开始淘汰key 配置读取 检查时机 getMaxmemoryState 第二步:淘汰哪些k ...
- Redis数据结构和使用场景,redis内存淘汰策略
什么样的数据适合放入Redis? sql执行耗时特别久,且结果不频繁变动的数据,适合放入Redis. Redis是单线程的,为什么会这么快? 纯内存操作 单线程操作,避免频繁的上下文切换 采用了非阻塞 ...
- Redis详解(十一)------ 过期删除策略和内存淘汰策略
在介绍这篇文章之前,我们先来看如下几个问题: ①.如何设置Redis键的过期时间? ②.设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗? ③.如何设置R ...
- 面试官:Redis 过期删除策略和内存淘汰策略有什么区别?
作者:小林coding 计算机八股文网站:https://xiaolincoding.com 大家好,我是小林. Redis 的「内存淘汰策略」和「过期删除策略」,很多小伙伴容易混淆,这两个机制虽然都 ...
- Redis系列11:内存淘汰策略
Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...
- JVM 内存溢出 实战 (史上最全)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- spring + spring mvc + tomcat 面试题(史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- Zab协议 (史上最全)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- 消息队列面试题、RabbitMQ面试题、Kafka面试题、RocketMQ面试题 (史上最全、持续更新、吐血推荐)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
随机推荐
- Pikachu漏洞靶场 Unsafe Fileupload(文件上传)
Unsafe Fileupload 文章目录 Unsafe Fileupload 1.client check 2.MIME type 3.getimagesize 1.client check 标题 ...
- 【csharp】抽象类与接口有哪些不同?什么时候应该使用抽象类?
抽象类与接口有哪些不同? 抽象类和接口是在面向对象编程中两个不同的概念,它们有一些重要的区别.以下是抽象类和接口的主要不同点: 抽象类(Abstract Class): 成员类型: 抽象类可以包含抽象 ...
- Go 自动补全gocode
go语言自动补全代码,需要添加gocode的程序. 执行: go get github.com/nsf/gocode 一般来说,gocode的源码会在$GOPATH/src/github.com/ns ...
- vue上传文件显示进度条,当上传完成后间隔一秒进度条消失
<template> <el-upload class="avatar-uploader" action="api/file/upload" ...
- LeetCode 回溯篇(46、77、78、51)
46. 全排列 给定一个 没有重复 数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1, ...
- LeetCode 分治篇(50、17)
50. Pow(x, n) 实现 pow(x, n) ,即计算 x 的 n 次幂函数. 示例 1: 输入: 2.00000, 10 输出: 1024.00000 示例 2: 输入: 2.10000, ...
- 13个VSCode使用技巧,开启高效的开发模式
摘要:VsCode是一款开源的编辑器,拥有强大的功能,.由于拥有各种各样的插件,这就使得VsCode可以做到的事情更多了.在使用的过程中,也是有很多技巧的,掌握一些技巧对于后期写代码也会轻松很多. 本 ...
- 2023开发者必备iOS开发工具
2023开发者必备iOS开发工具 工欲善其事,必先利其器.进行开发工作时,利用并熟练使用恰当的工具可以让工作效率得到大幅度提高.下边会介绍一些在进行iOS开发工作时常用的一些工具,本文并不对其进行 ...
- Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
Unity3D 导出的apk进行混淆加固.保护与优化原理(防止反编译) 目录 前言: 准备资料: 正文: 1:打包一个带有签名的apk 2:对包进行反编译 3:使用ipaguard来对程序进行加固 ...
- 字节跳动基于火山引擎DataLeap的一站式数据治理架构实践
更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在7月22日举行的 ArchSummit 全球架构师峰会(深圳站)上,来自火山引擎DataLeap的技术专家为大家 ...