lucene FieldCache 内部细节和使用方式分享
来源:互联网 发布:iphone6s没有4g网络 编辑:程序博客网 时间:2024/05/20 16:42
以下主要以使用的lucene 3.5为例:
一,FieldCache是什么?
FieldCache其实就是一个字段域值的一个缓存,外层是一些Map,它的底层数据结构就是一个数组,数组的下标就是docId, 值就是docId对应的Field value.可以快速完成docId到Field value的映射。
二,FieldCache怎么使用以及使用场景是什么?
1)使用方式:
比如Field的数据类型为Int,那么就可以通过如下方式来使用:
int[] fieldValues=FieldCache.DEFAULT.getInts(IndexReader reader, String field);
或者
int[] fieldValues=FieldCache.DEFAULT.getInts(IndexReader reader, String field, IntParser parser)
或者
int[] fieldValues=FieldCache.DEFAULT.getInts(IndexReader reader, String field, IntParser parser, boolean setDocsWithField)
第一个参数就是IndexReader,第二个参数就是field的名称,第三个参数parser,它可以把值转换成对于的数据类型,第四个参数setDocsWithField是否记录存在这个Field的docID。
2)使用场景:
其实使用场景有很多,在我们的代码里可以随时使用,这里举例几种比较典型的使用场景
1,用SortField排序时,Lunene会使用这个字段对应的FieldCache
2,solr在做facet时也会使用
3,在自定义的Collector里,使用详情可以参考mandy内部的YHDCollector
三,FieldCache的内部实现细节
1,从结构层次上看,FieldCacheImpl是一个三层的Map实现。
a , 第一层Map是一个HashMap,根据数据类型来划分,有9种类型的cache,根据字段类型不同选择不同的cache. Key为数据类型,value为Cache类的子类实例, Cache类里包含了第二层的Map。
private synchronized void init() {
caches = new HashMap<Class<?>,Cache>(9);
caches.put(Byte.TYPE, new ByteCache(this));
caches.put(Short.TYPE, new ShortCache(this));
caches.put(Integer.TYPE, new IntCache(this));
caches.put(Float.TYPE, new FloatCache(this));
caches.put(Long.TYPE, new LongCache(this));
caches.put(Double.TYPE, new DoubleCache(this));
caches.put(String.class, new StringCache(this));
caches.put(StringIndex.class, new StringIndexCache(this));
caches.put(DocsWithFieldCache.class, new DocsWithFieldCache(this));
}
b, 第二层的Map是一个WeakHashMap,按照IndexReader来划分,不同的IndexReader实例对应不同的cache,,key为IndexReader实例, value为第三层的map。
c,第三层的Map是一个HashMap,按照字段来划分,以Entry为key, Entry由FieldName和parser组成,value就是一个数组或者FixedBitSet。
2, 解析实现细节:
1)先来看一下Cache类的定义,主要包含这几个部分:
A, final Map<Object,Map<Entry,Object>>readerCache = new WeakHashMap<Object,Map<Entry,Object>>();
内部定义一个WeakHashMap,这个作为第二层的map,以IndexReader为key,以第三层的hashMap作为value.
B,protectedabstract Object createValue(IndexReader reader, Entry key,boolean setDocsWithField)
throws IOException;
一个抽象方法,9种不同数据类型的子类cache都必须实现这个方法,用于获取真正的Field value,来填充字段对应的数组或者FixedBitSet.
C, put(IndexReader reader, Entry key, Object value)
用于构建整个Cache体系,创建第三层的innerCache,然后把参数key和value放入innerCache中,最后把参数reader和innerCache放入到第二层的weakHashMap中
D,get(IndexReader reader, Entry key, boolean setDocsWithField)
根据reader和Entry(entry由fieldName和parser组成)来获取field对应的数组或者FixedBitSet,如果不存在,通过调用子类的实现的createValue方法获取,并放入各层map中。setDocsWithField是否记录存在这个Field的docID集合
E,purge(IndexReader r) 如果IndexReader已经完成或者关闭了,就会调用该方法,用于清理weakHashMap中IndexReader对应的value
2) 看一下Cache抽象方法createValue(IndexReader reader, Entry key,boolean setDocsWithField)在子类中的实现,以子类IntCache为例:
finalint maxDoc = reader.maxDoc(); //获取reader对应的maxDoc,这个用于定义数组的长度或者FixedBitSet的大小
TermDocs termDocs = reader.termDocs();//获取reader对应termDocs,不断循环迭代获取所有的docId
TermEnum termEnum = reader.terms (new Term (field));//获取reader对应的TermEnum,不断循环迭代获取所有的field的值
通过不断的循环迭代,构建出最后填充好的数组或者FixedBitSet,完成docId到Field value的映射.
3) 看一下parser, 如果是一个数字类型的Field,使用时要注意,举例来说,
如果采用老的方式,doc.add(new Field("stock_"+i,, Integer.toString(product.getStock()), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
加载FieldCache时就用DEFAULT_INT_PARSER,inBrandsValues = FieldCache.DEFAULT.getInts(reader, "brand_id",FieldCache.DEFAULT_INT_PARSER, missingValue != null);
如果是用doc.add(new NumericField("stock_"+i, Field.Store.NO, true).setIntValue(0)); 加载时FieldCache如果使用DEFAULT_INT_PARSER就会报错,
必须使用FieldCache.NUMERIC_UTILS_INT_PARSER,因为NumericField 在索引的时候是经过特殊编码的。或者直接使用FieldCache.DEFAULT.getInts(reader, "brand_id"),不加parser.
原因如下,在createValue方法中有如下代码,如果没有传入parser,那么默认采用DEFAULT_INT_PARSER,发生NumberFormatException异常后会采用NUMERIC_UTILS_INT_PARSER.
if (parser ==null) {
try {
returnwrapper.getInts(reader, field, DEFAULT_INT_PARSER, setDocsWithField);
} catch (NumberFormatException ne) {
returnwrapper.getInts(reader, field,NUMERIC_UTILS_INT_PARSER, setDocsWithField);
}
}
建议我们在加载FieldCache时,可以直接使用FieldCache.DEFAULT.getInts(reader, "brand_id")方式,不要传任何的parser,肯定不会报错。
4) 第二层Map是一个的WeakHashMap,内部实现采用的是WeakReference,这并不会阻止垃圾回收器对其内部数据的回收,垃圾回收器可能在任何时候对其数据进行回收,如果回收掉了,
下次再使用时就必须重新加载,Lucene采用WeakHashMap也是考虑到内存占用的问题。其实FieldCache加载时需要遍历所有的doc,有一定的性能消耗,需要做一个权衡。
5) 再来看一下FixedBitSet
FixedBitSet是一个固定长度为reader.maxDoc的BitSet,非常的节省空间,相当于同样大小的int数组的1/32,上面第9种cache是DocsWithFieldCache,内部就是使用了fixedBitSet,在有两种情况可以使用:
1,如果我们并不需要直接使用和获取FieldCache中Field value,只是需要判断doc是否存在这个字段时,可以采用FixedBitSet.
public Bits getDocsWithField(IndexReader reader, String field)
例如判断有无库存时,是可以采用这种方式的。
2,在使用SortField排序时,如果设置了missingValue,内部实现也是一个FixedBitSet.
SortField stockSort = new SortField("stock_" +req.getProvinceId(),SortField.INT,true).setMissingValue(1)
意思是如果doc没有这个字段,采用默认值1参与排序。
四,使用过程中注意点:
1,FieldCache的字段必须是被索引的,是否保存没有关系。
2,FieldCache的字段必须是单值域,也就意味着创建Field必须选择Field.Index.NOT_ANALYZED或者Field.Index.NOT_ANALYZED_NO_NORMS
如果选择的是Field.Index.ANALYZED或者Field.Index.ANALYZED_NO_NORMS,这个Field value有可能被分词成多个Term,
FieldCache加载时只会加载每个Field value分词之后的最后那个Term,这个时候加载的数据是不正确的。有的Field虽然不可能被分词,但是尽量还是不要选择
Field.Index.ANALYZED或者Field.Index.ANALYZED_NO_NORMS。
3,如果字段是存在多个同名域,加载FieldCache也会不正确。
doc.add(new Field("id", String.valueOf(i), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
doc.add(new Field("id", String.valueOf(i+1), Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
FieldCache加载的是同名域的后面那个Field的value.
4,lucene4.2以下版本加载FieldCache时,尽量避免传入不存在的FieldName,因为即使传入一个不存在的Field,也会创建一个数组,占用同样多的内存。
5,调用FieldCache,不要传入最外层的IndexReader,最好传入都是SegmentReader,如果既有外层的IndexReader被传入,又有内层的SegmentReader被传入,相当于占用了两份内存。
1)在Collector和FieldComparator中类存在下面的方法,这个方法里的参数是SegmentReader,可以在这个方法里加载需要的FieldCache.
setNextReader(IndexReader reader, int docBase)
2) 如果拿到的是外层的IndexReader的话,可以先获取内部的subReaders,然后判断是否为SegmentReader,直到获取到SegmentReader,再加载FieldCache.
Lucene4.2及以上版本,FieldCache实现大体是一样的,增加了从docValues中获取FieldCache,对不存在的FieldName做了特殊处理等等!
- lucene FieldCache 内部细节和使用方式分享
- Lucene FieldCache的使用
- Lucene系列-FieldCache
- Lucene系列-Fieldcache
- FieldCache在lucene中使用的代码解析
- Android ListView OnItemLongClick和OnItemClick事件内部细节分享
- SDWebImage基本使用和内部细节
- FieldCache在lucene中使用的代码解析,使用场景个人分析
- FieldCache在lucene中使用的代码解析,使用场景个人分析
- lucene的FieldCache与内存管理
- boost any类库的使用和内部实现细节
- Android ListView OnItemLongClick和OnItemClick事件内部细节分享以及几个比较特别的属性
- 关于Lucene的详细说明和操作使用方式
- 关于Lucene的详细说明和操作使用方式
- Lucene入门和使用
- lucene详解和实际应用分享
- 使用internal内部访问方式
- NSTimer使用和细节
- Excel导出
- 课程练习二-1014 A strange lift
- C++第四次实验——九九乘法表
- 归并排序 递归和非递归
- C++实验四-求1000以内所有偶数的和
- lucene FieldCache 内部细节和使用方式分享
- java中调用声明没实现方法体的类完全运行正常
- iOS企业账号申请
- 【hdu5538】【2015ACM/ICPC亚洲区长春站 】House Building 题意&题解&代码
- hdu1166 敌兵布-线段树的简单应用-查询更改
- Hadoop日志
- c++第4次作业
- toLocaleString的好玩的地方
- C#基础知识之抽象类