《玩转Redis》系列文章主要讲述Redis的基础及中高级应用。本文是《玩转Redis》系列第【14】篇,最新系列文章请前往公众号“zxiaofan”(点我点我)查看,或百度搜索“玩转Redis zxiaofan”(点我点我)即可。
本文关键字:玩转Redis、Redis数据淘汰策略、8种数据淘汰策略、Redis缓存满了怎么办、Redis近似LRU算法、Redis的LFU算法;
往期精选:《玩转Redis-生产环境如何导入、导出及删除大量数据》
大纲
- 为什么Redis需要数据淘汰机制?
- Redis的8种数据淘汰策略
- Redis的近似LRU算法
- LRU算法原理
- 近似LRU算法原理(approximated LRU algorithm)
- Redis的LFU算法
- LFU与LRU的区别
- LFU算法原理
- 小知识
- 为什么Redis要使用自己的时钟?
- 如何发现热点key?
1、为什么Redis需要数据淘汰机制?
众所周知,Redis作为知名内存型NOSQL,极大提升了程序访问数据的性能,高性能互联网应用里,几乎都能看到Redis的身影。为了提升系统性能,Redis也从单机版、主从版发展到集群版、读写分离集群版等等,业界也有诸多著名三方扩展库(如Codis、Twemproxy)。
阿里云的企业版Redis(Tair)的性能增强型集群版更是“[豪]无人性”,内存容量高达4096 GB 内存,支持约61440000 QPS。Tair混合存储版更是使用内存和磁盘同时存储数据的集群版Redis实例,最高规格为1024 GB内存8192 GB磁盘(16节点)。【援引:https://help.aliyun.com/document_detail/26350.html】
既然Redis这么牛,那我们就使劲把数据往里面存储吗?
32G DDR4 内存条大约 900 元,1TB 的 SSD 硬盘大约 1000 元,价格实在悬殊。此外,即使数据量很大,但常用数据其实相对较少,全放内存性价比太低。“二八原则”在这里也是适用的。
既然内存空间有限,为避免内存写满,就肯定需要进行内存数据淘汰了。
- 性价比;
- 内存空间有限;
2、Redis的8种数据淘汰策略
redis.conf中可配置Redis的最大内存量 maxmemory,如果配置为0,在64位系统下则表示无最大内存限制,在32位系统下则表示最大内存限制为 3 GB。当实际使用内存 mem_used 达到设置的阀值 maxmemory 后,Redis将按照预设的淘汰策略进行数据淘汰。
# redis.conf 最大内存配置示例,公众号 zxiaofan
# 不带单位则 单位是 字节<bytes>
maxmemory 1048576
maxmemory 1048576B
maxmemory 1000KB
maxmemory 100MB
maxmemory 1GB
maxmemory 1000K
maxmemory 100M
maxmemory 1G
除了在配置文件中修改配置,也可以使用 config 命令动态修改maxmemory。
# redis maxmemory 动态设置及查看命令示例,公众号 zxiaofan
# 动态修改 maxmemory
config set maxmemory 10GB
# 查看 maxmemory
config get maxmemory
info memory | grep maxmemory
redis-cli -h 127.0.01 -p 6379 config get maxmemory
接下来我们讲讲8种数据淘汰策略,Redis 4.0开始,共有8种数据淘汰机制。
淘汰策略名称 | 策略含义 |
---|---|
noeviction | 默认策略,不淘汰数据;大部分写命令都将返回错误(DEL等少数除外) |
allkeys-lru | 从所有数据中根据 LRU 算法挑选数据淘汰 |
volatile-lru | 从设置了过期时间的数据中根据 LRU 算法挑选数据淘汰 |
allkeys-random | 从所有数据中随机挑选数据淘汰 |
volatile-random | 从设置了过期时间的数据中随机挑选数据淘汰 |
volatile-ttl | 从设置了过期时间的数据中,挑选越早过期的数据进行删除 |
allkeys-lfu | 从所有数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用) |
volatile-lfu | 从设置了过期时间的数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用) |
// redis.conf,Redis 6.0.6版本
// 默认策略 是 noeviction,在生产环境建议修改。
# The default is:
#
# maxmemory-policy noeviction
// 在线设置数据淘汰策略 maxmemory-policy
config set maxmemory-policy volatile-lfu
noeviction 涉及的返回错误的写命令包含:
set,setnx,setex,append,incr,decr,rpush,lpush,rpushx,lpushx,linsert,lset,rpoplpush,sadd,sinter,sinterstore,sunion,sunionstore,sdiff,sdiffstore,zadd,zincrby,zunionstore,zinterstore,hset,hsetnx,hmset,hincrby,incrby,decrby,getset,mset,msetnx,exec,sort。
我们可以看到,除 noeviction 比较特殊外,allkeys 开头的将从所有数据中进行淘汰,volatile 开头的将从设置了过期时间的数据中进行淘汰。淘汰算法又核心分为 lru、random、ttl、lfu 几种。
让我们用一张图来概括:
3、Redis的近似LRU算法
在了解Redis近似LRU算法前,我们先来了解下原生的LRU算法。
3.1、LRU算法
LRU(Least Recently Used)最近最少使用。优先淘汰最近未被使用的数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
LRU底层结构是 hash 表 + 双向链表。hash 表用于保证查询操作的时间复杂度是O(1),双向链表用于保证节点插入、节点删除的时间复杂度是O(1)。
为什么是 双向链表而不是单链表呢?单链表可以实现头部插入新节点、尾部删除旧节点的时间复杂度都是O(1),但是对于中间节点时间复杂度是O(n),因为对于中间节点c,我们需要将该节点c移动到头部,此时只知道他的下一个节点,要知道其上一个节点需要遍历整个链表,时间复杂度为O(n)。
LRU GET操作:如果节点存在,则将该节点移动到链表头部,并返回节点值;
LRU PUT操作:①节点不存在,则新增节点,并将该节点放到链表头部;②节点存在,则更新节点,并将该节点放到链表头部。
LRU算法源码可参考Leetcode:https://www.programcreek.com/2013/03/leetcode-lru-cache-java/ 。
# LRU 算法 底层结构 伪代码,公众号 zxiaofan
class Node{
int key;
int value;
Node prev