Lucene的缓存机制和解决方案
概述
1、Filter Cache
2、field缓存
3、结论
4.LuceneBase缓存解决方案
概述
lucene的缓存可分为两类:filter cache和field cache。
filter cache的实现类为CachingWrapperFilter,用来缓存其他Filter的查询结果。
field cache的实现类是FieldCache,缓存用于排序的field的值。
简单来说,filter Cache用于查询缓存,field cache用于排序。
这两种缓存的生存周期都是在一个IndexReader实例内,因此提高Lucene查询性能的关键在于如何维护和使用同一个IndexReader(即IndexSearcher)。
Filter Cache
从严格意义上来说,lucene没有查询类似数据库服务器的数据高速缓存。lucene的Filter缓存实现类是CachingWrapperFilter,它缓存了查出来的bits。另外lucene还提供了FilterManager,一个单例对象,用来缓存Filter本身。
下面是CachingWrapperFilter的具体实现:
public class CachingWrapperFilter extends Filter {
protected Filter filter;
protected transient Map cache;//这是作为缓存使用的map
public CachingWrapperFilter(Filter filter) {
this.filter = filter;
}
public BitSet bits(IndexReader reader) throws IOException {
if (cache == null) {
cache = new WeakHashMap();//采用WeakHashMap实现,由JVM回收内存
}
synchronized (cache) { // check cache
BitSet cached = (BitSet) cache.get(reader);//key为IndexReader,value为BitSet,所以该缓存生存周期在一个IndexReader内
if (cached != null) {
return cached;
}
}
//若没有找到缓存,则重新读取
final BitSet bits = filter.bits(reader);
synchronized (cache) { // update cache
cache.put(reader, bits);
}
return bits;
}
在FilterManager里,采用Filter.hashCode()作为key的,所以使用的时候应该在自定义的Filter类中重载hashCode()方法。
例子:Filter filter=FilterManager.getInstance().getFilter(new CachingWrapperFilter(new MyFilter()));如果该filter已经存在,在FilterManager返回该Filter的缓存(带有bit缓存),否则返回本身(不带bit缓存的)。
FilterManager里有个定时线程,会定期清理缓存,以防造成内存溢出错误。
field缓存
field缓存是用来排序用的。lucene会将需要排序的字段都读到内存来进行排序,所占内存大小和文档数目相关。经常有人用lucene做排序出现内存溢出的问题,一般是因为每次查询都启动新的searcher实例进行查询,当并发大的时候,造成多个Searcher实例同时装载排序字段,引起内存溢出。
Field缓存的实现类是FieldCacheImpl,下面我们看看排序时怎么用到Field缓存的:
在IndexSearcher类里的方法,有关排序的查询都会调用到此方法:
public TopFieldDocs search(Weight weight, Filter filter, final int nDocs,Sort sort)throws IOException {
TopFieldDocCollector collector =
new TopFieldDocCollector(reader, sort, nDocs);//排序操作由TopFieldDocCollector实现
search(weight, filter, collector);//开始查询,查询结果回调Collector.collect()方法时实现排序
return (TopFieldDocs)collector.topDocs();//返回TopFieldDocs对象,这个对象和TopDocs的差异在于TopFieldDocs里包含排序字段的信息,包括字段名和字段值。其中TopFieldDocs中ScoreDoc[]的实例是FieldDoc[]
}
下面看看TopFieldDocCollector.collect()是怎么实现的:
public void collect(int doc, float score) {
if (score > 0.0f) {
totalHits++;
if (reusableFD == null)
reusableFD = new FieldDoc(doc, score);s
else {
reusableFD.score = score;
reusableFD.doc = doc;
}
reusableFD = (FieldDoc) hq.insertWithOverflow(reusableFD);//hq是FieldSortedHitQueue对象,一个PriorityQueue的子类,insertWithOverflow()实现一个固定大小的排序队列,排序靠后的对象被挤出队列
}
}
FieldSortedHitQueue是通过重载lessThan()方法来实现排序功能的:
*/
protected boolean lessThan (final Object a, final Object b) {
final ScoreDoc docA = (ScoreDoc) a;
final ScoreDoc docB = (ScoreDoc) b;
// run comparators
final int n = comparators.length;
int c = 0;
for (int i=0; i<n && c==0; ++i) {
c = (fields[i].reverse) ? comparators[i].compare (docB, docA)
: comparators[i].compare (docA, docB);//通过comparators[]来进行排序,我们剩下的任务就是看看这些comparator[]是怎么构造的,怎么使用的Fieldcache的
}
// avoid random sort order that could lead to duplicates (bug #31241):
if (c == 0)
return docA.doc > docB.doc;
return c > 0;
}
comparators实在FieldSortedHitQueue的构造函数里创建的:
public FieldSortedHitQueue (IndexReader reader, SortField[] fields, int size)throws IOException {
final int n = fields.length;
comparators = new ScoreDocComparator[n];
this.fields = new SortField[n];
for (int i=0; i<n; ++i) {
String fieldname = fields[i].getField();
comparators[i] = getCachedComparator (reader, fieldname, fields[i].getType(), fields[i].getLocale(), fields[i].getFactory());//调用getCachedComparator方法获得缓存的comparators,comparator是ScoreDocComparator的实例
if (comparators[i].sortType() == SortField.STRING) {
this.fields[i] = new SortField (fieldname, fields[i].getLocale(), fields[i].getReverse());
} else {
this.fields[i] = new SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
}
}
initialize (size);
}
下面看看getCachedComparator ()的实现:
static final FieldCacheImpl.Cache Comparators = new FieldCacheImpl.Cache(){
。。。
}
static ScoreDocComparator getCachedComparator (IndexReader reader, String field, int type, Locale locale, SortComparatorSource factory)throws IOException {
//以下两种不需要读取字段
if (type == SortField.DOC) return ScoreDocComparator.INDEXORDER;//按索引顺序排序
if (type == SortField.SCORE) return ScoreDocComparator.RELEVANCE;//按相关度排序
FieldCacheImpl.Entry entry = (factory != null)? new FieldCacheImpl.Entry (field, factory)
: new FieldCacheImpl.Entry (field, type, locale);
//其他类型的排序需要读取字段到缓存中
return (ScoreDocComparator)Comparators.get(reader, entry);//Comparators 是一个FieldCache的实例
}
Comparators.get()方法根据排序字段类型的不同,返回ScoreDocComparator的不同实现,下面我们看看String类型的实现,就可以知道什么时候调用fieldCache了:
static ScoreDocComparator comparatorString (final IndexReader reader, final String fieldname)
throws IOException {
final String field = fieldname.intern();
//下面代码读取缓存,得到字段值和文档id的对应关系,如果缓存不存在,则读取索引文件。缓存的生命周期是和IndexReader一样,所以不同查询使用同一个Searcher,可以保证排序缓存只有一个,不会出现内存溢出的问题
final FieldCache.StringIndex index = FieldCache.DEFAULT.getStringIndex (reader, field);
return new ScoreDocComparator () {
public final int compare (final ScoreDoc i, final ScoreDoc j) {
final int fi = index.order[i.doc];//index.order[]的值是按自定义字段的排序,数组的索引是lucene docid;可以看看getStringIndex的具体实现来看看这些值是怎么读进来的,这里就不详细说明了
final int fj = index.order[j.doc];
if (fi < fj) return -1;
if (fi > fj) return 1;
return 0;
}
public Comparable sortValue (final ScoreDoc i) {
return index.lookup[index.order[i.doc]];
}
public int sortType() {
return SortField.STRING;
}
};
}
结论
lucene使用上述的两个缓存机制已经能解决绝大部分的问题了。solr在lucene之上封装,又增加了另外的缓存,但应该说作用不太大,反而使代码变得很复杂了。
缓存解决方案
Lucene缓存的生存周期都是在一个IndexReader实例内,因此提高Lucene查询性能的关键在于如何维护和使用同一个IndexReader(即IndexSearcher)。
因此我们需要新写一个SingleIndexSearcher(源代码见下)类,该类继承IndexSearcher,作用为实现IndexSearcher的单例模式。
LuceneBase加入类SingleIndexSearcher并将IndexSearcher对象的生成都用SingleIndexSearcher. getInstance()方法。
缓存Filter用法:Filter filter = new CachingWrapperFilter(new FieldFilter(field, value));
或
Filter filter = FilterManager.getInstance().getFilter(new CachingWrapperFilter(new FieldFilter(field, value)));
/**
* IndexSearcher单例模式的实现 采取单例模式是要充分利用Lucene的缓存,同时防止多个IndexSearcher对象导致内存溢出和并发问题
*
* @author 路卫杰
* @version 1.0, 2010-8-4
* @see IndexSearcher
*/
public class SingleIndexSearcher extends IndexSearcher {
/** 私有静态SingleIndexSearcher对象 */
private static IndexSearcher instance;
static{
try {
instance = new SingleIndexSearcher(Configure.getProperties().getProperty("ZkAnalyzerPath"));
System.out.println("构造");
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 构造方法
*
* @param path
* 索引路径
* @throws IOException
* @throws CorruptIndexException
*/
public SingleIndexSearcher(String path) throws CorruptIndexException, IOException{
super(path);
}
/**
* 获得单例
*/
public static IndexSearcher getInstance() {
return instance;
}
}
搜索速度比较
搜索相同关键字和过滤器次数(次) 一般过滤器(ms) 缓存过滤器(ms) 缓存排序(ms)
1 2407 2438 2093
5 4750 2531 2219
10 8110 2672 2313
20 14750 2922 2593
50 34498 3672 3250
100 67546 4844 4407
分享到:
相关推荐
航班搜索引擎动态缓存策略研究,周超,林友芳,随着国内民航市场的快速发展,互联网平台的航班查询量越来越多。为减轻后台服务的查询压力,查询缓存策略逐渐成为重要的研究课题
搜索引擎-搜索引擎精简摘要缓存方法研究.pdf
电商项目:fastDFS 搜索引擎 三级缓存 秒杀模块 oauth 第三方登录 rabbitmq
Web搜索与Web缓存的若干关键问题研究.rar 基于Lucene的Web站内信息搜索系统.rar 基于多Agent的信息搜索引擎技术研究与应用.rar 基于多Agent的智能搜索引擎系统研究.rar 搜索引擎的研究与实现.rar 搜索引擎中的数据...
Web搜索与Web缓存的若干关键问题研究.rar 基于Lucene的Web站内信息搜索系统.rar 基于多Agent的信息搜索引擎技术研究与应用.rar 基于多Agent的智能搜索引擎系统研究.rar 搜索引擎的研究与实现.rar 搜索引擎中的数据...
Web搜索与Web缓存的若干关键问题研究.rar 基于Lucene的Web站内信息搜索系统.rar 基于多Agent的信息搜索引擎技术研究与应用.rar 基于多Agent的智能搜索引擎系统研究.rar 搜索引擎的研究与实现.rar 搜索引擎中的数据...
基于springboot框架开发,使用 mysql 数据库做数据交互,redis 做缓存,使用 Elasticsearch 分布式搜索引擎做全局搜索
对于实际搜索引擎所涉及的各种核心技术都有全面细致的介绍,除了作为搜索系统核心的网络爬虫、索引系统、排序系统、链接分析及用户分析外,还包括网页反作弊、缓存管理、网页去重技术等实际搜索引擎必须关注的技术,...
元搜索引擎(MetaSearch)的特点是把多个独立搜索引擎的搜索结果整合、...后台数据库采用Microsoft SQL Server,静态化搜索系统设计采用XML数据岛缓存搜索结果提高系统的稳定性和性能、节省服务器资源减轻系统负担。
7.3.5 搜索页面的索引缓存与更新 190 7.4 实现关键词高亮显示 191 7.5 实现多维视图 194 7.6 实现相似文档搜索 200 7.7 实现AJAX自动完成 203 7.7.1 总体结构 203 7.7.2 服务器端处理 203 7.7.3 浏览器端处理 205 ...
更重要的是,Solr 创建的索引与 Lucene 搜索引擎库完全兼容。 通过对 Solr 进行适当的配置, 某些情况下可能需要进行编码,Solr 可以阅读和使用构建到其他 Lucene 应用程序中的索引。此 外,很多 Lucene 工具(如 Nutch、...
1.此系统综合百度,sogua,tom三方面的音乐搜索引擎,实现音乐天下搜,相信没有找不到的...4,搜索引擎采用缓存技术,真正实现快速搜索,降低服务器占用率。5,同时此系统可以与音乐站结合使用,打造音乐站超级引擎。
1、搜索引擎技术发展史 2、搜索引擎爬虫 3、搜索引擎索引系统 4、搜索引擎分词 5、索引压缩 6、链接分析 7、搜索引擎反作弊 8、用户意图分析 9、搜索引擎缓存系统 10、SEO与数据分析
针对移动Widget无法脱机访问或网络延迟现象严重问题,提出了一种基于HTML 5.0技术的移动Widget引擎内容缓存模型,将网络端数据进行智能的下载和存储,通过使用HTML 5.0技术,合理地安排各种移动Widget应用的网络数据...
MyEngine 是 zxing 使用 MyDream框架开发的一个 搜索引擎小偷程序。你可以免费使用该程序以及底层开发框架,并可以对其进行修改和二次发布,但二次发布必须仍然携带...5 对搜索结果页面启用浏览器缓存减小服务器压力。
上传即可用百度网盘搜索引擎网站源码,此为php网站源码,上传后在后台更新缓存即可使用。内容自动采集更新。
lucene.net及.net爬虫实现的简单搜索引擎,为缓解读写速度与爬取速度不匹配,使用redis缓存数据库作为中间件来实现缓存机制
ChatGPT智能搜索引擎的性能优化和调优可以从以下几个方面入手: 1. 数据处理:对数据进行处理和优化,包括数据清洗、数据压缩、数据分片等。可以利用现有的大数据处理工具,例如Hadoop、Spark等,进行数据处理和优化...
PHP100视频教程30:PHP模板引擎Smarty缓存应用.rar
owllook使用了mongodb储存了用户使用过程中的产生的基本信息,诸如注册信息、搜索小说信息、收藏小说数据等,对于某些必要的缓存,则利用redis进行缓存处理,如小说缓存、session缓存,注意,对于限制数据:都将在24...