带你学习Mybatis之缓存



缓存

Cache接口

mybatis中包含一级缓存和二级缓存,在本质上是一样的,使用的都是Cache接口的实现。

Cache接口定义了所有缓存的基本行为。

public interface Cache {
  	// 缓存对象的id
    String getId();
		// 向缓存中添加数据,key一般为CacheKey
    void putObject(Object var1, Object var2);
		// 通过key来获取缓存值
    Object getObject(Object var1);
		// 通过key删除对应的缓存
    Object removeObject(Object var1);
		// 清除缓存
    void clear();
		// 获取缓存中数据的大小
    int getSize();
		// 3.2.6之后不在使用
    ReadWriteLock getReadWriteLock();
}

对于每一个 namespace 都会创建一个缓存的实例,Cache 实现类的构造方法都必须传入一个 String 类型的ID,Mybatis自身的实现类都使用 namespace 作为 ID

下面介绍一下Cache接口的实现

PerpetualCache

使用HashMap进行存储

public class PerpetualCache implements Cache {
    private final String id;
  // 存储缓存数据
    private final Map<Object, Object> cache = new HashMap();

    public PerpetualCache(String id) {
        this.id = id;
    }

    public String getId() {
        return this.id;
    }

    public int getSize() {
        return this.cache.size();
    }

    public void putObject(Object key, Object value) {
        this.cache.put(key, value);
    }

    public Object getObject(Object key) {
        return this.cache.get(key);
    }

    public Object removeObject(Object key) {
        return this.cache.remove(key);
    }

    public void clear() {
        this.cache.clear();
    }

    public boolean equals(Object o) {
        if (this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else if (this == o) {
            return true;
        } else if (!(o instanceof Cache)) {
            return false;
        } else {
            Cache otherCache = (Cache)o;
            return this.getId().equals(otherCache.getId());
        }
    }

    public int hashCode() {
        if (this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else {
            return this.getId().hashCode();
        }
    }
}
BlockingCache

BlockingCache是阻塞版本的缓存装饰器,保证只有一个线程到数据库中查找指定key对应的数据

public class BlockingCache implements Cache {
  	//阻塞超时时长
    private long timeout;
  	// 被装饰的底层Cache对象
    private final Cache delegate;
  	//每个key对应一个ReetrantLock
    private final ConcurrentHashMap<Object, ReentrantLock> locks;

    public BlockingCache(Cache delegate) {
        this.delegate = delegate;
        this.locks = new ConcurrentHashMap();
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

    public void putObject(Object key, Object value) {
        try {
            this.delegate.putObject(key, value);
        } finally {
            this.releaseLock(key);
        }

    }

    public Object getObject(Object key) {
      	// 获取该key对应的锁
        this.acquireLock(key);
      	//查询key
        Object value = this.delegate.getObject(key);
      	//缓存中有key对应的缓存项才会释放锁,否则会一直持有锁
        if (value != null) {
            this.releaseLock(key);
        }

        return value;
    }

    public Object removeObject(Object key) {
        this.releaseLock(key);
        return null;
    }

    public void clear() {
        this.delegate.clear();
    }

    private ReentrantLock getLockForKey(Object key) {
        return (ReentrantLock)this.locks.computeIfAbsent(key, (k) -> {
            return new ReentrantLock();
        });
    }

    private void acquireLock(Object key) {
        Lock lock = this.getLockForKey(key);
        if (this.timeout > 0L) {
            try {
                boolean acquired = lock.tryLock(this.timeout, TimeUnit.MILLISECONDS);
                if (!acquired) {
                    throw new CacheException("Couldn't get a lock in " + this.timeout + " for the key " + key + " at the cache " + this.delegate.getId());
                }
            } catch (InterruptedException var4) {
                throw new CacheException("Got interrupted while trying to acquire lock for key " + key, var4);
            }
        } else {
            lock.lock();
        }

    }

    private void releaseLock(Object key) {
        ReentrantLock lock = (ReentrantLock)this.locks.get(key);
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }

    }

    public long getTimeout() {
        return this.timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }
}
FifoCache

FifoCache是先进先出版本的装饰器如果缓存项的个数达到上限,会将缓存中最先入队的缓存项删除。

public class FifoCache implements Cache {
    private final Cache delegate;
  	// 根据key进入缓存的先后顺序
    private final Deque<Object> keyList;
  	// 记录缓存的上限
    private int size;

    public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList();
        this.size = 1024;
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

    public void setSize(int size) {
        this.size = size;
    }

    public void putObject(Object key, Object value) {
        this.cycleKeyList(key);
        this.delegate.putObject(key, value);
    }

    public Object getObject(Object key) {
        return this.delegate.getObject(key);
    }

    public Object removeObject(Object key) {
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.delegate.clear();
        this.keyList.clear();
    }
		
  	//检测并清理缓存
    private void cycleKeyList(Object key) {
        this.keyList.addLast(key);
        if (this.keyList.size() > this.size) {
            Object oldestKey = this.keyList.removeFirst();
            this.delegate.removeObject(oldestKey);
        }

    }
}
LruCache

LruCache按照最近最少使用算法进行缓存清理的装饰器。

public class LruCache implements Cache {
    private final Cache delegate;
  	//LinkedHashMap
    private Map<Object, Object> keyMap;
    private Object eldestKey;

    public LruCache(Cache delegate) {
        this.delegate = delegate;
        this.setSize(1024);
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

    public void setSize(final int size) {
      	//LinkedHashMap的第三个参数true表示记录的顺序是access-order,get方法会改变顺序记录
        this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) {
            private static final long serialVersionUID = 4267176411845948333L;
						// 调用put方法时会调用该方法
            protected boolean removeEldestEntry(Entry<Object, Object> eldest) {
                boolean tooBig = this.size() > size;
                if (tooBig) {
                    LruCache.this.eldestKey = eldest.getKey();
                }

                return tooBig;
            }
        };
    }

    public void putObject(Object key, Object value) {
        this.delegate.putObject(key, value);
        this.cycleKeyList(key);
    }

    public Object getObject(Object key) {
        this.keyMap.get(key);
        return this.delegate.getObject(key);
    }

    public Object removeObject(Object key) {
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.delegate.clear();
        this.keyMap.clear();
    }

    private void cycleKeyList(Object key) {
        this.keyMap.put(key, key);
        if (this.eldestKey != null) {
            this.delegate.removeObject(this.eldestKey);
            this.eldestKey = null;
        }

    }
}
SoftCache
public class SoftCache implements Cache {
  	// 最近使用的一部分缓存不会被GC掉,将值放到该队列中(强引用指向其value)
    private final Deque<Object> hardLinksToAvoidGarbageCollection;
  	//引用队列,用于记录已经被GC回收的缓存项所对应的SoftEntry对象
    private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
    private final Cache delegate;
    private int numberOfHardLinks;

    public SoftCache(Cache delegate) {
        this.delegate = delegate;
        this.numberOfHardLinks = 256;
        this.hardLinksToAvoidGarbageCollection = new LinkedList();
        this.queueOfGarbageCollectedEntries = new ReferenceQueue();
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        this.removeGarbageCollectedItems();
        return this.delegate.getSize();
    }

    public void setSize(int size) {
        this.numberOfHardLinks = size;
    }

    public void putObject(Object key, Object value) {
        this.removeGarbageCollectedItems();
        this.delegate.putObject(key, new SoftCache.SoftEntry(key, value, this.queueOfGarbageCollectedEntries));
    }

    public Object getObject(Object key) {
        Object result = null;
        SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
        if (softReference != null) {
            result = softReference.get();
            if (result == null) {
                this.delegate.removeObject(key);
            } else {
                synchronized(this.hardLinksToAvoidGarbageCollection) {
                    this.hardLinksToAvoidGarbageCollection.addFirst(result);
                    if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
                        this.hardLinksToAvoidGarbageCollection.removeLast();
                    }
                }
            }
        }

        return result;
    }

    public Object removeObject(Object key) {
        this.removeGarbageCollectedItems();
        return this.delegate.removeObject(key);
    }

    public void clear() {
        synchronized(this.hardLinksToAvoidGarbageCollection) {
            this.hardLinksToAvoidGarbageCollection.clear();
        }

        this.removeGarbageCollectedItems();
        this.delegate.clear();
    }

    private void removeGarbageCollectedItems() {
        SoftCache.SoftEntry sv;
        while((sv = (SoftCache.SoftEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
            this.delegate.removeObject(sv.key);
        }

    }

    private static class SoftEntry extends SoftReference<Object> {
        private final Object key;

        SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
            super(value, garbageCollectionQueue);
            this.key = key;
        }
    }
}
WeakCache
public class WeakCache implements Cache {
    private final Deque<Object> hardLinksToAvoidGarbageCollection;
    private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
    private final Cache delegate;
    private int numberOfHardLinks;

    public WeakCache(Cache delegate) {
        this.delegate = delegate;
        this.numberOfHardLinks = 256;
        this.hardLinksToAvoidGarbageCollection = new LinkedList();
        this.queueOfGarbageCollectedEntries = new ReferenceQueue();
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        this.removeGarbageCollectedItems();
        return this.delegate.getSize();
    }

    public void setSize(int size) {
        this.numberOfHardLinks = size;
    }

    public void putObject(Object key, Object value) {
        this.removeGarbageCollectedItems();
        this.delegate.putObject(key, new WeakCache.WeakEntry(key, value, this.queueOfGarbageCollectedEntries));
    }

    public Object getObject(Object key) {
        Object result = null;
        WeakReference<Object> weakReference = (WeakReference)this.delegate.getObject(key);
        if (weakReference != null) {
            result = weakReference.get();
            if (result == null) {
                this.delegate.removeObject(key);
            } else {
                this.hardLinksToAvoidGarbageCollection.addFirst(result);
                if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
                    this.hardLinksToAvoidGarbageCollection.removeLast();
                }
            }
        }

        return result;
    }

    public Object removeObject(Object key) {
        this.removeGarbageCollectedItems();
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.hardLinksToAvoidGarbageCollection.clear();
        this.removeGarbageCollectedItems();
        this.delegate.clear();
    }

    private void removeGarbageCollectedItems() {
        WeakCache.WeakEntry sv;
        while((sv = (WeakCache.WeakEntry)this.queueOfGarbageCollectedEntries.poll()) != null) {
            this.delegate.removeObject(sv.key);
        }

    }

    private static class WeakEntry extends WeakReference<Object> {
        private final Object key;

        private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
            super(value, garbageCollectionQueue);
            this.key = key;
        }
    }
}
LoggingCache

LoggingCache在Cache的基础上提供了日志功能,通过hits和request字段记录了Cache的命中次数和访问次数。

public class LoggingCache implements Cache {
    private final Log log;
    private final Cache delegate;
    protected int requests = 0;
    protected int hits = 0;

    public LoggingCache(Cache delegate) {
        this.delegate = delegate;
        this.log = LogFactory.getLog(this.getId());
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

    public void putObject(Object key, Object object) {
        this.delegate.putObject(key, object);
    }

    public Object getObject(Object key) {
        ++this.requests;
        Object value = this.delegate.getObject(key);
        if (value != null) {
            ++this.hits;
        }

        if (this.log.isDebugEnabled()) {
            this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
        }

        return value;
    }

    public Object removeObject(Object key) {
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.delegate.clear();
    }

    public int hashCode() {
        return this.delegate.hashCode();
    }

    public boolean equals(Object obj) {
        return this.delegate.equals(obj);
    }
		//命中率
    private double getHitRatio() {
        return (double)this.hits / (double)this.requests;
    }
}
ScheduledCache

ScheduledCache周期性清理缓存,

public class ScheduledCache implements Cache {
    private final Cache delegate;
  	// 记录两次缓存清理的时间间隔,默认一小时
    protected long clearInterval;
  	// 记录最后一次清理的时间戳
    protected long lastClear;

    public ScheduledCache(Cache delegate) {
        this.delegate = delegate;
        this.clearInterval = TimeUnit.HOURS.toMillis(1L);
        this.lastClear = System.currentTimeMillis();
    }

    public void setClearInterval(long clearInterval) {
        this.clearInterval = clearInterval;
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        this.clearWhenStale();
        return this.delegate.getSize();
    }

    public void putObject(Object key, Object object) {
        this.clearWhenStale();
        this.delegate.putObject(key, object);
    }

    public Object getObject(Object key) {
        return this.clearWhenStale() ? null : this.delegate.getObject(key);
    }

    public Object removeObject(Object key) {
        this.clearWhenStale();
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.lastClear = System.currentTimeMillis();
        this.delegate.clear();
    }

    public int hashCode() {
        return this.delegate.hashCode();
    }

    public boolean equals(Object obj) {
        return this.delegate.equals(obj);
    }

    private boolean clearWhenStale() {
        if (System.currentTimeMillis() - this.lastClear > this.clearInterval) {
            this.clear();
            return true;
        } else {
            return false;
        }
    }
}
SerializedCache

SerializedCache提供了将value进行序列化的功能

public class SerializedCache implements Cache {
    private final Cache delegate;

    public SerializedCache(Cache delegate) {
        this.delegate = delegate;
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

    public void putObject(Object key, Object object) {
        if (object != null && !(object instanceof Serializable)) {
            throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
        } else {
            this.delegate.putObject(key, this.serialize((Serializable)object));
        }
    }

    public Object getObject(Object key) {
        Object object = this.delegate.getObject(key);
        return object == null ? null : this.deserialize((byte[])((byte[])object));
    }

    public Object removeObject(Object key) {
        return this.delegate.removeObject(key);
    }

    public void clear() {
        this.delegate.clear();
    }

    public int hashCode() {
        return this.delegate.hashCode();
    }

    public boolean equals(Object obj) {
        return this.delegate.equals(obj);
    }

    private byte[] serialize(Serializable value) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            Throwable var3 = null;

            Object var6;
            try {
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                Throwable var5 = null;

                try {
                    oos.writeObject(value);
                    oos.flush();
                    var6 = bos.toByteArray();
                } catch (Throwable var31) {
                    var6 = var31;
                    var5 = var31;
                    throw var31;
                } finally {
                    if (oos != null) {
                        if (var5 != null) {
                            try {
                                oos.close();
                            } catch (Throwable var30) {
                                var5.addSuppressed(var30);
                            }
                        } else {
                            oos.close();
                        }
                    }

                }
            } catch (Throwable var33) {
                var3 = var33;
                throw var33;
            } finally {
                if (bos != null) {
                    if (var3 != null) {
                        try {
                            bos.close();
                        } catch (Throwable var29) {
                            var3.addSuppressed(var29);
                        }
                    } else {
                        bos.close();
                    }
                }

            }

            return (byte[])var6;
        } catch (Exception var35) {
            throw new CacheException("Error serializing object.  Cause: " + var35, var35);
        }
    }

    private Serializable deserialize(byte[] value) {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(value);
            Throwable var4 = null;

            Serializable result;
            try {
                ObjectInputStream ois = new SerializedCache.CustomObjectInputStream(bis);
                Throwable var6 = null;

                try {
                    result = (Serializable)ois.readObject();
                } catch (Throwable var31) {
                    var6 = var31;
                    throw var31;
                } finally {
                    if (ois != null) {
                        if (var6 != null) {
                            try {
                                ois.close();
                            } catch (Throwable var30) {
                                var6.addSuppressed(var30);
                            }
                        } else {
                            ois.close();
                        }
                    }

                }
            } catch (Throwable var33) {
                var4 = var33;
                throw var33;
            } finally {
                if (bis != null) {
                    if (var4 != null) {
                        try {
                            bis.close();
                        } catch (Throwable var29) {
                            var4.addSuppressed(var29);
                        }
                    } else {
                        bis.close();
                    }
                }

            }

            return result;
        } catch (Exception var35) {
            throw new CacheException("Error deserializing object.  Cause: " + var35, var35);
        }
    }

    public static class CustomObjectInputStream extends ObjectInputStream {
        public CustomObjectInputStream(InputStream in) throws IOException {
            super(in);
        }

        protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
            return Resources.classForName(desc.getName());
        }
    }
}
SynchronizedCache

SynchronizedCache为每个方法添加synchronized,添加了同步功能

public class SynchronizedCache implements Cache {
    private final Cache delegate;

    public SynchronizedCache(Cache delegate) {
        this.delegate = delegate;
    }

    public String getId() {
        return this.delegate.getId();
    }

    public synchronized int getSize() {
        return this.delegate.getSize();
    }

    public synchronized void putObject(Object key, Object object) {
        this.delegate.putObject(key, object);
    }

    public synchronized Object getObject(Object key) {
        return this.delegate.getObject(key);
    }

    public synchronized Object removeObject(Object key) {
        return this.delegate.removeObject(key);
    }

    public synchronized void clear() {
        this.delegate.clear();
    }

    public int hashCode() {
        return this.delegate.hashCode();
    }

    public boolean equals(Object obj) {
        return this.delegate.equals(obj);
    }
}

CacheKey

在Cache中唯一确定一个缓存项使用的key为CacheKey

CacheKey的组成部分

  • MappedStatement的id
  • 指定查询结果集的范围,也就是RowBounds.offset和RowBounds.limit
  • 查询所使用的sql语句,boundSql.getSql(),包含?占位符
  • 用户传递的实际参数

CacheKey就是mappedStementId + offset + limit + SQL + queryParams + environment生成一个哈希码

public class CacheKey implements Cloneable, Serializable {
    private static final long serialVersionUID = 1146682552656046210L;
    public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
        public void update(Object object) {
            throw new CacheException("Not allowed to update a null cache key instance.");
        }

        public void updateAll(Object[] objects) {
            throw new CacheException("Not allowed to update a null cache key instance.");
        }
    };
    private static final int DEFAULT_MULTIPLIER = 37;
    private static final int DEFAULT_HASHCODE = 17;
  	// 参与计算hashcode,默认为37
    private final int multiplier;
  	// CacheKey的hashcode,默认为17
    private int hashcode;
  	//检验和
    private long checksum;
  	// updateList集合的个数
    private int count;
  	// 由该集合中的所有对象共同决定两个CacheKey是否相同
    private List<Object> updateList;

    public CacheKey() {
        this.hashcode = 17;
        this.multiplier = 37;
        this.count = 0;
        this.updateList = new ArrayList();
    }

    public CacheKey(Object[] objects) {
        this();
        this.updateAll(objects);
    }

    public int getUpdateCount() {
        return this.updateList.size();
    }
		// 向updateList中添加对象时,使用的是update方法
    public void update(Object object) {
        int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
        ++this.count;
        this.checksum += (long)baseHashCode;
        baseHashCode *= this.count;
        this.hashcode = this.multiplier * this.hashcode + baseHashCode;
        this.updateList.add(object);
    }

    public void updateAll(Object[] objects) {
        Object[] var2 = objects;
        int var3 = objects.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Object o = var2[var4];
            this.update(o);
        }

    }

  //  判断两个cacheKey是否相等
    public boolean equals(Object object) {
        if (this == object) {
            return true;
        } else if (!(object instanceof CacheKey)) {
            return false;
        } else {
            CacheKey cacheKey = (CacheKey)object;
            if (this.hashcode != cacheKey.hashcode) {
                return false;
            } else if (this.checksum != cacheKey.checksum) {
                return false;
            } else if (this.count != cacheKey.count) {
                return false;
            } else {
                for(int i = 0; i < this.updateList.size(); ++i) {
                    Object thisObject = this.updateList.get(i);
                    Object thatObject = cacheKey.updateList.get(i);
                    if (!ArrayUtil.equals(thisObject, thatObject)) {
                        return false;
                    }
                }

                return true;
            }
        }
    }

    public int hashCode() {
        return this.hashcode;
    }

    public String toString() {
        StringJoiner returnValue = new StringJoiner(":");
        returnValue.add(String.valueOf(this.hashcode));
        returnValue.add(String.valueOf(this.checksum));
        this.updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
        return returnValue.toString();
    }

    public CacheKey clone() throws CloneNotSupportedException {
        CacheKey clonedCacheKey = (CacheKey)super.clone();
        clonedCacheKey.updateList = new ArrayList(this.updateList);
        return clonedCacheKey;
    }
}

参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾光师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值