热数据缓存
这是使用缓存最频繁最直接的方式,即我们把需要频繁访问DB的数据加载到内存里面,以提高响应速度。通常我们的做法是使用一个ConcuccrentHashMap<Request, AtomicInteger>来记录一天当中每个请求的次数,每天凌晨取出昨天访问最频繁的K个请求(K取多少个取决你的可用内存有多少),从DB中读取这些请求的返回结果放到一个ConcuccrentHashMap<Request, Response>容器中,然后把所有请求计数清0,重新开始计数。
LRU缓存
热数据缓存适用于那些热数据比较明显且稳定的业务场景,而对于那些热数据不稳定的应用场景我们需要发明一种动态的热数据识别方式。我们都知道常用的内存换页算法有2种:LFU和LRU。
LFU(Least Frequently Used)是把那些最近最不经常使用的页面置换出去,这跟上面讲的热数据缓存是一个道理,缺点有2个:
- 需要维护一个计数器,记住每个页面的使用次数。
- 上一个时间段频繁使用的,在下一个时间段不一定还频繁。
LRU(Least Recently Used)策略是把最近最长时间未使用的页面置换出去。实现起来很简单,只需要一个链表结构,每次访问一个元素时把它移到链表的尾部,当链表已满需要删除元素时就删除头部的元素,因为头部的元素就是最近最长时间未使用的元素。
1 import java.util.ArrayList; 2 import java.util.Collection; 3 import java.util.LinkedHashMap; 4 import java.util.Map; 5 import java.util.concurrent.locks.ReadWriteLock; 6 import java.util.concurrent.locks.ReentrantReadWriteLock; 7 8 /** 9 * 利用LinkedHashMap实现一个定长容量的,先进先出的队列。当指定按访问顺序排序时,就实际上是一个最近最少使用LRU队列<br> 10 * <br> 11 * 根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表。<br> 12 * 默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素移至链表尾部。<br> 13 * 不断访问可以形成按访问顺序排序的链表。<br> 14 * 可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素。<br> 15 * 16 * @Author:zhangchaoyang 17 * @Since:2014-9-5 18 * @Version:1.0 19 */ 20 public class LRUCache<K, V> extends LinkedHashMap<K, V> { 21 22 private static final long serialVersionUID = -2045058079564141163L; 23 24 private final int maxCapacity; 25 26 // 本类中设置装载因子实际没有意义,因为容量超过maxCapacity时就会把元素移除掉 27 private static final float DEFAULT_LOAD_FACTOR = 1f; 28 29 private final ReadWriteLock lock = new ReentrantReadWriteLock(); 30 31 public LRUCache(int maxCapacity) { 32 super(maxCapacity, DEFAULT_LOAD_FACTOR, true);// 第3个参数false表示维持插入顺序,这样最早插入的将最先被移除。true表示维持访问顺序,调用get方法后,会将这次访问的元素移至链表尾部,删除老元素时会删除表头元素。 33 this.maxCapacity = maxCapacity; 34 } 35 36 @Override 37 protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { 38 return size() > maxCapacity;// 到达maxCapacity时就移除老元素,这样实现定长的LinkedHashMap 39 } 40 41 @Override 42 public boolean containsKey(Object key) { 43 try { 44 lock.readLock().lock(); 45 return super.containsKey(key); 46 } finally { 47 lock.readLock().unlock(); 48 } 49 } 50 51 @Override 52 public V get(Object key) { 53 try { 54 lock.readLock().lock(); 55 return super.get(key); 56 } finally { 57 lock.readLock().unlock(); 58 } 59 } 60 61 @Override 62 public V put(K key, V value) { 63 try { 64 lock.writeLock().lock(); 65 return super.put(key, value); 66 } finally { 67 lock.writeLock().unlock(); 68 } 69 } 70 71 public int size() { 72 try { 73 lock.readLock().lock(); 74 return super.size(); 75 } finally { 76 lock.readLock().unlock(); 77 } 78 } 79 80 public void clear() { 81 try { 82 lock.writeLock().lock(); 83 super.clear(); 84 } finally { 85 lock.writeLock().unlock(); 86 } 87 } 88 89 public Collection<Map.Entry<K, V>> getAll() { 90 try { 91 lock.readLock().lock(); 92 return new ArrayList<Map.Entry<K, V>>(super.entrySet()); 93 } finally { 94 lock.readLock().unlock(); 95 } 96 } 97 }