LruCache

LruCache通过重写LinkedHashMap的removeEldestEntry方法移除最老的元素。

  1. package org.apache.ibatis.cache.decorators;
  2. import java.util.LinkedHashMap;
  3. import java.util.Map;
  4. import java.util.concurrent.locks.ReadWriteLock;
  5. import org.apache.ibatis.cache.Cache;
  6. /**
  7. * Lru (least recently used) cache decorator
  8. *
  9. * @author Clinton Begin
  10. */
  11. public class LruCache implements Cache {
  12. private final Cache delegate;
  13. private Map<Object, Object> keyMap;
  14. private Object eldestKey;
  15. public LruCache(Cache delegate) {
  16. this.delegate = delegate;
  17. setSize(1024);
  18. }
  19. @Override
  20. public String getId() {
  21. return delegate.getId();
  22. }
  23. @Override
  24. public int getSize() {
  25. return delegate.getSize();
  26. }
  27. public void setSize(final int size) {
  28. keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
  29. private static final long serialVersionUID = 4267176411845948333L;
  30. //重写LinkedHashMap的removeEldestEntry方法,
  31. //当数据量超过size时移除最老的元素
  32. @Override
  33. protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
  34. boolean tooBig = size() > size;
  35. if (tooBig) {
  36. eldestKey = eldest.getKey();
  37. }
  38. return tooBig;
  39. }
  40. };
  41. }
  42. @Override
  43. public void putObject(Object key, Object value) {
  44. delegate.putObject(key, value);
  45. //同时把key放到keyMap中,并判断是否删除缓存超出的数据
  46. cycleKeyList(key);
  47. }
  48. @Override
  49. public Object getObject(Object key) {
  50. keyMap.get(key); //touch
  51. return delegate.getObject(key);
  52. }
  53. private void cycleKeyList(Object key) {
  54. keyMap.put(key, key);
  55. if (eldestKey != null) {
  56. //删除缓存
  57. delegate.removeObject(eldestKey);
  58. eldestKey = null;
  59. }
  60. }
  61. }

FifoCache

  1. package org.apache.ibatis.cache.decorators;
  2. import java.util.Deque;
  3. import java.util.LinkedList;
  4. import java.util.concurrent.locks.ReadWriteLock;
  5. import org.apache.ibatis.cache.Cache;
  6. /**
  7. * FIFO (first in, first out) cache decorator
  8. *
  9. * @author Clinton Begin
  10. */
  11. public class FifoCache implements Cache {
  12. private final Cache delegate;
  13. //用队列保存缓存顺序
  14. private final Deque<Object> keyList;
  15. private int size;
  16. public FifoCache(Cache delegate) {
  17. this.delegate = delegate;
  18. this.keyList = new LinkedList<Object>();
  19. this.size = 1024;
  20. }
  21. @Override
  22. public void putObject(Object key, Object value) {
  23. //把key保存到keyList
  24. cycleKeyList(key);
  25. delegate.putObject(key, value);
  26. }
  27. @Override
  28. public void clear() {
  29. delegate.clear();
  30. keyList.clear();
  31. }
  32. private void cycleKeyList(Object key) {
  33. keyList.addLast(key);
  34. //缓存个数超出限制,删掉最新进入的
  35. if (keyList.size() > size) {
  36. Object oldestKey = keyList.removeFirst();
  37. delegate.removeObject(oldestKey);
  38. }
  39. }
  40. }

BlockingCache

当一个key的值为空的时候,阻塞线程直到key的值被塞进缓存才释放锁,这样也就避免了冲击数据库。

  1. package org.apache.ibatis.cache.decorators;
  2. import java.util.concurrent.ConcurrentHashMap;
  3. import java.util.concurrent.TimeUnit;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.ReadWriteLock;
  6. import java.util.concurrent.locks.ReentrantLock;
  7. import org.apache.ibatis.cache.Cache;
  8. import org.apache.ibatis.cache.CacheException;
  9. /**
  10. * Simple blocking decorator
  11. *
  12. * Simple and inefficient version of EhCache's BlockingCache decorator.
  13. * It sets a lock over a cache key when the element is not found in cache.
  14. * This way, other threads will wait until this element is filled instead of hitting the database.
  15. *
  16. * @author Eduardo Macarron
  17. *
  18. */
  19. public class BlockingCache implements Cache {
  20. private long timeout;
  21. private final Cache delegate;
  22. //每个key都有一把锁
  23. private final ConcurrentHashMap<Object, ReentrantLock> locks;
  24. public BlockingCache(Cache delegate) {
  25. this.delegate = delegate;
  26. this.locks = new ConcurrentHashMap<Object, ReentrantLock>();
  27. }
  28. @Override
  29. public void putObject(Object key, Object value) {
  30. try {
  31. delegate.putObject(key, value);
  32. } finally {
  33. //当key的值不为空就释放锁
  34. releaseLock(key);
  35. }
  36. }
  37. @Override
  38. public Object getObject(Object key) {
  39. //先拿到锁
  40. acquireLock(key);
  41. Object value = delegate.getObject(key);
  42. if (value != null) {
  43. //如果值不为空就释放锁
  44. releaseLock(key);
  45. }
  46. return value;
  47. }
  48. @Override
  49. public Object removeObject(Object key) {
  50. // despite of its name, this method is called only to release locks
  51. releaseLock(key);
  52. return null;
  53. }
  54. //每个key生成一把锁
  55. private ReentrantLock getLockForKey(Object key) {
  56. ReentrantLock lock = new ReentrantLock();
  57. ReentrantLock previous = locks.putIfAbsent(key, lock);
  58. return previous == null ? lock : previous;
  59. }
  60. //如果key的值为空,那么第一个获取key的线程占据锁,
  61. //其他线程就会阻塞,当设置key不为空时就会释放锁,
  62. //然后其他线程拿到锁,就可以获取key的值了
  63. private void acquireLock(Object key) {
  64. Lock lock = getLockForKey(key);
  65. if (timeout > 0) {
  66. try {
  67. boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
  68. if (!acquired) {
  69. throw new CacheException("Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
  70. }
  71. } catch (InterruptedException e) {
  72. throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
  73. }
  74. } else {
  75. lock.lock();
  76. }
  77. }
  78. private void releaseLock(Object key) {
  79. ReentrantLock lock = locks.get(key);
  80. if (lock.isHeldByCurrentThread()) {
  81. lock.unlock();
  82. }
  83. }
  84. }

SynchronizedCache

SynchronizedCache在每个方法上都有synchronized同步标记,保证同一时刻只有一个线程操作缓存。

  1. /**
  2. * Copyright 2009-2017 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.cache.decorators;
  17. import java.util.concurrent.locks.ReadWriteLock;
  18. import org.apache.ibatis.cache.Cache;
  19. /**
  20. * @author Clinton Begin
  21. */
  22. public class SynchronizedCache implements Cache {
  23. private final Cache delegate;
  24. public SynchronizedCache(Cache delegate) {
  25. this.delegate = delegate;
  26. }
  27. @Override
  28. public synchronized void putObject(Object key, Object object) {
  29. delegate.putObject(key, object);
  30. }
  31. @Override
  32. public synchronized Object getObject(Object key) {
  33. return delegate.getObject(key);
  34. }
  35. @Override
  36. public synchronized Object removeObject(Object key) {
  37. return delegate.removeObject(key);
  38. }
  39. @Override
  40. public synchronized void clear() {
  41. delegate.clear();
  42. }
  43. }

TransactionalCache

TransactionalCache是一个二级缓存事务缓冲区。此类保存会话期间要添加到二级缓存的所有缓存项。调用commit时将条目发送到缓存,如果回滚会话,则丢弃条目。

  1. /**
  2. * Copyright 2009-2017 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.cache.decorators;
  17. import java.util.HashMap;
  18. import java.util.HashSet;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import java.util.concurrent.locks.ReadWriteLock;
  22. import org.apache.ibatis.cache.Cache;
  23. import org.apache.ibatis.logging.Log;
  24. import org.apache.ibatis.logging.LogFactory;
  25. /**
  26. * The 2nd level cache transactional buffer.
  27. *
  28. * This class holds all cache entries that are to be added to the 2nd level cache during a Session.
  29. * Entries are sent to the cache when commit is called or discarded if the Session is rolled back.
  30. * Blocking cache support has been added. Therefore any get() that returns a cache miss
  31. * will be followed by a put() so any lock associated with the key can be released.
  32. *
  33. * @author Clinton Begin
  34. * @author Eduardo Macarron
  35. */
  36. public class TransactionalCache implements Cache {
  37. private static final Log log = LogFactory.getLog(TransactionalCache.class);
  38. private final Cache delegate;
  39. private boolean clearOnCommit;
  40. private final Map<Object, Object> entriesToAddOnCommit;
  41. private final Set<Object> entriesMissedInCache;
  42. public TransactionalCache(Cache delegate) {
  43. this.delegate = delegate;
  44. this.clearOnCommit = false;
  45. this.entriesToAddOnCommit = new HashMap<Object, Object>();
  46. this.entriesMissedInCache = new HashSet<Object>();
  47. }
  48. @Override
  49. public String getId() {
  50. return delegate.getId();
  51. }
  52. @Override
  53. public int getSize() {
  54. return delegate.getSize();
  55. }
  56. @Override
  57. public Object getObject(Object key) {
  58. // issue #116
  59. Object object = delegate.getObject(key);
  60. if (object == null) {
  61. entriesMissedInCache.add(key);
  62. }
  63. // issue #146
  64. if (clearOnCommit) {
  65. return null;
  66. } else {
  67. return object;
  68. }
  69. }
  70. @Override
  71. public ReadWriteLock getReadWriteLock() {
  72. return null;
  73. }
  74. @Override
  75. public void putObject(Object key, Object object) {
  76. //先放在缓存区,提交事务时才会真正放到缓存
  77. entriesToAddOnCommit.put(key, object);
  78. }
  79. @Override
  80. public Object removeObject(Object key) {
  81. return null;
  82. }
  83. //先清除缓冲区内容,提交事务时才会真正清除缓存
  84. @Override
  85. public void clear() {
  86. clearOnCommit = true;
  87. entriesToAddOnCommit.clear();
  88. }
  89. //提交事务
  90. public void commit() {
  91. if (clearOnCommit) {
  92. delegate.clear();
  93. }
  94. flushPendingEntries();
  95. reset();
  96. }
  97. public void rollback() {
  98. unlockMissedEntries();
  99. reset();
  100. }
  101. private void reset() {
  102. clearOnCommit = false;
  103. entriesToAddOnCommit.clear();
  104. entriesMissedInCache.clear();
  105. }
  106. private void flushPendingEntries() {
  107. for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
  108. delegate.putObject(entry.getKey(), entry.getValue());
  109. }
  110. for (Object entry : entriesMissedInCache) {
  111. if (!entriesToAddOnCommit.containsKey(entry)) {
  112. delegate.putObject(entry, null);
  113. }
  114. }
  115. }
  116. private void unlockMissedEntries() {
  117. for (Object entry : entriesMissedInCache) {
  118. try {
  119. delegate.removeObject(entry);
  120. } catch (Exception e) {
  121. log.warn("Unexpected exception while notifiying a rollback to the cache adapter."
  122. + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
  123. }
  124. }
  125. }
  126. }

LoggingCache

用于计算缓存命中率。

  1. package org.apache.ibatis.cache.decorators;
  2. import java.util.concurrent.locks.ReadWriteLock;
  3. import org.apache.ibatis.cache.Cache;
  4. import org.apache.ibatis.logging.Log;
  5. import org.apache.ibatis.logging.LogFactory;
  6. /**
  7. * @author Clinton Begin
  8. */
  9. public class LoggingCache implements Cache {
  10. private final Log log;
  11. private final Cache delegate;
  12. protected int requests = 0;
  13. protected int hits = 0;
  14. @Override
  15. public Object getObject(Object key) {
  16. //总的请求数
  17. requests++;
  18. final Object value = delegate.getObject(key);
  19. if (value != null) {
  20. //缓存命中数
  21. hits++;
  22. }
  23. if (log.isDebugEnabled()) {
  24. //日志记录缓存命中率
  25. log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
  26. }
  27. return value;
  28. }
  29. private double getHitRatio() {
  30. return (double) hits / (double) requests;
  31. }
  32. }

装饰器模式

这些缓存通过装饰器组合,可以形成具有很多种功能的缓存。

  1. PerpetualCache cache = new PerpetualCache("id");
  2. //具有lru功能
  3. LruCache lru = new LruCache(cache);
  4. //具有lru和log功能
  5. LoggingCache log = new LoggingCache(lru);
  6. //具有lru和log和同步锁功能
  7. SynchronizedCache sync = new SynchronizedCache(log);