1. 正排索引
正排索引: 正排索引是指文档ID为key,表中记录每个关键词出现的次数,查找时扫描表中的每个文档中字的信息,直到找到所有包含查询关键字的文档。
网页A中的内容片段:
Tom is a boy. Tom is a student too.
正排索引如下:
内容 | Tom | is | a | boy | Tom | is | a | student | too |
分词 | Tom | is | a | boy | student | too |
- 正排索引
Index | Word | word_hit(词出现的次数) | word_offset(词出现的位置) |
TA | Tom | 2 | 1,5 |
is | 2 | 2,6 | |
a | 2 | 3,7 | |
boy | 1 | 4 | |
student | 1 | 8 | |
too | 1 | 9 |
出现的位置进行差分序列
Index | word | word_hit(词出现的次数) | word_offset(词出现的位置) |
docid | Tom | 2 | 1,4 |
is | 2 | 2,4 | |
a | 2 | 3,4 | |
boy | 1 | 4 | |
student | 1 | 8 | |
too | 1 | 9 |
- 根据位置还原字符串
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Tom | Tom | |||||||
is | is | |||||||
a | a | |||||||
boy | ||||||||
student | ||||||||
too | ||||||||
从上面的介绍可以看出,正排是以 docid 作为索引的,但是在搜索的时候我们基本上都是用关键词来搜索。
所以,试想一下,我们搜一个关键字(Tom),当100个网页的10个网页含有Tom这个关键字。但是由于是正排是doc id 作为索引的,所以我们不得不把100个网页都扫描一遍,然后找出其中含有Tom的10个网页。然后再进行rank,sort等。效率就比较低了。尤其当现在网络上的网页数已经远远超过亿这个数量后,这种方式现在并不适合作为搜索的依赖。
不过与之相比的是,正排这种模式容易维护。由于是采用doc 作为key来存储的,所以新增网页的时候,只要在末尾新增一个key,然后把词、词出现的频率和位置信息分析完成后就可以使用了。 所有正排的优点是:易维护;缺点是搜索的耗时太长;
2. 生成时间
正排索引在PUT/POST操作的时候就会生成。
3.核心原理
正排索引,也会写入磁盘文件中,然后会进入os cache中进行缓存,以提升正排索引的性能。如果os cache内存大小不足够放得下整个正排索引,就会将正排索引的数据写入磁盘文件中去。
4. 性能问题
ES官方建议,ES是大量基于os cache来进行缓存和提升性能的,不建议使用jvm内存来进行缓存,那样会导致一定的GC开销和OOM问题,所以应该给jvm更少的内存,给os cache更大的内存,这样可以大容量的os cache可以提升doc value和倒排索引的缓存和查询效率。 一般64G内存的服务器的,最多给jvm16G内存,将剩余的几十G都给os cache。
5. 存储压缩
正排索引的value在保存之前会做相应的处理,然后再来保存进去正排索引,以节省空间。具体策略如下:
(1)所有值相同,直接保留单值。
(2)少于256个值,使用table encoding模式的压缩方式处理。
(3)大于256个值,看有没有最大公约数,有就除以最大公约数,然后保留这个最大公约数和商。
(4)如果没有最大公约数,采取offset节后压缩的方式。
6. 禁用
如果的确不需要doc value,比如聚合等操作,那么可以禁用,减少磁盘空间占用。
{ "mappings": { "my_type": { "properties": { "my_field": { "type": "keyword" "doc_values": false } } } } }
7. 使用
聚合分析使用了倒排索引+正排索引(doc value)。
只使用倒排索引存在的问题:
如存在数据如下:
id | province | latency | timestamp |
1 | 江苏 | 68 | 2016-10-28 |
2 | 江苏 | 76 | 2016-10-29 |
3 | 江苏 | 83 | 2016-10-29 |
4 | 新疆 | 654 | 2016-10-28 |
5 | 江苏 | 105 | 2016-10-29 |
6 | 新疆 | 101 | 2016-10-28 |
7 | 新疆 | 302 | 2016-10-29 |
8 | 江苏 | 76 | 2016-10-28 |
进行如下聚合分析: 查询province为江苏的所有latency的分组数量。查询参数如下:
{ "size":0, "query": { "match": { "province": "江苏" } }, "aggs": { "group_by_province": { "terms": { "field": "latency" } } } }
如果只使用倒排索引进行查询,则查找过程如下: 江苏 -> 1,2,3,5,8 新疆 -> 4,6,7
第一步:根据倒排索引province字段查询,得到了其对应的文档为1,2,3,5,8。
第二步:根据latency字段的值进行分组。但是这时候我们只知道我们要找的文档为1,2,3,5,8。但是文档1,2,3,5,8当中有哪些latency,我们是无法得到的。此时只能扫描整个倒排索引,根据latency的值获取到对应的文档,看本文档是否在1,2,3,5,8之中,
使用倒排索引+正排索引 首先使用倒排索引进行查询,则查找过程如下: 江苏 -> 1,2,3,5,8 新疆 -> 4,6,7
第一步:根据倒排索引province字段查询,得到了其对应的文档为1,2,3,5,8。
正排索引如下: doc1 ->江苏 68 2016-10-28 doc2 ->江苏 76 2016-10-29 ...... 这时候
第二步就不需要扫描整个倒排索引,直接使用正排索引,在文档1,2,3,5,8之中查询统计即可。