php直播源码,一级缓存源码分析
php直播源码,一级缓存源码分析
本小节将对一级缓存对应的Mybatis源码进行讨论。在Mybatis源码-Executor的执行过程中已经知道,禁用二级缓存的情况下,执行查询操作时,调用链如下所示。
在BaseExecutor中有两个重载的query()方法,下面先看第一个query()方法的实现,如下所示。
@Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 获取Sql语句 BoundSql boundSql = ms.getBoundSql(parameter); // 生成CacheKey CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // 调用重载的query()方法 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }
在上述query()方法中,先会在MappedStatement中获取SQL语句,然后生成CacheKey,这个CacheKey实际就是本会话一级缓存中缓存的唯一标识,CacheKey类图如下所示。
CacheKey中的multiplier,hashcode,checksum,count和updateList字段用于判断CacheKey之间是否相等,这些字段会在CacheKey的构造函数中进行初始化,如下所示。
public CacheKey() { this.hashcode = DEFAULT_HASHCODE; this.multiplier = DEFAULT_MULTIPLIER; this.count = 0; this.updateList = new ArrayList<>(); }
同时hashcode,checksum,count和updateList字段会在CacheKey的update()方法中被更新,如下所示。
public void update(Object object) { int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); count++; checksum += baseHashCode; baseHashCode *= count; hashcode = multiplier * hashcode + baseHashCode; updateList.add(object); }
主要逻辑就是基于update()方法的入参计算并更新hashcode,checksum和count的值,然后再将入参添加到updateList集合中。同时,在CacheKey重写的equals()方法中,只有当hashcode相等,checksum相等,count相等,以及updateList集合中的元素也全都相等时,才算做两个CacheKey是相等。
回到上述的BaseExecutor中的query()方法,在其中会调用createCacheKey()方法生成CacheKey,其部分源码如下所示。
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { ...... // 创建CacheKey CacheKey cacheKey = new CacheKey(); // 基于MappedStatement的id更新CacheKey cacheKey.update(ms.getId()); // 基于RowBounds的offset更新CacheKey cacheKey.update(rowBounds.getOffset()); // 基于RowBounds的limit更新CacheKey cacheKey.update(rowBounds.getLimit()); // 基于Sql语句更新CacheKey cacheKey.update(boundSql.getSql()); ...... // 基于查询参数更新CacheKey cacheKey.update(value); ...... // 基于Environment的id更新CacheKey cacheKey.update(configuration.getEnvironment().getId()); return cacheKey; }
所以可以得出结论,判断CacheKey是否相等的依据就是MappedStatement id + RowBounds offset + RowBounds limit + SQL + Parameter + Environment id相等。
获取到CacheKey后,会调用BaseExecutor中重载的query()方法,如下所示。
@Override 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."); } // queryStack是BaseExecutor的成员变量 // queryStack主要用于递归调用query()方法时防止一级缓存被清空 if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; // 先从一级缓存中根据CacheKey命中查询结果 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { // 处理存储过程相关逻辑 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 未命中,则直接查数据库 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (BaseExecutor.DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); // 如果一级缓存作用范围是STATEMENT时,每次query()执行完毕就需要清空一级缓存 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } return list; }
上述query()方法中,会先根据CacheKey去缓存中命中查询结果,如果命中到查询结果并且映射文件中CURD标签上的statementType为CALLABLE,则会先在handleLocallyCachedOutputParameters()方法中处理存储过程相关逻辑然后再将命中的查询结果返回,如果未命中到查询结果,则会直接查询数据库。上述query()方法中还使用到了BaseExecutor的queryStack字段,主要防止一级缓存作用范围是STATEMENT并且还存在递归调用query()方法时,在递归尚未终止时就将一级缓存删除,如果不存在递归调用,那么一级缓存作用范围是STATEMENT时,每次查询结束后,都会清空缓存。下面看一下BaseExecutor中的一级缓存localCache,其实际是PerpetualCache,类图如下所示。
所以PerpetualCache的内部主要是基于一个Map(实际为HashMap)用于数据存储。现在回到上面的BaseExecutor的query()方法中,如果没有在一级缓存中命中查询结果,则会直接查询数据库,queryFromDatabase()方法如下所示。
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 { // 调用doQuery()进行查询操作 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } // 将查询结果添加到一级缓存中 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } // 返回查询结果 return list; }
queryFromDatabase()方法中和一级缓存相关的逻辑就是在查询完数据库后,会将查询结果以CacheKey作为唯一标识缓存到一级缓存中。
Mybatis中如果是执行增,改和删操作,并且在禁用二级缓存的情况下,均会调用到BaseExecutor的update()方法,如下所示。
@Override 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); }
所以Mybatis中的一级缓存在执行了增,改和删操作后,会被清空即失效。
最后,一级缓存的使用流程可以用下图进行概括。
以上就是php直播源码,一级缓存源码分析, 更多内容欢迎关注之后的文章