在Lucene4.x以后,出现一个重大的特性,就是索引支持DocValues,这对于广大的solr和elasticsearch用户,无疑来讲是一个福音,这玩意的出现经过牺牲必定的磁盘空间带来的好处主要有两个: 前端
DocValues实际上是Lucene在构建索引时,会额外创建一个有序的基于document => field value的映射列表; 算法
基于lucene的solr和es都是使用经典的倒排索引模式来达到快速检索的目的,简单的说就是创建 搜索词=》 文档id列表 这样的关系映射,
而后在搜索时,经过相似hash算法,来快速定位到一个搜索关键词,而后读取其的文档id集合,这就是倒排索引的核心思想,这样搜索数据
是很是高效快速的,固然它也是有缺陷的,假如咱们须要对数据作一些聚合操做,好比排序,分组时,lucene内部会遍历提取全部出如今文档集合
的排序字段而后再次构建一个最终的排好序的文档集合list,这个步骤的过程所有维持在内存中操做,并且若是排序数据量巨大的话,很是容易就形成solr内存溢出和性能缓慢。
基于这个缘由,在lucene4.x以后出现了docvalue这个新特性,在构建索引时会对开启docvalues的字段,额外构建一个已经排好序的文档到字段级别的一个列式存储映射,它减轻了在排序和分组时,对内存的依赖,并且大大提高了这个过程的性能,固然它也会耗费的必定的磁盘空间。 数组
经过上面的剖析,散仙相信你们已经对DocValues有一个初步的了解了,至于它的应用场景,那么也很是明显了,总结起来主要如下几个方面:
1,须要聚合的字段,包括sort,agg,group,facet等
2,须要提供函数查询的字段
3,须要高亮的字段,这个确实能加速,可是不建议把高亮放在服务端程序作,建议放在前端实现,不容易出错并且整体性能比服务端高
4,须要参与自定义评分的字段,这个稍复杂,大多数人的场景中,不必定能用到,后面会单独写一篇文章介绍。
对于不须要参与上面任何一项的字段,能够选择关闭docvalues,这样能够节省必定的磁盘空间. session
在lucene的枚举类DocValuesType 中,咱们能够看见它声明了六个常量:
1, NONE 不开启docvalue时的状态
2, NUMERIC 单个数值类型的docvalue主要包括(int,long,float,double)
3, BINARY 二进制类型值对应不一样的codes最大值可能超过32766字节,
4, SORTED 有序增量字节存储,仅仅存储不一样部分的值和偏移量指针,值必须小于等于32766字节
5, SORTED_NUMERIC 存储数值类型的有序数组列表
6, SORTED_SET 能够存储多值域的docvalue值,但返回时,仅仅只能返回多值域的第一个docvalue
一般有四种docvalue存储场景:
A: 字符串或UUID字段+单值 会选择SORTED做为docvalue存储
B: 字符串或UUID字段+多值 会选择SORTED_SET做为docvalue存储
C:数值或日期或枚举字段+单值 会选择NUMERIC 做为docvalue存储
D:数值或日期或枚举字段+多值 会选择SORTED_SET做为docvalue存储
注意,分词字段存储docvalue是没有意义的 dom
说完了概念方面的东西,下面来点实例的例子,来看下如何给索引加上docsvalue,只要加上docvalues后,排序,分组,聚合的时候
会自动使用docvalue提速,因此咱们关注的重点是如何激活docvalue。
1,在原生Lucene中使用DocValues,这个稍麻烦,须要自定义组装,由于lucene是核心算法包,因此封装程度并非很高,正是
因为这样,理解了lucene以后,再理解solr和elasticsearch是很是easy的。
下面是在lucene中存储docvalue例子,一个是string类型,一个是数值类型,分词类型在这里没有意义,再也不说起: elasticsearch
//数值存储例子 FieldType num=new FieldType(); num.setStored(true);//设置存储 num.setIndexOptions(IndexOptions.DOCS);//设置索引类型 num.setNumericType(NumericType.DOUBLE);//数值类型 num.setDocValuesType(DocValuesType.NUMERIC);//DocValue类型 Document doc=new Document(); //添加string字段 doc.add(new SortedDocValuesField("id",new BytesRef("01011"))); //添加数值类型的字段 Float,Doule须要额外转成bit位才能存储,Interger和Long则不须要 doc.add(new DoubleField("price", Double.doubleToRawLongBits(25.258), num));
如何读取: 函数
//读取索引文件 DirectoryReader reader=DirectoryReader.open(FSDirectory.open(Paths.get(indexDir))); //若是有多个段须要merge成一个,获取第一个进行测试,本例中仅仅就有一个段 SortedDocValues str = DocValues.getSorted(reader.leaves().get(0).reader(), "id"); //数值类型 NumericDocValues db = DocValues.getNumeric(reader.leaves().get(0).reader(), "price"); //读取字符串类型的ByteRef而后打印其内容 System.out.println("id:"+str.get(0).utf8ToString()); //注意此处,要与类型对应,若是是Float,则须要Float.intBitsToFloat((int)db.get(0))进行位数还原 System.out.println("price: "+Double.longBitsToDouble(db.get(0))); reader.close();
2,在Solr中docvalue默认是所有关闭,比较严谨,你们可酌情开启 性能
<fieldname="easy_money"type="double"indexed="true"stored="true"docValues="true" />
3,在ElasticSearch中,默认docvalue所有激活,比较简单暴力,你们可酌情关闭一些不须要使用docvalue的字段,以节省磁盘空间 测试
"session_id":{"type":"string","index":"not_analyzed","doc_values":false}
做者:random-walk
来源:CSDN
原文:https://blog.csdn.net/asdfsadfasdfsa/article/details/71576750
版权声明:本文为博主原创文章,转载请附上博文连接!.net