需求
随着公司的业务越来越复杂,需要提供一个用户系统,供各个业务系统来查询用户的基本信息。且业务方对用户信息的查询频率很高,设计的用户系统需要注意性能。
- 初始设计: 考虑到性能,可以在内存中创建一个哈希表作为缓存,每当查找一个用户时,会现在哈希表中进行查询,查询不到再去数据库查询。
- 初始设计存在的问题: 随着用户量不断增大,可能会因为哈希表逐渐增大导致内存某一天会被撑爆。
- 初始设计的优化:使用LRU算法(内存管理算法,Least Recently Used),最近最少使用的思想。算法基于一种假设:长期不被使用的数据,在未来被用到的几率也不大。因此,当数据所占内存达到一定阈值时,我们要移除掉最近最少被使用的数据。
LRU算法
在LRU算法中,使用一种叫作“哈希链表”的数据结构。哈希表由若干个key-value组成,逻辑上这些key-value是无所谓排序的。但在哈希链表中,这些key-value被一个链条串在一起,不再是无序的了,而是有 了固定的排列顺序。每一个key-value都有前驱key-value、后继key-value。所以我们可以把key-value按照最后使用的时间来进行排序。链表的尾部是最近被使用的,头部是最近被使用最少甚至没有被使用过的key-value。所以,当缓存容量达到上限时,会先删除链表最左端的值,再把新的值插入到链表的最右端。
LRU算法实现
注意点: 删除节点的同时,记得把节点的key也要从hashMap中删除
1 package blogSrc; 2 3 import java.util.HashMap; 4 5 public class LRUCache { 6 private Node head; 7 private Node end; 8 9 private HashMap<String,Node> hashMap; 10 private int limit; //缓存上限 11 12 public LRUCache(int limit){ 13 this.limit = limit; 14 this.hashMap = new HashMap<String,Node>(); 15 } 16 17 public void addNode (Node node){ 18 if (head == null){ //空链 19 head = node; 20 } 21 22 if(end != null) { //节点添加到尾部 23 end.next = node; 24 node.pre = end; 25 node.next = null; 26 } 27 end = node; 28 } 29 30 public String removeNode(Node node){ 31 if(head == end && node == head){ //链表只有一个节点 32 head = null; 33 end = null; 34 }else if (node == end){ //node为最后一个节点 35 end.pre.next = null; 36 end = end.pre; 37 }else if (node == head){ //node为第一个节点 38 head.next.pre = null; 39 head = head.next; 40 }else { //node为中间节点 41 node.pre.next = node.next; 42 node.next.pre = node.pre; 43 } 44 return node.key; 45 } 46 47 //刷新链表 48 public void refreshNode(Node node){ 49 if (node == end){ 50 return; 51 } 52 removeNode(node); //删除节点 53 addNode(node); //添加节点 54 } 55 56 //从链表上取数据 57 public String get(String key){ 58 Node node = hashMap.get(key); 59 if (node == null){ 60 return null; 61 } 62 refreshNode(node); 63 return node.value; 64 } 65 66 //向链表上添加key-value 67 public void put(String key, String value){ 68 Node node = hashMap.get(key); 69 if (node == null){ 70 if (hashMap.size() >= limit){ 71 String oldKey = removeNode(head); //删除节点 72 hashMap.remove(oldKey); //同时需要把节点的key也从hashmap中删除 73 } 74 node = new Node(key,value); 75 addNode(node); 76 hashMap.put(key,node); 77 }else { 78 node.value = value; 79 refreshNode(node); 80 } 81 } 82 83 //从链表上删除数据 84 public void remove(String key){ 85 Node node = hashMap.get(key); 86 if (node == null){ 87 return ; 88 } 89 removeNode(node); 90 hashMap.remove(key); 91 } 92 93 class Node { 94 public Node next; 95 public Node pre; 96 public String key; 97 public String value; 98 Node(String key, String value){ 99 this.key = key; 100 this.value = value; 101 } 102 103 public String getNextKey() { 104 return next.getKey(); 105 } 106 107 public String getPreKey() { 108 return pre.getKey(); 109 } 110 111 public String getKey() { 112 return key; 113 } 114 115 public String getValue() { 116 return value; 117 } 118 } 119 120 121 public static void main(String[] args){ 122 LRUCache lruCache = new LRUCache(10); 123 lruCache.put("001","用户1信息"); 124 lruCache.put("002","用户2信息"); 125 lruCache.put("003","用户3信息"); 126 lruCache.put("004","用户4信息"); 127 lruCache.get("003"); 128 System.out.println("Now End Node Key is: " + lruCache.end.getKey()+ ",Value is: " +lruCache.end.getValue()); 129 lruCache.put("002","用户2信息更新"); 130 System.out.println("Now End Node Key is: " + lruCache.end.getKey() + ",Value is: " +lruCache.end.getValue() ); 131 lruCache.put("006","用户6信息"); 132 System.out.println("Now End Node Key is: " + lruCache.end.getKey()+ ",Value is: " +lruCache.end.getValue()); 133 } 134 135 }
结果:
Now End Node Key is: 003,Value is: 用户3信息 Now End Node Key is: 002,Value is: 用户2信息更新 Now End Node Key is: 006,Value is: 用户6信息