一.倒排索引
倒排索引的概念,感兴趣的可以点击查看<<ES权威指南中关于倒排索引的解释>>
也可以查看elasticsearch简介和倒排序索引介绍这篇文章,写的很清晰。
1.1 字段和倒排索引的关系
首先,在es中,我们可以把一个doc(文档)理解为数据库中的一行数据,每个doc对应多个field(字段),例如:
PUT /employee/group/1
{
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
PUT /employee/group/2
{
"age" : 32,
"about" : "I like to collect rock albums",
"interests": [ "music" ]
}
es中,会在_source中将数据存一份,然后,写入数据的同时,自动生成mapping信息,根据mapping信息,生成字段对应的倒排索引文件。
自动生成的mapping如下:
{
"employee": {
"mappings": {
"group": {
"properties": {
"about": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"age": {
"type": "long"
},
"interests": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
按照字段,分别生成倒排索引文件,然后检索字段的时候,搜索字段对应的倒排索引文件即可,如下:
age字段:
age字段因为是long类型,整个字段无需分词(term,token),直接按字段值做倒排索引。
term词条 | docID |
25 | 1 |
32 | 2 |
about字段本身为text类型,支持分词,about.keyword多域字段为keyword类型,按整词作为term词条,所以产生about,about.keyword两个字段的倒排索引文件。
term词条 | docID |
i | 1,2 |
love | 1 |
to | 1,2 |
go | 1 |
rock | 1,2 |
climbing | 1 |
like | 2 |
collect | 2 |
albums | 2 |
term词条 | docID |
I love to go rock climbing | 1 |
I like to collect rock albums | 2 |
ES检索过程:
1.根据检索字段,如果是text类型的,按照写入索引时的分词机制,进行查询关键字分词,
2.查询字段对应的倒排索引文件,查询term分词,得到对应的docID
3.根据docID,获得doc,显示查询结果。
提问:
1.about.keyword的作用,换而言之,keyword数据类型和text数据类型的区别?
"about":"I love to go rock climbing"
对上面的about字段,keyword数据类型只支持精确搜索,因为keyword建立的索引文件的term,是全词建立索引,没有分词,所以,对于检索 about:"i",如果格式为keyword将返回null,无搜索结果。如果格式为text,则分词后建立索引,搜索 about:"i",有结果,因为有I 的term,由此可见,分词机制对搜索影响很大,因为term只能完全匹配,才会得到term对应的文档。
倒排索引的不足
1.倒排索引,按分词建立,建立后不可修改。
ES针对修改倒排索引,使用的是再合并策略:对修改的数据建立新的,小的索引文件,记录时间,新的索引文件和旧的索引文件合并,修改的部分,按时间最新覆盖。
2.倒排索引可以很快的通过分词,得到包含分词的文档。但是,如果我们已知文档,想得到文档拥有哪些分词,需要遍历整个倒排索引文件,影响效率。
解决办法是引入doc_values,doc_values是文档对应的分词形成的索引文件。
3.倒排索引十分依赖分词机制,分词机制不好,则搜索结果不理想
ES提供很多分词机制,也支持自定义分词机制。当然,如果不需要分词,只需要精准匹配,直接建立keyword类型数据即可。
二.doc_values 文档值
2.1. doc_values是什么
doc_values是elasticsearch中,对于倒排索引的部分缺点进行部分补充的功能。查看dov_values官方解释
倒排索引很方便单项查看 分词对应的文档,但是不利于反向查看,文档所有的分词。doc_values可以理解为反向的倒排索引,
doc_values存放的是文档对应的分词
2.1 doc_values的应用场景
上面说doc_values 是为了满足反向查询倒排索引而建立的。那么,什么样的情况我们需要用到doc_values呢。大致说来,聚合,排序,都会用到doc_values,
首先,我们进行一次聚合。
//增加一条记录
PUT /employee/group/3
{
"age" : 32,
"about" : "I like to read book",
"interests": [ "music","books" ]
}
//查询abount中有i的,按age聚合,统计文档数
GET /employee/_search
{
"query" : {
"match" : {
"about" : "i"
}
},
"aggs" : {
"res": {
"terms" : {
"field" : "age"
}
}
}
}
这条语句主要是为了分析,每个数据出现的次数,例如,music有多少人喜欢,sports有多少人喜欢。结果如下:
{
"aggregations": {
"res": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": 32, //age=32的有两个文档
"doc_count": 2
},
{
"key": 25, //age=25的有一个文档
"doc_count": 1
}
]
}
}
}
将上面的查询聚合过程分解:
1.执行query,查询about中包含i这个term的doc。得到文档1,2,3
2.得到文档1,2,3后,我们分析1,2,3的age字段,判断每个age的有多少人。
在第2步中,如果我们只有倒排索引文件,我们需要遍历整个倒排索引,去获取,1,2,3文档的age索引文件,按照age索引文件的term(age是数值类型,整词形成倒排索引,不进行分词),即age的值,然后合并统计文档数。
但是如果我们有doc_values之后,就很方便统计了。
doc_values是和倒排索引同时建立的。同样按索引/类型/字段区分索引文件,doc_value生成数据为:
文档ID | 包含的term(不分词的term,即字段对应的值) |
1 | 25 |
2 | 32 |
3 | 32 |
有了doc_values的查询聚合过程如下:
1.执行query,查询about中包含i这个term的doc。得到文档1,2,3
2.得到文档1,2,3后,查询age对应的doc_values索引文件,将age合并,计数,得到25->1,32->2,返回结果。
搜索和聚合是相互紧密缠绕的。搜索使用倒排索引查找文档,聚合操作收集和聚合 doc values 里的数据。
注意:
doc_values对不分词的字段默认开启,包括about.keyword这类多域字段,对分词字段默认关闭,即about字段
#分词字段# 从数据类型来看,text默认进行分词,keyword,long,int,double,复杂数据类型等默认不进行分词
刚才的描述中,经常强调doc_values存放的是 文档对应的分词数,这句话是容易引起歧义的,但是又是正确的,如果,该字段不允许分词,则该字段的值,就是该文档在这个索引文件的唯一分词(即term),对于能分词的字段,doc_values是默认关闭,但是可以手动开启,这个时候,doc_values存放的是文档对应的分词数 ,这句话就是正确的。
所以,对于doc_values的用途,ES官网的说法是:
Doc values 不仅可以用于聚合。 任何需要查找某个文档包含的值的操作都必须使用它。 除了聚合,还包括排序,访问字段值的脚本,父子关系处理,参见 父-子关系文档
#为什么对分词字段关闭# 没找到官方解释 、个人看法:对分词字段,开启doc_values,产生的索引文件占用磁盘过大,处理的时候,消耗cpu资源,
doc_values的优缺点
1.doc_values 提高了聚合,排序,所有针对字段值的操作的效率,
2.doc_values加大了磁盘空间,但是ES提供了doc_values的压缩算法,对doc_values进行了大量压缩。而且,用不到可以禁用,节省空间。
三.Fielddata
3.1 fielddata是什么
因为doc_values不针对分词字段,那么,如果一定要对分词字段的term分词进行聚合,该怎么办呢?
在es中,对分词的field,直接执行聚合操作,会报错,
{
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [about] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
}
大概意思是说,你必须要打开fielddata,然后将正排索引数据加载到内存中,才可以对分词的field执行聚合操作,而且会消耗很大的内存
fielddata就是满足该需求的,但是,因为过于消耗资源,所以默认关闭。
fielddata结构和doc_values相同,区别是.fielddata运算时在内存中生成,纯内存计算,doc_values放在磁盘中即可
有需求时,在mapping中打开fielddata开关。默认false关闭。
doc_values和fielddata详细说明可以查看博客 elasticsearch的Doc Values 和 Fielddata
四.store存储
字段独立于_source,单独存储,
注:_source中依然会存储该字段
优点: ES中,所有数据统一存在_source中,如果只是查询一个小字段,却要把_source中的整条数据显示后,再从_source中取出对应的字段,浪费资源。
缺点: 加大磁盘使用。
应用场景: 如果_source很大,频繁使用的字段很小,则应该store该字段,这样不需要每次都将_source加载完后,在查找该字段。