当前位置:   article > 正文

LRU与LFU_lru和lfu的区别,应用场景有何不同

lru和lfu的区别,应用场景有何不同

一、概念

LRU(Least Recently Used,最近最少使用)算法和

LFU(Least Frequently Used,最不经常使用)算法

都是用来作为缓存淘汰机制,保证缓存空间的有效利用。

二、使用场景

适合使用 LRU 的场景:

  • 适合使用 LRU 的场景是数据访问具有时间局部性的场景。
  • 例如在Web网站中,用户最近访问的页面,很可能再次被访问。在这种情况下,缓存的内容一般都是最近访问的页面数据,而离当前时间较长的访问页面数据缓存命中率较小,很可能被清理出缓存,因此使用 LRU 算法清理缓存会比较合适。
  • 另一个适合使用 LRU 的场景是数据库缓存。通常情况下,数据库表不会突然新增大量的记录导致缓存不够用,而是查询热点数据集中在很少几张表上。在这种情况下,使用 LRU 算法淘汰缓存可以比 LFU 更好地利用 LRU 的时间局部性,将数据进入缓存,提高缓存命中率。

适合使用 LFU 的场景:

  • 适合使用 LFU 的场景是数据访问热度不均匀的场景。
  • 例如在电商网站的热门商品推荐、搜索引擎中的热门搜索推荐等场景中,只有少量的热门数据集中访问比较多,而大量的数据很少被访问,使用 LRU 算法会出现所谓的「冷用户」现象,这时候使用 LFU 算法就能很好的有效清理低访问率的数据。
  • 另一个适合使用 LFU 的场景是数据库缓存。当数据集合较大,且访问热度比较分散的时候,使用 LRU 的效果较差,因为 LRU 显然更倾向于缓存最近的数据,而 LFU 会更倾向于缓存访问频率高的数据。

综上,根据访问局部性和热度分布的不同,不同的应用场景会选择不同的缓存淘汰算法,以达到最优的缓存利用效果。

三、代码实现

LRU算法实现

  1. import java.util.LinkedHashMap;
  2. import java.util.Map;
  3. public class LRUCache<K, V> extends LinkedHashMap<K, V> {
  4. private final int MAX_CACHE_SIZE; // 缓存最大容量
  5. public LRUCache(int maxCacheSize) {
  6. // 第三个参数 accessOrder=true表示按照访问顺序而非插入顺序排序。
  7. super((int) Math.ceil(maxCacheSize / 0.75) + 1, 0.75f, true);
  8. MAX_CACHE_SIZE = maxCacheSize;
  9. }
  10. // 在LRUCache类中,我们通过覆盖removeEldestEntry方法来实现LRU算法的删除策略,当缓存元素长度超过设定的最大容量时,就将最久未使用的元素删除。
  11. @Override
  12. protected boolean removeEldestEntry(Map.Entry eldest) {
  13. return size() > MAX_CACHE_SIZE;
  14. }
  15. public static void main(String[] args) {
  16. LRUCache<String, String> cache = new LRUCache<>(3);
  17. cache.put("1", "one");
  18. cache.put("2", "two");
  19. cache.put("3", "three");
  20. System.out.println(cache);
  21. cache.get("2");
  22. cache.put("4", "four");
  23. System.out.println(cache);
  24. cache.put("3", "new-three");
  25. System.out.println(cache);
  26. }
  27. }

输出:

  1. {1=one, 2=two, 3=three}
  2. {2=two, 4=four, 3=three}
  3. {2=two, 4=four, 3=new-three}

LFU算法实现

  1. import java.util.HashMap;
  2. import java.util.LinkedHashSet;
  3. import java.util.Map;
  4. public class LFUCache {
  5. // 记录key对应的value
  6. private Map<Integer, Integer> valueMap = new HashMap<>();
  7. // 记录key键的访问频率
  8. private Map<Integer, Integer> freqMap = new HashMap<>();
  9. // 记录每个频率下的key,使用LinkedHashSet保证添加和删除的效率
  10. private Map<Integer, LinkedHashSet<Integer>> freqKeys = new HashMap<>();
  11. // LFU缓存容量
  12. private int capacity;
  13. // 最小频率
  14. private int minFreq = 0;
  15. /**
  16. * LFU缓存构造函数
  17. * @param capacity 缓存容量
  18. */
  19. public LFUCache(int capacity) {
  20. this.capacity = capacity;
  21. }
  22. /**
  23. * 获取LFU缓存中对应key的value值
  24. * @param key 键
  25. * @return 对应的值
  26. */
  27. public int get(int key) {
  28. // 如果缓存中没有这个key,返回-1
  29. if (!valueMap.containsKey(key)) {
  30. return -1;
  31. }
  32. // 更新key的访问频率并返回value值
  33. int freq = freqMap.get(key);
  34. freqMap.put(key, freq + 1);
  35. freqKeys.get(freq).remove(key);
  36. if (freq == minFreq && freqKeys.get(freq).isEmpty()) {
  37. // 如果刚刚移除的是最小的频率,更新最小频率
  38. minFreq++;
  39. }
  40. freqKeys.computeIfAbsent(freq + 1, l -> new LinkedHashSet<>()).add(key);
  41. return valueMap.get(key);
  42. }
  43. /**
  44. * 向LFU缓存中添加键值对
  45. * @param key 键
  46. * @param value 值
  47. */
  48. public void put(int key, int value) {
  49. if (capacity <= 0) {
  50. return;
  51. }
  52. // 如果缓存中已经有这个key,更新value值
  53. if (valueMap.containsKey(key)) {
  54. valueMap.put(key, value);
  55. get(key);
  56. return;
  57. }
  58. // 缓存已满,移除访问频率最低的key
  59. if (valueMap.size() >= capacity) {
  60. int toRemove = freqKeys.get(minFreq).iterator().next();
  61. freqKeys.get(minFreq).remove(toRemove);
  62. valueMap.remove(toRemove);
  63. freqMap.remove(toRemove);
  64. }
  65. // 添加新的键值对
  66. valueMap.put(key, value);
  67. freqMap.put(key, 1);
  68. freqKeys.computeIfAbsent(1, l -> new LinkedHashSet<>()).add(key);
  69. minFreq = 1;
  70. }
  71. /**
  72. * 测试用例
  73. * @param args 参数
  74. */
  75. public static void main(String[] args) {
  76. LFUCache cache = new LFUCache(3);
  77. cache.put(1, 1);
  78. cache.put(2, 2);
  79. System.out.println(cache.get(1)); // 1
  80. cache.put(3, 3);
  81. System.out.println(cache.get(2)); // -1
  82. System.out.println(cache.get(3)); // 3
  83. cache.put(4, 4);
  84. System.out.println(cache.get(1)); // -1
  85. System.out.println(cache.get(3)); // 3
  86. System.out.println(cache.get(4)); // 4
  87. }
  88. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/950755
推荐阅读
相关标签
  

闽ICP备14008679号