Redis 笔记整理:回收策略与 LRU 算法
Redis的回收策略
- noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
- allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
- volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
- allkeys-random: 回收随机的键使得新添加的数据有空间存放。
- volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
- volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
注意:
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction差不多了。
回收策略选择
选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过RedisINFO命令输出以便调优。
一般的经验规则:
- 使用allkeys-lru策略:当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。如果你不确定选择什么,这是个很好的选择。.
- 使用allkeys-random:如果你是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)。
- 使用volatile-ttl:如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期。
注意:
allkeys-lru 和 volatile-random策略对于当你想要单一的实例实现缓存及持久化一些键时很有用。不过一般运行两个实例是解决这个问题的更好方法。
为了键设置过期时间也是需要消耗内存的,所以使用allkeys-lru这种策略更加高效,因为当内存有压力时,没有必要为键去设置过期时间。
回收进程如何工作
理解回收进程如何工作是非常重要的:
- 一个客户端运行了新的命令,添加了新的数据。
- Redis检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。
- 一个新的命令被执行,等等。
- 所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。
说明:
Maxmemory:
maxmemory
配置指令用于配置Redis存储数据时指定限制的内存大小。通过redis.conf可以设置该指令,或者之后使用CONFIG SET命令来进行运行时配置。
如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。
LRU算法
Redis使用的是近似的LRU算法,通过少量keys进行取样,然后回收其中一个最好的key(被访问时间较早的)。从redis3.0以后,redis改进了回收键的候选池,也就是改善了算法的性能,使得更加近似真的LRU算法。
Redis LRU有个很重要的点,你通过调整每次回收时检查的采样数量,以实现调整算法的精度。这个参数可以通过以下的配置指令调整:maxmemory-samples。
Redis为什么不使用真实的LRU实现是因为这需要太多的内存。不过近似的LRU算法对于应用而言应该是等价的。
java的LRU算法实现
LRU是Least Recently Used的缩写,即最少使用,常用于页面置换算法,为虚拟页式存储管理服务。LRU算法也经常被用作缓存淘汰策略。
方式一:
基于JDK提供的LinkedHashMap的实现。
LinkedHashMap说明:
- LinkedHasnMap内部维护的是双向链表,用来维护插入顺序或LRU顺序。
- 它继承HashMap,具有快速查询的特性。
- 头节点head是最老的节点,尾节点是最近一次被访问的节点,维护LRU顺序时,容量用完后,再执行插入新的数据时,会将头节点移除。
- accessOrder表示维护那种顺序,默认为false。true:代表LRU顺序,false:代表插入顺序。
示例
public class LRUCache<k, v> extends LinkedHashMap<k, v> {
//最多能缓存的数据
private final int CACHE_SIZE;
public LRUCache(int cacheSize) {
//true 表示让linkedHashMap按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部
super((int)Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
//当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据
return size() > CACHE_SIZE;
}
}
方式二:
原理:其实根据方式一我们可以推到出,可以使用一个双向链表来缓存数据。
详细逻辑:
- 新的数据插入到链表的头
- 每当链表中的数据被访问,就将该数据移动到链表头
- 当链表容量大于定义容量时,删除链表的尾部数据
示例
/**
* 双向链表
*/
public class Node {
private int key;
private int val;
private Node prev;
private Node next;
}
public class LRUCache {
//容量
private int capacity;
//头节点
private Node head;
//尾节点
private Node last;
private Map<Integer, Node> map;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>(capacity);
}
public int get(int key) {
Node node = map.get(key);
if (node == null) {
return -1;
}
removeHead(node);
return node.val;
}
private void removeHead(Node node) {
if (node == head) {
return;
}
if (node == last) {
last.prev.next = null;
last = last.prev;
} else {
node.prev.next = node.next;
node.next.prev = node.prev;
}
node.prev = head.prev;
node.next = head;
head.prev = node;
head = node;
}
public void put(int key, int value) {
Node node = map.get(key);
if (node == null) {
node = new Node();
node.key = key;
node.val = value;
if (map.size() == capacity) {
removeLast();
}
addHead(node);
map.put(key, node);
} else {
node.val = value;
removeHead(node);
}
}
private void addHead(Node node) {
if (map.isEmpty()) {
head = node;
last = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
private void removeLast() {
map.remove(last.key);
Node prev = last.prev;
if (prev != null) {
prev.next = null;
last = prev;
}
}
@Override
public String toString() {
return map.keySet().toString();
}
}
优点:实现逻辑简单
缺点:每次访问都需要更新链表数据
参考:redis官网lru算法
Redis 笔记整理:回收策略与 LRU 算法的更多相关文章
- Redis的内存回收策略和内存上限(阿里)
还有一篇文章 讲解guava如何删除过期数据的,与redis不同,guava没有维护线程删除过期key,只是在设置 key 或者 读取key的时候,顺带删除参考:GuavaCache简介(一)是轻量级 ...
- Redis备份及回收策略
Redis备份(持久化) Redis备份存在两种方式: 1.一种是"RDB".是快照(snapshotting),它是备份当前瞬间Redis在内存中的数据记录; 2.另一种是&qu ...
- Redis笔记整理
Redis 遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库.数据结构服务器. 特点: 1.Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时 ...
- 面试题之redis的内存回收策略
1.maxmemory-policy noeviction(默认):内存空间不足会报错 2.allkeys-lru:最少使用的数据去淘汰 3.allkeys-random:随机淘汰一些key 4.vo ...
- Redis内存回收:LRU算法
Redis技术交流群481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read Redis中采用两种算法进行内存回收,引用计数算法以及LRU ...
- Redis笔记4-持久化方案
一:快照模式 默认redis是会以快照的形式将数据持久化到磁盘的(一个二进制文件,dump.rdb,这个文件名字可以指定),在配置文件中的格式是:save N M表示在N秒之内,redis至少发生M次 ...
- 动手实现 LRU 算法,以及 Caffeine 和 Redis 中的缓存淘汰策略
我是风筝,公众号「古时的风筝」. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 那天我在 LeetCode 上刷到一道 LRU 缓存机制的问题, ...
- Redis内存回收策略
如果使用Redis的时候,不合理使用内存,把什么东西都放在内存里面,又不设置过期时间,就会导致内存的堆积越来越大.根据28法则,除了20%的热点数据之外,剩余的80%的非热点或不怎么重要的数据都在占用 ...
- redis 数据删除策略和逐出算法
数据存储和有效期 在 redis 工作流程中,过期的数据并不需要马上就要执行删除操作.因为这些删不删除只是一种状态表示,可以异步的去处理,在不忙的时候去把这些不紧急的删除操作做了,从而保证 redis ...
随机推荐
- 编译原理之非确定的自动机NFA确定化为DFA
1.设有 NFA M=( {0,1,2,3}, {a,b},f,0,{3} ),其中 f(0,a)={0,1} f(0,b)={0} f(1,b)={2} f(2,b)={3} 画出状态转换矩阵 ...
- UDP组播
多播(组播) 组播组可以是永久的也可以是临时的.组播组地址中,有一部分由官方分配的,称为永久组播组.永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化.永久组播组中成员的数量都可以是任意的 ...
- 【第二章】Zabbix3.4监控SQLServer数据库和H3C交换机思科Cisco防火墙交换机教程笔记
监控SQLServer数据库 SSMS执行相关SQL SQL模板命名规则 Zabbix客户端导入模板 添加SQLServer监控图形 SQLServer服务器关联模板 监控思科Cisco防火墙交换机 ...
- Linux 部署 FastDFS
FastDFS 安装规划: 项目 信息 Group Name group1 FastDFS安装主目录 /usr/local/fastdfs-5.0.8 FastDFS work主目录 /usr/loc ...
- 损失函数--KL散度与交叉熵
损失函数 在逻辑回归建立过程中,我们需要一个关于模型参数的可导函数,并且它能够以某种方式衡量模型的效果.这种函数称为损失函数(loss function). 损失函数越小,则模型的预测效果越优.所以我 ...
- Web安全测试学习笔记-DVWA-SQL注入-2
接上一篇SQL注入的学习笔记,上一篇我通过报错信息得知后台数据库是MySQL(这个信息非常重要~),然后通过SQL注入拿到了用户表的所有行,其实我们还可以通过MySQL的特性来拿更多的信息. 1. 获 ...
- VUE Error: listen EADDRNOTAVAIL: address not available
vue项目运行npm run dev时出现 Error: listen EADDRNOTAVAIL: address not available ... 的错误 原因:配置的IP不对 解决方法:打开c ...
- js格式化JSON数据
前言: 最近做的项目中遇到个需要在前端页面中将某个设备需要的数据格式展示出来,方便用户配置.一开始单纯的将数据格式写入到pre标签中, 但是通过pre标签写入的数据格式在代码可视化上不是很优雅.由于上 ...
- JavaScript的概念,引入,基本数据类型
08.05自我总结 JavaScript 一.概念 JavaScript(下文我们会用简称JS来代替)是脚本编程语言,JS语言开发的文件是以.js为后缀,通过在html文件中引入该js文件来控制htm ...
- linux中vi和vim操作
一.简单介绍 vi 和 vim 类似于windows的文本编辑器 所有的Linux系统都会内置vi文本编辑器 vim可以看做是vi增强版,具有程序编辑能力,支持语法高亮,代码补全,编译及错误跳转等功能 ...