Redis缓存失效策略思考

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我微信「java_front」一起交流学习

 

1 删除过期数据

我们在设置Redis元素时可以指定超时时间,那么Redis如何删除这些超时元素?Redis采用了两种策略:定期删除和惰性删除。

1.1 定期删除

Redis每隔一段时间就检查哪些KEY已经过期,如果过期就删除。但是我们来设想一个问题:如果Redis存储KEY非常多,仅仅超时检查这项工作就会非常耗费资源并严重影响服务能力。为了解决这个问题Redis并不是检查全量KEY而只是检查部分,同时引入了惰性删除策略。

1.2 惰性删除

假设当KEY1已经过期,但是由于没有被检查到而未被删除。那么当程序访问KEY1时,Redis会检查KEY1是否过期,如果过期则删除并不返回该值,这就是惰性删除策略。结合定期删除和惰性删除两种策略,就可以保证过期数据可以被删除。

 

2 内存淘汰

当内存不足时Redis会选择一些缓存元素进行删除,那么哪些缓存元素会被删除?删除规则就是Redis内存淘汰策略,常见内存淘汰策略如下:

no-enviction
禁止驱逐数据,新写入操作会报错

volatile-lru
从已设置过期时间的数据集选择最近最少使用的数据淘汰

volatile-ttl
从已设置过期时间的数据集选择将要过期的数据淘汰

volatile-random
从已设置过期时间的数据集选择任意的数据淘汰

allkeys-lru
从数据集选择最近最少使用的数据淘汰

allkeys-random
从数据集选择任意的数据淘汰

LRU(Least Recently Used)最近最少使用是比较常用的策略,我们使用JAVA代码实现一个简单LRU策略,代码原理并不复杂:使用一个链表存储元素,表头存储最近访问的元素,这样存储的结果是表尾存储最早访问的元素,表头存储最近访问的元素,当超出链表容量时删除表尾元素即可。

/**
 * 元素对象
 *
 * @author 今日头条号「IT徐胖子」
 *
 */
public class CacheElement {
	private String key;
	private Object value;

	public CacheElement(String key, Object value) {
		this.key = key;
		this.value = value;
	}

	public String getKey() {
		return key;
	}

	public Object getValue() {
		return value;
	}

	@Override
	public String toString() {
		return "Element [key=" + key + ", value=" + value + "]";
	}
}


/**
 * LRU缓存策略
 *
 * @author 今日头条号「IT徐胖子」
 *
 */
public class LRUCache {
	private int capacity;
	private LinkedList<CacheElement> cache;

	public LRUCache(int capacity) {
		this.capacity = capacity;
		this.cache = new LinkedList<>();
	}

	/**
	 * 获取缓存元素
	 *
	 * 找到元素后将元素从原位置删除并插入到链表头部(最近)
	 */
	public CacheElement get(String key) {
		Iterator<CacheElement> iterator = cache.iterator();
		while (iterator.hasNext()) {
			CacheElement element = iterator.next();
			if (element.getKey().equals(key)) {
				iterator.remove();
				System.out.println("获取到元素=" + element);
				put(element.getKey(), element.getValue());
				return element;
			}
		}
		return null;
	}

	/**
	 * 存储缓存元素
	 *
	 * 新元素插入到链表头部(最近)
	 */
	public boolean put(String key, Object value) {
		Iterator<CacheElement> iterator = cache.iterator();
		while (iterator.hasNext()) {
			CacheElement element = iterator.next();
			if (element.getKey().equals(key)) {
				iterator.remove();
				break;
			}
		}
		if (capacity == cache.size()) {
			CacheElement deleteElement = cache.removeLast();
			System.out.println("容量已满删除尾部元素=" + deleteElement);
		}
		CacheElement element = new CacheElement(key, value);
		cache.addFirst(element);
		System.out.println("插入头部元素=" + element);
		return Boolean.TRUE;
	}

	@Override
	public String toString() {
		return "LRUCache [capacity=" + capacity + ", cache=" + cache + "]";
	}
}
/**
 * LRU测试实例
 *
 * @author 今日头条号「IT徐胖子」
 *
 */
public class TestCache {
	public static void main(String[] args) {
		System.out.println("==================存储缓存元素==================");
		LRUCache cache = new LRUCache(2);
		CacheElement element0 = new CacheElement("k0", "v0");
		CacheElement element1 = new CacheElement("k1", "v1");
		CacheElement element2 = new CacheElement("k2", "v2");
		cache.put(element0.getKey(), element0.getValue());
		cache.put(element1.getKey(), element1.getValue());
		cache.put(element2.getKey(), element2.getValue());

		System.out.println("==================获取缓存元素==================");
		System.out.println("获取元素之前缓存对象=" + cache);
		cache.get("k1");
		System.out.println("获取元素之后缓存对象=" + cache);
	}
}
==================存储缓存元素==================
插入头部元素=Element [key=k0, value=v0]
插入头部元素=Element [key=k1, value=v1]
容量已满删除尾部元素=Element [key=k0, value=v0]
插入头部元素=Element [key=k2, value=v2]

==================获取缓存元素==================
获取元素之前缓存对象=LRUCache [capacity=2, cache=[Element [key=k2, value=v2], Element [key=k1, value=v1]]]
获取到元素=Element [key=k1, value=v1]
插入头部元素=Element [key=k1, value=v1]
获取元素之后缓存对象=LRUCache [capacity=2, cache=[Element [key=k1, value=v1], Element [key=k2, value=v2]]]

3 文章总结

本文我们分析了Redis缓存失效策略:删除过期数据和内存淘汰,并且使用JAVA代码模拟了LRU策略实现。这里我们可以做一个引申:Redis分布式锁是否可靠。因为Redis存在着内存淘汰机制,那么作为分布式锁的KEY从概率上会被淘汰,从而导致分布式锁失效。所以仅仅有分布式锁是不够的,我们还需要其它方法,例如设置数据库层唯一索引,防止重复数据产生。

欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我微信「java_front」一起交流学习

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值