浏览 8626 次
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-01-27
时,发现自己对于hibernte cache存在一些理解误差,于是回去翻看了一遍hibernte的源代码,下面写出hibernate 的Query Cache部分的分析: 先看看QueryCache的源代码 引用 public void put(QueryKey key, Type[] returnTypes, List result, SessionImplementor session) throws HibernateException { if ( log.isDebugEnabled() ) log.debug("caching query results in region: " + regionName); List cacheable = new ArrayList( result.size()+1 ); cacheable.add( new Long( session.getTimestamp() ) ); 1) for ( int i=0; i<result.size(); i++ ) { if ( returnTypes.length==1 ) { cacheable.add( returnTypes[0].disassemble( result.get(i), session ) ); } else { cacheable.add( TypeFactory.disassemble( (Object[]) result.get(i), returnTypes, session ) ); } } queryCache.put(key, cacheable); 2) } 红色加亮的部分是核心部分,将查询结果集Result保存到一个List cacheable中,保存的对象是由各自Type desassemble出的对象。 代码中红色加亮的2)说明用一个QueryKey作为key将查询结果集保存到cache中。 值得说明的是,这里的cache是一个接口,通过这个良好设计的接口,hibernate很容易切换不同的cache 实现。 再来看看QueryKey的代码: public QueryKey(String queryString, QueryParameters queryParameters) { this.sqlQueryString = queryString; this.types = queryParameters.getPositionalParameterTypes(); this.values = queryParameters.getPositionalParameterValues(); RowSelection selection = queryParameters.getRowSelection(); if (selection!=null) { firstRow = selection.getFirstRow(); maxRows = selection.getMaxRows(); } else { firstRow = null; maxRows = null; } this.namedParameters = queryParameters.getNamedParameters(); } 可以看出,它包含了一个Query所有的信息。甚至包括firstRow,maxRow。 联想到QueryCache是二级缓存,那么它应该由SessionFactory维护,于是在SessionFactoryImpl中search "QueryCache", 找出如下 与 QueryCache相关的核心代码: if ( settings.isQueryCacheEnabled() ) { updateTimestampsCache = new UpdateTimestampsCache( settings.getCacheProvider(), properties ); [color=red]1)[/color] queryCache = new QueryCache( settings.getCacheProvider(), properties, updateTimestampsCache, null ); [color=red]2)[/color] queryCaches = Collections.synchronizedMap( new HashMap() ); } settings.isQueryCacheEnabled(),这个应该对应到hibernate.properties中的 use_query_cache true 属性。SessionFacotryImpl中维护着一个updateTimestampsCache实例变量,从1)中可以看出, 它的构造器需要一个CacheProvider 和一个props,这个props有cfg.getProperties()来,应该包含一些与 queryCache相关的配置信息。 我们看一下这个类UpdateTimestampsCache的源代码, UpdateTimestamplsCache的doc上清楚写道: 引用 Tracks the timestamps of the most recent updates to particular tables.
说明这个类是用来跟踪特定表的最近修改timestamp(时间戳)。它只有一个实例变量: private Cache updateTimestamps; 可以猜测这个变量是用来保存特定表的最近更新时间戳。 UpdateTimestampsCache有一个核心method: public synchronized boolean isUpToDate(Set spaces, Long timestamp) throws HibernateException { Iterator iter = spaces.iterator(); while ( iter.hasNext() ) { Serializable space = (Serializable) iter.next(); Long lastUpdate = (Long) updateTimestamps.get(space); [color=red]1)[/color] if ( lastUpdate==null ) { //the last update timestamp was lost from the cache //(or there were no updates since startup!) //updateTimestamps.put( space, new Long( updateTimestamps.nextTimestamp() ) ); //result = false; // safer } else { if ( lastUpdate.longValue() >= timestamp.longValue() ) return false; [color=red]2)[/color] } } return true; } 这个方法传入两个参数,一个是spaces,一个是需要比较的时间戳。对于第一个参数,从1)可以看出,它是取出需要跟参数2) 比较的时间戳的key,至于它具体的意思,我们可以看看另外一个与之相对应的method: public synchronized void preinvalidate(Serializable[] spaces) throws CacheException { //TODO: to handle concurrent writes correctly, this should return a Lock to the client Long ts = new Long( updateTimestamps.nextTimestamp() + updateTimestamps.getTimeout() ); [color=red]1;[/color] for ( int i=0; i<spaces.length; i++ ) updateTimestamps.put( spaces[i], ts ); //TODO: return new Lock(ts); } 很明显,cache的key是由外部caller传入的参数,具体的含义我们可以由caller处找出,这个等下再分析。 值得关注的是ts,它等于当前下一个时间戳+updateTimestamps的失效时间。 回过头来看isUpToDate(Set spaces, Long timestamp),含义非常清楚, 在传入的keys中,如果其中一个key对应的最后修改时间戳大于参数2,则返回false。否则,返回upToDate. 这个upToDate是对于caller传入的参数2而言,而不是updateTimestamps而言,这个method的命名也是 符合围绕调用者定义的原则的。(一开始,我也是被这个method 命名迷惑) 好了,分析完UpdateTimestampsCache之后,我们重新回到SessionFactoryImpl上的1), 这里还要强调一点,SessionFactory维护着updateTimestampsCache对象,而所有的QueryCache实例中的 updateTimestampsCache实例变量都是引用自这个变量,这点很重要,说明SessionFactory管理着一个 对应于全部QueryCache的全局的时间戳表。这点可以从SessionFactoryImpl的2)得到验证。 2)那里终于出现了期盼已久的QueryCache对象,我们回到文章开始,再重新分析QueryCache对象, 它有两个实例变量 private Cache queryCache; 1) private UpdateTimestampsCache updateTimestampsCache; 2) queryCache是用来保存[queryKey, Result]的,而updateTimestampsCache则是用来 决定当前缓存的查询结果集和是否是脏数据的判断标准。为此,我们仔细分析这个get方法: public List get(QueryKey key, Type[] returnTypes, Set spaces, SessionImplementor session) throws HibernateException { if ( log.isDebugEnabled() ) log.debug("checking cached query results in region: " + regionName); List cacheable = (List) queryCache.get(key); if (cacheable==null) { log.debug("query results were not found in cache"); return null; } List result = new ArrayList( cacheable.size()-1 ); Long timestamp = (Long) cacheable.get(0); [color=red]1)[/color]if ( !updateTimestampsCache.isUpToDate(spaces, timestamp) ) { log.debug("cached query results were not up to date"); return null; } log.debug("returning cached query results"); for ( int i=1; i<cacheable.size(); i++ ) { if ( returnTypes.length==1 ) { result.add( returnTypes[0].assemble( (Serializable) cacheable.get(i), session, null ) ); } else { result.add( TypeFactory.assemble( (Serializable[]) cacheable.get(i), returnTypes, session, null ) ); } } return result; } 红色部分1)说明这点。 我们再从queryCache调用者角度进一步分析queryCache在hibernate 中的交互行为。 在sessionImpl中search“queryCache”发现: private void executeAll(List list) throws HibernateException { final boolean lockQueryCache = factory.isQueryCacheEnabled(); int size = list.size(); for ( int i=0; i<size; i++ ) { Executable executable = (Executable) list.get(i); executions.add(executable); [color=red]1)[/color]if (lockQueryCache) factory.getUpdateTimestampsCache().preinvalidate( executable.getPropertySpaces() ); executable.execute(); } list.clear(); if ( batcher!=null ) batcher.executeBatch(); } 对于session中的操作,如果开启queryCache,则会更新SessionFactory的时间戳缓存。其中比较有意义的是:executable.getPropertySpaces()。 从UpdateTimestampsCache中可以看出,这个是作为保存最后更新时间戳的key,有必要看看他们到底是什么咚咚: 在ScheduledEntityAction中发现,它其实是调用ClassPersister 的getPropertySpaces()方法。 最后得出结论,通过Hibernate的sessionAPI调用的save,update,delete,都可以更新受影响的UpdateTimestampsCache。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |