知识点
相关文章
更多最近更新
更多lucene的缓存机制分析
2019-03-27 00:59|来源: 网路
转自:http://www.360doc.com/content/13/0411/22/11869636_277679623.shtml
lucene的缓存可分为两类:filter cache和field cache。filter cache的实现类为CachingWrapperFilter,用来缓存其他Filter的查询结果。field cache的实现类是FieldCache,缓存用于排序的field的值。简单来说,filter Cache用于查询缓存,field cache用于排序。这两种缓存的生存周期都是在一个IndexReader实例内,因此提高Lucene查询性能的关键在于如何维护和使用同一个IndexReader(即IndexSearcher)。
1、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里有个定时线程,会定期清理缓存,以防造成内存溢出错误。
2、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);
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;
}
};
}
3、结论
lucene使用上述的两个缓存机制已经能解决绝大部分的问题了。solr在lucene之上封装,又增加了另外的缓存,但应该说作用不太大,反而使代码变得很复杂了。
转自:http://www.cnblogs.com/zjw520/archive/2013/04/11/3015551
相关问答
更多-
JAVA 缓存机制[2022-02-12]
你这个分数太少了吧,程序到是有,不过给你有点可惜 CacheMgr.java import java.util.*; import cn.javass.framework.cache.vo.CacheConfModel; public class CacheMgr { private static Map cacheMap = new HashMap(); private static Map cacheConfMap = new HashMap(); private CacheMgr(){ } priva ... -
Redis是什么缓存机制[2022-01-17]
redis(Remote Dictionary Server)远程数据服务 内存高速缓存数据库。C语言编写,数据模型为key-value,NoSql数据库。 希望对你有所启发。apeit-程序猿IT中redis章节讲的不错,由浅入深,适合入门学习。 -
lucene怎么用[2022-07-03]
Lucene是一个全文检索系统框架,开源的。 用起来比较方便,去Lucene的官网上下一个包并导入到你的工程中就可以调用包里面的类了。 一般的书里面介绍的版本都是1.4.3或者稍微高级一点的,不过现在lucene3.0正式发布,一些函数调用方法已经改变了,你可以下载一个版本低一点的Lucene比较适合学习~ 当然你直接从3.0入手的话网上资料也是非常丰富的~ -
hibernate的缓存机制是什么?[2022-05-03]
Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句, ... -
java 缓存机制 实现的原理?[2023-08-03]
所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例。这样做可以减少系统开销,提高系统效率。缓存机制的实现有很多中,这里讲一种。 public class CacheImmutale{ //声明要缓存的类名; private final String className; //声明10个缓存池 private static CacheImmutale[] cache= new CachImmutale[10]; //记录缓存的位置,最新位置为[pos-1 ... -
面试中问到HIBERNATE的缓存机制请问下该怎么回答[2022-05-09]
这是面试中经常问到的一个问题,楼主可以按照我的思路回答,准你回答得很完美,首先说下Hibernate缓存的作用(即为什么要用缓存机制),然后再具体说说Hibernate中缓存的分类情况, 最后可以举个具体的例子。 Hibernate缓存的作用: Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据 Hibernate ... -
我很害怕Lucene的工作方式并非如此。 Lucene希望您在其字段中构建包含未分析值的文档,并在将文档放入索引时对其进行分析。 Hibernate Search负责设置正确的配置,以便Lucene知道每个字段使用哪个分析器。 碰巧这很容易配置标准的@Field字段( @Field(analyzer = ...) ),但不适用于字段桥中添加的字段。 目前,最简单的解决方案将是本博文中描述的第三个解决方案:分析器鉴别器。 这不是分析仪鉴别器的预期目的,但它可以工作。 基本上你必须: 像往常一样使用@Analy ...
-
Lucene分析仪的比较(Comparison of Lucene Analyzers)[2022-09-08]
一般来说,Lucene中的任何分析器都是tokenizer + stemmer + stop-words过滤器。 令牌将文本分割成块,由于不同的分析器可能会使用不同的记号器,因此可以获得不同的输出令牌流 ,即文本块的顺序。 例如,您提到的KeywordAnalyzer 不会将文本分解 ,并将所有字段作为单个令牌。 与此同时, StandardAnalyzer (和其他大多数分析仪)使用空格和标点符号作为分割点。 例如,对于“我很高兴”这个短语,它会产生一个列表[“我”,“我”,“非常”,“快乐”](或类似的 ... -
Lucene评分机制(Lucene Scoring mechanism)[2023-08-17]
这完全正常,并不代表您的代码中的错误。 当索引的内容发生更改时,分数可能会发生变化,即使这些更改似乎与您的特定查询没有多大关系。 分数实际上仅在特定搜索执行的上下文中有效,因此它们的绝对值实际上并不重要,但是这些值相对于查询的其他结果是有意义的。 在两个结果集中,前两个得分相等,另一个得分明显较低。 这里改变的主要原因是idf(逆文档频率)评分因子。 这意味着在整个索引中不那么频繁地使用较重的术语,这种想法认为像“the”这样的常用术语作为搜索结果不像“geronimo”那样不那么常见。 在你的情况下,你的 ... -
最后我得到了它的工作: public final class StandardAnalyzerV36 extends Analyzer { public static final CharArraySet STOP_WORDS_SET = StopAnalyzer.ENGLISH_STOP_WORDS_SET; @Override protected TokenStreamComponents createComponents(String fieldName) { ...