lucene filter器群组及其缓存大盘点

lucene中的filter其实并不起眼,你们对其对性能的影响也不是很关注,但实际上filter是除了单纯搜索之外,其余搜索附加功能的必选组件,其性能很大程度上会直接影响搜索的性能,以前我一直认为filter的性能比query高,但事实说明并不彻底如此(这里所说的负荷是指io消耗并非cpu),实际上在lucene中充满着各类io流,也就是说不少东西都没法从根本上保存,这也给缓存带来了很大难度(这个问题看似简单,可是在超复杂的组合查询下,缓存可能会几乎无用,缘由就是key怎么把握)

首先来看看filter的接口定义:
public abstract class Filter implements java.io.Serializable {
public abstract BitSet bits(IndexReader reader) throws IOException;
} 前端

简单明了从reader中知道哪些记录是能够读出来的用true false放在bitsets中,而后再用这去和总集合作and操做获得剩余记录数,而后再经过query查询.原理知道了,下面来考虑下它的缓存:
缓存filter自己,因为他是序列化对象,那么已经具有了缓存的条件,可是这是一个错误,由于你缓存了这个类,而当你把参数reader拿出来依然会和机器产生io,所以这是极其不恰当的方法,应该缓存它的结果.
在lucene中有这么几个和filter有关的类:
CachingWrapperFilter
CachingSpanFilter
RemoteCachingWrapperFilter
FilterManager java

其实我想质疑前两个,为何呢,请看他的源码:
protected transient Map cache; nginx

他放置缓存的map竟然是transient的,这意味着即便你把它实例在static中这个变量依然会每次要new的,这样的缓存有意义吗?我看不出他怎么缓存的
/**
* A transient Filter cache. To cache Filters even when using {@link  org.apache.lucene.search.RemoteSearchable} use
{@link  org.apache.lucene.search.RemoteCachingWrapperFilter} instead.
*/
上面这句注释总算明了了,呵呵.
那么其实RemoteCachingWrapperFilter才是真正的cache类,他的实现借助于filterManager,这个类是咱们平时能理解的那种cache结构
public BitSet bits(IndexReader reader) throws IOException {
Filter cachedFilter = FilterManager.getInstance().getFilter(filter);
return cachedFilter.bits(reader);
} 数据库

但这个还不够,第一他的性能我内心没谱,遇到上万的访问怎么办?因此仍是要用第三方的缓存,我使用的是memcached,这个东西不介绍了,只有一个问题,就是必需要求对象是可序列化的,这个不难理解,要想网络传输只能治么搞.
个人缓存策略:把最细胞的filter用memached缓存他的结果集,而他的组合fliter用自带的filtermanager管理就行了. apache

而我这样的道理也是基于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确定是不同的,放一块儿也是这样的结果,这样也没有很差,当一台机器出现问题或者须要更新代码能够用时间差来保证负荷平稳过渡,不像之前一台机器每次重启都是有点怕怕的,只能找空闲时间才敢这么作. app

最后要讲的query,其实前面我说了半天没有提到query,query的缓存呢? 其实在lucene中有这么个类:
QueryFilter
这个类简单说就是把query变成filter,那干什么呢?很简单啊,这样任何查询都会变成filter的,因此全部的缓存都是filter!那么从缓存中取出来的filterquery怎么用? memcached

MatchAllDocsQuery matchAll = new MatchAllDocsQuery(); result = isearch.search(matchAll, filter, sort); filter是用个人合成filter组合的,这样消耗就更低了,固然不建议无限制增长系统负荷,由于那样就几乎没法重启了,呵呵.好了基本说到这里,其实最后我想说个人核心思想: 任何query都是filter,lucene就是filter查询,事实是如此的,呵呵.

相关文章
相关标签/搜索