某某某 2008-07-04
首先来看看filter的接口定义:
publicabstractclassFilterimplementsjava.io.Serializable{
publicabstractBitSetbits(IndexReaderreader)throwsIOException;
}
简单明了从reader中知道哪些记录是可以读出来的用truefalse放在bitsets中,然后再用这去和总集合做and操作得到剩余记录数,然后再通过query查询.原理知道了,下面来考虑下它的缓存:
缓存filter本身,由于他是序列化对象,那么已经具备了缓存的条件,但是这是一个错误,因为你缓存了这个类,而当你把参数reader拿出来依然会和机器产生io,因此这是极其不恰当的方法,应该缓存它的结果.
在lucene中有这么几个和filter有关的类:
CachingWrapperFilter
CachingSpanFilter
RemoteCachingWrapperFilter
FilterManager
其实我想质疑前两个,为什么呢,请看他的源码:
protectedtransientMapcache;
他放置缓存的map居然是transient的,这意味着即使你把它实例在static中这个变量依然会每次要new的,这样的缓存有意义吗?我看不出他怎么缓存的
/**
*AtransientFiltercache.TocacheFiltersevenwhenusing{@linkorg.apache.lucene.search.RemoteSearchable}use
*{@linkorg.apache.lucene.search.RemoteCachingWrapperFilter}instead.
*/
上面这句注释总算明了了,呵呵.
那么其实RemoteCachingWrapperFilter才是真正的cache类,他的实现借助于filterManager,这个类是我们平时能理解的那种cache结构
publicBitSetbits(IndexReaderreader)throwsIOException{
FiltercachedFilter=FilterManager.getInstance().getFilter(filter);
returncachedFilter.bits(reader);
}
但这个还不够,第一他的性能我心里没谱,遇到上万的访问怎么办?所以还是要用第三方的缓存,我使用的是memcached,这个东西不介绍了,只有一个问题,就是必须要求对象是可序列化的,这个不难理解,要想网络传输只能治么搞.
我的缓存策略:把最细胞的filter用memached缓存他的结果集,而他的组合fliter用自带的filtermanager管理就好了.filter怎么合在一起上次写过一个,看这里:http://www.edwardpro.com/post/572/
而我这样的道理也是基于filtermanager的key是reader的hashcode,因此他是对应不同的索引的.那么肯定有朋友问怎么刷新呢?太简单了啊,你的key只要有reader或者search的hashcode就可以了,你一旦更新的源hashcode就变化了.(如果你的search和reader的hash不是固定的那么你肯定承受不了100以上的并行访问,io会高得惊人.)
另外一个技巧,是关于rangefilter的,这个东西不错,但是有一点难,在哪里呢?因为他的查询似乎效率不高,因此一定要缓存!但是key呢?比如我常用的key是timestamp,但是实际中就会发现如果用毫秒的timestamp那么key几乎无用,因为很少相同的,经过改进,我把时间可以用月做单位,查询也是如此,如果你的要求高我觉得做到天就ok了,如果你数据再多用到小时肯定也够了吧,这样filter的缓存会带来极大的性能提升.
那么实际效果呢,在原来使用时候2台集群机(nginx作为前端代理,后部用resin作为应用服务器)io平均1.xx现在加了缓存之后常年保持在0.2左右!性能得到了几乎5~6倍的提升.而一般查询一个十万当量的+5个关键字+3个filter时间大约是<10ms非命中时大约是70~80ms这个速度如果得到同样结果的数据库至少要放大1000倍的时间.
由于我memcached没有做集群是独立的(事实也应该如此,因为你两台机器的reader的hashcode肯定是不一样的,放一起也是这样的结果,这样也没有不好,当一台机器出现问题或者需要更新代码可以用时间差来保证负荷平稳过渡,不像以前一台机器每次重启都是有点怕怕的,只能找空闲时间才敢这么做.
最后要讲的query,其实前面我说了半天没有提到query,query的缓存呢?其实在lucene中有这么个类:
QueryFilter
这个类简单说就是把query变成filter,那干什么呢?很简单啊,这样任何查询都会变成filter的,所以所有的缓存都是filter!那么从缓存中取出来的filterquery怎么用?
MatchAllDocsQuerymatchAll=newMatchAllDocsQuery();
result=isearch.search(matchAll,filter,sort);
filter是用我的合成filter组合的,这样消耗就更低了,当然不建议无限制增加系统负荷,因为那样就几乎无法重启了,呵呵.好了基本说到这里,其实最后我想说我的核心思想:任何query都是filter,lucene就是filter查询,事实是如此的,当然这点可能还可以讨论下,因为我用filter比较依赖.
大家有什么其他方案也可以讨论和交流一下,呵呵.