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 ...
随机推荐
- diango创建一个app
创建一个app terminal里执行命令 python manage.py startapp app名称 注册 settings配置 INSTALLED_APPS = [ 'app01', 'app ...
- Vim 命令常用功能详解
Vim编辑器 文本编辑器 , 字处理器ASCIIvi:Visual Interface vim :VI iMproved 全屏编辑器,模式化编辑器vim 模式:编辑模式(命令模式)输入模式末行模式 模 ...
- 【转载】解决:'webpack-dev-server' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
注:网上能搜到的常规解决办法我都试了不好用,这个是最快的解决办法. 以下是转载的解决办法: ****************************************************** ...
- leetcode题解:回文数
判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解释: 从左向 ...
- MATLAB实例:绘制折线图
MATLAB实例:绘制折线图 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 条形图的绘制见:MATLAB实例:绘制条形图 用MATLAB将几组不同的数 ...
- 基于docker-compose搭建本地sentry服务
环境要求: centos 7 Docker 17.05.0+ Compose 1.19.0+ RAM 2400MB docker-compose 安装 ``$ curl -L https://get. ...
- Java 创建线程的3种方法及各自优势
1. 继承 Thread 类,然后调用 start 方法. class MyThread extends Thread { //重写run方法,线程运行后,跑的就是run方法 public void ...
- Element-ui 下拉列表 选项过多时通过自定义搜索来解决卡顿问题
当使用Select选择器时,如果下拉列表的数据量太多,会有一个明显的卡顿体验,例如: <!DOCTYPE html> <html lang="en"> &l ...
- PHP实现微信提现功能
提现必须得用双向证书.所以大家一定要在微信的商户平台找到相应的地方去设置.因为做这个提现已经有一段时间了.所以设置微信商户平台的那几个地方没有图的情况.也说不清楚.下次再做提现的时候.给大家分享如何设 ...
- 资深程序员对于Python各个方向的面试经验分享,非常给力!
之前早有前辈们说过,"裸辞一时爽,一直裸辞一直爽",这话一点不假,裸辞你要面临没有收入来源,但是每天眼睁睁看着各种花销不断支出的煎熬,我主要是觉得一边在上家公司工作一边去下家面试可 ...