Mybatis的缓存

缓存实现

Mybatis的缓存实现应用了装饰器模式,基础缓存为PerpetualCache,其余的缓存类都在装饰PerpetualCache来实现不同的功能,下面图是部分实现

1. PerpetualCache

这个缓存比较简单,使用Map来达到缓存的目的。下边是他的源码,比较简单,只贴出了部分

public class PerpetualCache implements Cache {
    // 缓存id
	private final String id;	
    // 存储缓存内容
    private final Map<Object, Object> cache = new HashMap<>();
}
2. 装饰器
2.1 FifoCache

先进先出式缓存,在PerpetualCache的基础上添加了一个队列集合来实现

插入缓存时:

  • 将缓存的key放入到keyList中,检查keyList的长度是否越界(越界移除掉队列头元素,并移除delegate中的kv)
  • 将kv插入到delegate中
public class FifoCache implements Cache {
    // PerpetualCache
    private final Cache delegate;
  	// 存放delegate中的key集合实现先进先出
  	private final Deque<Object> keyList;
    // keyList的大小
	private int size;
    
    public FifoCache(Cache delegate) {
    	this.delegate = delegate;
        // 用LinkedList做队列使用
   		this.keyList = new LinkedList<>();
        // 设置keyList的最大值为1024
	    this.size = 1024;
	}
    
    // 添加缓存
    public void putObject(Object key, Object value) {
   		cycleKeyList(key);
		delegate.putObject(key, value);
  	}
    
    // 对key的操作
    private void cycleKeyList(Object key) {
        // 添加到队列尾
    	keyList.addLast(key);
	    if (keyList.size() > size) {
    		// 若keyList的size超过最大容量时,移除队列头元素
		    Object oldestKey = keyList.removeFirst();
	    	// 移除delegate中的key
	    	delegate.removeObject(oldestKey);
        }
    }
}
2.2 LruCache

最近最少使用缓存,通过重写LinkedHashMapremoveEldestEntry()来实现

public class LruCache implements Cache {
    // PerpetualCache
    private final Cache delegate;
    // 用于维护最近最少使用key
  	private Map<Object, Object> keyMap;
    // 需要移除的key(也就是最近最少使用的key)
  	private Object eldestKey;
    
    public LruCache(Cache delegate) {
   		this.delegate = delegate;
		setSize(1024);
	}
    
    public void setSize(final int size) {
        // 维护LinkedHashMap的访问顺序,这里弄懂需要跟一下LinkedListMap的put源码
		keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
			private static final long serialVersionUID = 4267176411845948333L;
	        @Override
      		protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
                boolean tooBig = size() > size;
        		if (tooBig) {
                    // 当长度越界后,返回LinkedList的头结点的key
          			eldestKey = eldest.getKey();
        		}
        		return tooBig;
      		}
    	};
  	}
    
  	public void putObject(Object key, Object value) {
    	delegate.putObject(key, value);
    	cycleKeyList(key);
  	}
    
    private void cycleKeyList(Object key) {
        // 将key存入keyMap
    	keyMap.put(key, key);
    	if (eldestKey != null) {
            // delegate中移除eldestKey并将eldestKey置null
      		delegate.removeObject(eldestKey);
      		eldestKey = null;
    	}
  	}
}
2.3 BlockingCache

阻塞式缓存,使用ReentrantLock来实现的,同一时刻只有一个线程可以key的缓存

线程A访问缓存时,

  • 通过**getObject()**方法加锁,当缓存命中,返回值不为null(释放锁)
  • 当缓存未命中,返回null(不释放锁),mybatis查询数据库,将结果放入缓存(putObject()方法),此时释放锁

线程A操作缓存时,线程B进入获取和A相同的key时,只能等待

public class BlockingCache implements Cache {
	// 超时时间
	private long timeout;
  	// PerpetualCache
  	private final Cache delegate;
  	// 存储锁
  	private final ConcurrentHashMap<Object, ReentrantLock> locks;

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

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

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

  	public void putObject(Object key, Object value) {
    	try {
            // 查询缓存
            delegate.putObject(key, value);
    	} finally {
      		// 释放锁
      		releaseLock(key);
    	}
  	}
    
    public Object getObject(Object key) {
        // 尝试加锁
        acquireLock(key);
        Object value = delegate.getObject(key);
        if (value != null) {
          	// value不为null时才会释放锁
          	releaseLock(key);
        }
        return value;
  	}

    public Object removeObject(Object key) {
      // despite of its name, this method is called only to release locks
    	releaseLock(key);
        return null;
    }

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

  	private ReentrantLock getLockForKey(Object key) {
    	// 获取锁对象
    	return locks.computeIfAbsent(key, k -> new ReentrantLock());
  	}

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

    private void releaseLock(Object key) {
        ReentrantLock lock = locks.get(key);
        if (lock.isHeldByCurrentThread()) {
    	    // 当锁被当前线程持有才会释放
        	lock.unlock();
        }
    }

  	public long getTimeout() {return timeout;}
  	public void setTimeout(long timeout) {this.timeout = timeout;}
}
CacheKey

CacheKey主要用来充当Mybatis一二级缓存(PerpetualCache存储)的key,value肯定为数据库的返回结果,这点毋容置疑。那么key的选择会麻烦一些,需要考虑到方法id、参数列表、是否分页、执行的SQL等因素。所以有了CacheKey

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;
    // 默认hash
    private static final int DEFAULT_HASHCODE = 17;
    // 实际乘子
    private final int multiplier;
    // 实际hash
    private int hashcode;
    // 校验和
    private long checksum;
    // 影响因子个数
    private int count;
    // 影响因子(像方法id,参数列表这些都是影响因子)
    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();
    }
	
    // 更新操作(目的是为了hashcode更随机,因为这是需要作为map的key的)
    public void update(Object object) {
        // 获取基础hashcode
        int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
        // count自增1
        ++this.count;
        // 校验和更新
        this.checksum += (long)baseHashCode;
        // 基础hash更新
        baseHashCode *= this.count;
        // 实际hash更新
        this.hashcode = this.multiplier * this.hashcode + baseHashCode;
        // 添加影响因子到列表
        this.updateList.add(object);
    }

    // 将objects逐一放入到udateList中
    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);
        }

    }

    // 因为要作为缓存的key,所以重写了hash和equals
    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) {
                // 比较hash
                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;
    }
}
一级缓存

Mybatis的一级缓存是使用PerpetualCache来做的,key为CacheKey,value为数据库返回的结果集。会在INSERT|UPDATE|DELETE、事务提交、回滚时清空

public abstract class BaseExecutor implements Executor {
	// 一级缓存
    protected PerpetualCache localCache;
    
    // 查询方法
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 获取绑定SQL
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 获取cacheKey
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        // 调用重载,真正逻辑
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
    
    // 返回cacheKey对象
    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            CacheKey cacheKey = new CacheKey();
            // 传入ms的id:com.xyn.mapper.Rolemapper.getRole
            cacheKey.update(ms.getId());
            // 分页参数
            cacheKey.update(rowBounds.getOffset());
            cacheKey.update(rowBounds.getLimit());
            // sql
            cacheKey.update(boundSql.getSql());
            // 参数列表
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
            Iterator var8 = parameterMappings.iterator();

            while(var8.hasNext()) {
                ParameterMapping parameterMapping = (ParameterMapping)var8.next();
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
					// 传入运行时参数:#{xx}
                    cacheKey.update(value);
                }
            }

            if (this.configuration.getEnvironment() != null) {
                // 存在环境变量时传入环境变量
                cacheKey.update(this.configuration.getEnvironment().getId());
            }

            return cacheKey;
        }
    }
    
    // 真正的查询业务逻辑
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            // 若查询栈深度为1 且 ms是否清空缓存(根据ms类型决定,select类型为false,其余为true)
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                // 清空一级缓存
                this.clearLocalCache();
            }

            List list;
            try {
                // 栈深度自增
                ++this.queryStack;
                // 从一级缓存中取,若为null,则去数据库查询
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    // 存储过程逻辑
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    // 从数据库中取
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                // 栈深度自减
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                for (DeferredLoad deferredLoad : deferredLoads) {
        			deferredLoad.load();
      			}
      		// issue #601
      		deferredLoads.clear();
                // 检查本地缓存使用范围,若为(STATEMENT)则清空localCache
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }
    
    // 从数据库查询
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // TODO 插入占位符 没搞太懂这个操作
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            // 移除占位符
            this.localCache.removeObject(key);
        }

        // 查询结果放入到localCache中
        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            // 若为存储过程,放入到localOutputParameterCache中
            this.localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }
二级缓存

TODO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值