场景
有个业务比较长的接口,会对某张模板表做反复查询,接口执行时间比较长有1-2秒,需要优化,查看后台日志,同样的查询sql在反复执行。而且都是lambda的getById接口,所以初步朝lambda不支持缓存方向排查(结果不是)
排查思路
从getById起看源码链路,最终找到缓存的关键源码在BaseExecutor里
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 (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 1 主要取缓存的逻辑在这里,key是一个很长的字符串拼接,和参数值、mapperId、sql、beanId有关
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list); // 2 每次执行后会把值放到缓存里
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
通过给1的地方打断点发现,每隔一段时间cache的size会变成0,估计是被清空了,给cache的clear打了个断点,进去后看了下调用堆栈,清空逻辑在
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
return doUpdate(ms, parameter);
}
这里没有指定key,是全部清空,所以即使更新的是A表数据,B表也会清空。
解决方案
解决方案计划有两个
1 写一个类似spring cache的功能在service层做会话级缓存(微服务场景,全局缓存不行)
2 重新baseExecutor的query方法,在mybatis层面上加标签注明表,只刷新同表数据