ToB企服应用市场:ToB评测及商务社交产业平台

标题: LRU算法的应用 [打印本页]

作者: 东湖之滨    时间: 昨天 22:30
标题: LRU算法的应用
13.LRU算法的应用

题目

关于用户信息的需求
假定在一个复杂的体系中,必要抽象出一个用户体系,提供给其他子体系使用,该怎样实现。子体系对用户信息的查询频率很高,要留意性能题目。
用户信息是存储在数据库里的,但是对于查询频率高的数据,不能每一次哀求时都去查询数据库。
思绪

哈希表

使用以用户id为key,用户信息为value的哈希表,作为缓存以提高性能。存在题目,如果只向哈希表中存储数据,而不移除数据的话,可能内存会溢出,导致服务器宕机。
LRU全称Least Recently Used,最近最少未使用,是一种内存管理算法,该算法最早应用于LinuxOS。
该算法,基于一种假设:长期不使用的数据,在未来使用的概率也不大。因此,当内存数据占内存达到肯定阈值时,要移除最近最少使用的数据。
该算法,还基于一种,特殊的数据结构,哈希链表。
哈希表是由若干个K-V键值对的Node节点构成的。在此逻辑基础上,每个节点,加一个pre前驱和一个next后驱,将这些Node连接起来,想链表一样。
假设,使用哈希链表存储用户信息,001-User1002-User2003-User3004-User4。每次,对于未缓存的用户数据,都使用尾加法,加到链表的右边,对于使用到的链表中的数据,也将该节点移动到链表的末尾。这样链表的左侧,就是最近最少未使用的数据,移除就好。
代码
  1. public class LRUCache {
  2.     private Node head;
  3.     private Node end;
  4.     private int limit;
  5.     private HashMap<String, Node> hashMap;
  6.     public LRUCache(int limit) {
  7.         this.limit = limit;
  8.         hashMap = new HashMap<>();
  9.     }
  10.     public String get(String key) {
  11.         Node node = hashMap.get(key);
  12.         if (node == null)
  13.             return null;
  14.         refreshNode(node);
  15.         return node.value;
  16.     }
  17.     public void put(String key, String value) {
  18.         Node node = hashMap.get(key);
  19.         if (node == null) {
  20.             if (hashMap.size() >= limit) {
  21.                 String oldKey = removeNode(head);
  22.                 hashMap.remove(oldKey);
  23.             }
  24.             node = new Node(key, value);
  25.             addNode(node);
  26.             hashMap.put(key, node);
  27.         } else {
  28.             //如果key存在,则刷新key-value
  29.             node.value = value;
  30.             refreshNode(node);
  31.         }
  32.     }
  33.     public void remove(String key){
  34.         Node node = hashMap.get(key);
  35.         refreshNode(node);
  36.         hashMap.remove(key);
  37.     }
  38.     /**
  39.      * 刷新被访问节点的位置
  40.      *
  41.      * @param node 被访问节点
  42.      */
  43.     private void refreshNode(Node node) {
  44.         if (node == end)
  45.             return;
  46.         //移除节点
  47.         removeNode(node);
  48.         //重新插入节点
  49.         addNode(node);
  50.     }
  51.     /**
  52.      * 删除节点
  53.      *
  54.      * @param node 要删除的节点
  55.      * @return
  56.      */
  57.     private String removeNode(Node node) {
  58.         if (node == head && node == end) {
  59.             //移除唯一节点
  60.             head = null;
  61.             end = null;
  62.         } else if (node == end) {
  63.             //移除尾结点
  64.             end = end.pre;
  65.             end.next = null;
  66.         } else if (node == head) {
  67.             //移除头节点
  68.             head = head.next;
  69.             head.pre = null;
  70.         } else {
  71.             //移除中间节点
  72.             node.pre.next = node.next;//将当前节点的下一个,赋值给上一节点的下一个
  73.             node.next.pre = node.pre;//将当前节点的前一个,赋值给上一节点的前一个
  74.         }
  75.         return node.key;
  76.     }
  77.     private void addNode(Node node) {
  78.         if (end != null) {
  79.             end.next = node;
  80.             node.pre = end;
  81.             node.next = null;
  82.         }
  83.         end = node;
  84.         if (head == null)
  85.             head = node;
  86.     }
  87.     private static class Node {
  88.         Node(String key, String value) {
  89.             this.key = key;
  90.             this.value = value;
  91.         }
  92.         public Node pre;
  93.         public Node next;
  94.         public String key;
  95.         public String value;
  96.     }
  97.     public static void main(String[] args) {
  98.         LRUCache lruCache = new LRUCache(5);
  99.         lruCache.put("001","用户1信息");
  100.         lruCache.put("002","用户2信息");
  101.         lruCache.put("003","用户3信息");
  102.         lruCache.put("004","用户4信息");
  103.         lruCache.put("005","用户5信息");
  104.         lruCache.get("002");
  105.         lruCache.put("004","new用户4信息");
  106.         lruCache.put("006","用户6信息");
  107.         System.out.println(lruCache.get("001"));
  108.         System.out.println(lruCache.get("006"));
  109.     }
  110. }
复制代码
留意,该代码本质照旧使用HashMap作为缓存,且如果多个线程共享一个LRUCache对象时,访问这些方法,是线程不安全的。这是可以给这些方法手动加锁,大概使用ConcurrentHashMap类占存数据。本demo中的,存储的用户使用String范例表现,只是为了演示LRU算法,实际使用中,可以使用泛型。
对于这种缓存高频访问的数据,一般都是用Redis这种NoSQL的内存数据库,其底层也实现了类似LRU的回收算法。该笔记只是展示LRU算法。而且使用缓存数据库,就肯定存在一个数据同等性的题目,就是关系型数据库中存储的数据和缓存中的数据怎样保持同等?后续有机会分享。
只是为了记录本身的学习历程,且本人水平有限,不对之处,请指正。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4