当前位置:   article > 正文

Redis缓存失效策略思考

redis 查看缓存失效策略

1 删除过期数据

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

(1) 定期删除

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

(2) 惰性删除

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

2 内存淘汰

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

  1. no-enviction
  2. 禁止驱逐数据,新写入操作会报错
  3. volatile-lru
  4. 从已设置过期时间的数据集选择最近最少使用的数据淘汰
  5. volatile-ttl
  6. 从已设置过期时间的数据集选择将要过期的数据淘汰
  7. volatile-random
  8. 从已设置过期时间的数据集选择任意的数据淘汰
  9. allkeys-lru
  10. 从数据集选择最近最少使用的数据淘汰
  11. allkeys-random
  12. 从数据集选择任意的数据淘汰

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

  1. /**
  2. * 元素对象
  3. *
  4.  * @author 微信公众号「IT徐胖子」
  5. *
  6. */
  7. public class CacheElement {
  8. private String key;
  9. private Object value;
  10. public CacheElement(String key, Object value) {
  11. this.key = key;
  12. this.value = value;
  13. }
  14. public String getKey() {
  15. return key;
  16. }
  17. public Object getValue() {
  18. return value;
  19. }
  20. @Override
  21. public String toString() {
  22. return "Element [key=" + key + ", value=" + value + "]";
  23. }
  24. }
  25. /**
  26. * LRU缓存策略
  27. *
  28.  * @author 微信公众号「IT徐胖子」
  29. *
  30. */
  31. public class LRUCache {
  32. private int capacity;
  33. private LinkedList<CacheElement> cache;
  34. public LRUCache(int capacity) {
  35. this.capacity = capacity;
  36. this.cache = new LinkedList<>();
  37. }
  38. /**
  39. * 获取缓存元素
  40. *
  41. * 找到元素后将元素从原位置删除并插入到链表头部(最近)
  42. */
  43. public CacheElement get(String key) {
  44. Iterator<CacheElement> iterator = cache.iterator();
  45. while (iterator.hasNext()) {
  46. CacheElement element = iterator.next();
  47. if (element.getKey().equals(key)) {
  48. iterator.remove();
  49. System.out.println("获取到元素=" + element);
  50. put(element.getKey(), element.getValue());
  51. return element;
  52. }
  53. }
  54. return null;
  55. }
  56. /**
  57. * 存储缓存元素
  58. *
  59. * 新元素插入到链表头部(最近)
  60. */
  61. public boolean put(String key, Object value) {
  62. Iterator<CacheElement> iterator = cache.iterator();
  63. while (iterator.hasNext()) {
  64. CacheElement element = iterator.next();
  65. if (element.getKey().equals(key)) {
  66. iterator.remove();
  67. break;
  68. }
  69. }
  70. if (capacity == cache.size()) {
  71. CacheElement deleteElement = cache.removeLast();
  72. System.out.println("容量已满删除尾部元素=" + deleteElement);
  73. }
  74. CacheElement element = new CacheElement(key, value);
  75. cache.addFirst(element);
  76. System.out.println("插入头部元素=" + element);
  77. return Boolean.TRUE;
  78. }
  79. @Override
  80. public String toString() {
  81. return "LRUCache [capacity=" + capacity + ", cache=" + cache + "]";
  82. }
  83. }
  84. /**
  85. * LRU测试实例
  86. *
  87. * @author 微信公众号「IT徐胖子」
  88. *
  89. */
  90. public class TestCache {
  91. public static void main(String[] args) {
  92. System.out.println("==================存储缓存元素==================");
  93. LRUCache cache = new LRUCache(2);
  94. CacheElement element0 = new CacheElement("k0", "v0");
  95. CacheElement element1 = new CacheElement("k1", "v1");
  96. CacheElement element2 = new CacheElement("k2", "v2");
  97. cache.put(element0.getKey(), element0.getValue());
  98. cache.put(element1.getKey(), element1.getValue());
  99. cache.put(element2.getKey(), element2.getValue());
  100. System.out.println("==================获取缓存元素==================");
  101. System.out.println("获取元素之前缓存对象=" + cache);
  102. cache.get("k1");
  103. System.out.println("获取元素之后缓存对象=" + cache);
  104. }
  105. }
  106. ==================存储缓存元素==================
  107. 插入头部元素=Element [key=k0, value=v0]
  108. 插入头部元素=Element [key=k1, value=v1]
  109. 容量已满删除尾部元素=Element [key=k0, value=v0]
  110. 插入头部元素=Element [key=k2, value=v2]
  111. ==================获取缓存元素==================
  112. 获取元素之前缓存对象=LRUCache [capacity=2, cache=[Element [key=k2, value=v2], Element [key=k1, value=v1]]]
  113. 获取到元素=Element [key=k1, value=v1]
  114. 插入头部元素=Element [key=k1, value=v1]
  115. 获取元素之后缓存对象=LRUCache [capacity=2, cache=[Element [key=k1, value=v1], Element [key=k2, value=v2]]]

3 文章总结

本文分析了Redis缓存失效策略:删除过期数据和内存淘汰,并且使用JAVA代码模拟了LRU策略实现。

这里我们可以做一个展开:Redis分布式锁是否可靠。因为Redis存在内存淘汰机制,那么作为分布式锁的KEY概率上会被淘汰,从而导致分布式锁失效。所以仅仅有分布式锁是不够的,我们还需要其它方法,例如设置数据库层唯一索引,防止重复数据产生。

  1. 特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

  1. 长按订阅更多精彩▼
  2. 如有收获,点个在看,诚挚感谢
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/466917
推荐阅读
相关标签
  

闽ICP备14008679号