前言

我们通过前面两篇文章的学习,基本解es,但还不足以应对我们平时的开发任务,因此我们还需要全面深入的学习es技术。本篇文章会讲述很多底层内核级原理,所以我们需要集中精力深入体会。

后台分词 es es 分词原理_搜索

一 ES 内核级原理及相关概念

1.1 分词器原理&介绍

它指把一段语句,拆分成单个的单词。同时对每个单词进行normalization (时态转换,单复数转换)处理,以提升recall召回率(搜索的时候,增加能够搜索到的结果的数量)。

1.1.1 分词器的主要构成

大部分的分词器都是有三块组成 预处理 、分词 、 过滤处理:

  1. character filter:在一段文本进行分词之前,先进行预处理,比如说最常见的就是,过滤html标签(<span>hello<span> --> hello),& --> and(I&you --> I and you)
  2. tokenizer:分词,hello you and me --> hello, you, and, me
  3. token filter:lowercase,stop word,synonymom,dogs --> dog,liked --> like,Tom --> tom,a/the/an --> 干掉,mother --> mom,small --> little

分词器,很重要,将一段文本进行各种处理,最后处理好的结果才会拿去建立倒排索引。

1.1.2 内置分词器的介绍

通过一个例句简单了解下:

例句:Set the shape to semi-transparent by calling set_trans(5)

  1. standard analyzer:set, the, shape, to, semi, transparent, by, calling, set_trans, 5(默认的是standard)
  2. simple analyzer:set, the, shape, to, semi, transparent, by, calling, set, trans
  3. whitespace analyzer:Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
  4. language analyzer(特定的语言的分词器,比如说,english,英语分词器):set, shape, semi, transpar, call, set_tran, 5

1.2 倒排索引介绍

通过一个例子体会下:

doc1:I really liked my small dogs, and I think my mom also liked them.

doc2:He never liked any dogs, so I hope that my mom will not expect me to liked him.

1.2.1 简单的倒排索引:

word

doc1

doc2

*

*

really

*

 

liked

*

*

my

*

*

small

*

 

dogs

*

 

and

*

 

think

*

 

mom

*

 

also

*

 

them

*

 

He

 

*

never

 

*

any

 

*

so

 

*

hope

 

*

that

 

*

will

 

*

not

 

*

expect

 

*

me

 

*

to  

 

*

him

 

*

搜索  mother like little dog,不可能有任何结果。因为我们分词后是这样的:mother  like  little  dog

所以我们还需要 normalization 下,建立倒排索引的时候,也就是说对拆分出的各个单词进行相应的处理,以提升后面搜索的时候能够搜索到相关联的文档的概率。

因此我们要进行 :时态的转换,单复数的转换,同义词的转换,大小写的转换

mom —> mother

liked —> like

small —> little

dogs —> dog

1.2.2 normalization 后的倒排索引

word

doc1

doc2

normalization

*

*

 

really

*

 

 

liked

*

*

liked --> like

my

*

*

 

small

*

 

 small --> little

dogs

*

 

dogs --> dog

and

*

 

 

think

*

 

 

mom

*

 

 

also

*

 

 

them

*

 

 

He

 

*

 

never

 

*

 

any

 

*

 

so

 

*

 

hope

 

*

 

that

 

*

 

will

 

*

 

not

 

*

 

expect

 

*

 

me

 

*

 

to  

 

*

 

him

 

*

 

这样我们就能搜索到相关的文档了。

1.2.3 倒排索引的数据结构

倒排索引他主要是适合用于进行搜索的,因此倒排索引的结构是:

  1. 包含这个关键词的doc list
  2. 包含这个关键词的所有doc的数量:IDF(inverse document frequency)
  3. 这个关键词在每个doc中出现的次数:TF(term frequency)
  4. 这个关键词在这个doc中的次序
  5. 每个doc的长度:length norm
  6. 包含这个关键词的所有doc的平均长度

倒排索引不可变的好处

(1)不需要锁,提升并发能力,避免锁的问题

(2)数据不变,一直保存在os cache中,只要cache内存足够

(3)filter cache一直驻留在内存,因为数据不变

(4)可以压缩,节省cpu和io开销

倒排索引不可变的坏处

每次都要重新构建整个索引

1.3 doc写入(增删改)原理

ES为了实现进实时搜索,在写入doc 时利用了Buffer(内存),OS Cache(系统缓存,属于系统内存的一部分),Disk(磁盘)三种存储方式,尽可能的提升搜索的能力。ES的底层是lucene实现的,而在luncene中一个index会被分为若干个数据段(segment),每一个segment都会存放index的部分doc。从流程上讲,ES会先把一个index中的doc分散存储在若干个shard(指的是主分片)上,在每个shard中又使用若干个segment来存储具体的数据。

1.3.1 写入原理

ES写入数据的流程大致如下:

 

后台分词 es es 分词原理_倒排索引_02

                                                   注意该图是简化版的写入流程

客户端发起请求(增、删、改)到ES中。

ES将本次请求要操作的doc写入到buffer中。ES为了保证搜索的近实时(Near Real Time 简称 NRT),默认每秒刷新一次buffer,这个刷新时间间隔可以手动修改,也可以通过命令触发buffer的刷新。建议刷新时间间隔设置在1秒左右,好处使在服务器宕机后,只会丢失1秒左右的数据。当然了,如果Buffer中没有任何数据,则不会执行refresh操作(总不能创建空文件吧)

POST /index_name/_refresh
PUT index_name
{
 "settings": {
   "number_of_shards": 5,
   "number_of_replicas": 1,
   "refresh_interval": "1s"
 }
}

1.3.2 可靠写入原理

ES可靠写入数据的流程大致如下:

 

后台分词 es es 分词原理_后台分词 es_03

ES在将doc写入到buffer的同时,也会将内容写入到translog文件中,这份文件存在的意义在于即便ES宕机了,也能尽可能的减少丢失的数据(简单来说,就是把translog中的记录重新执行一遍)。当然translog也不能保证数据绝对不丢失。由于translog存储在磁盘Disk(日志文件)中,因此为了提高访问效率,ES与translog文件之间会建立并保持一个长连接(不然每次访问都要获取和释放文件流)。

translog 如何恢复数据:

在系统重启后,ES会重新读取磁盘Disk中保存的数据(一份份的 index segment文件)到系统缓存中,然后接着读取translog中的操作日志并逐条执行,以此来达到恢复数据的目的。

注意:translog 文件的持久有两种方式默认是per request fsync 每次客户端写入请求被持久化以后,才会回应200(同时后台也会异步每5s持久化一次)

这种安全保障是以牺牲写入吞吐量来换取的(这也是为什么升级到5.x后会发现写入吞吐量下降),所以应该怎么选择要看业务需求。 由于一般线上会配置1个或更多复制片,即使采用异步fsync模式,当某个结点真的掉电translog丢失了部分数据的时候,复制片会被promote成主片,而它的数据是完整的,数据依然安全。 只有同一个shard主副分片所在机器同时掉电才可能丢失部分数据。

所以在日志应用场景,一般用户允许极端情况丢失数据,我们就采用async方式持久化translog,可以换取巨大的吞吐量提升。 而在某些业务搜索的场景,一般数据量级很小,如果对于写入速度要求不高,那么可以采用默认的per request方式,保证极端情况下的数据安全。

PUT index_name
{
 "settings": {
   "number_of_shards": 5,
   "number_of_replicas": 1,
   "index.translog.durability" : "async",
   "index.translog.sync_interval" : "5s"
 }
}

1.3.3  translog flush 原理

随着时间的推移,translog文件会不断的增大,在内存中积压的数量众多的index segment file的文件流也在不断的增大,OS Cache中积压的数据也越来越大,当translog文件大到一定程度或默认30分钟执行一次,ES会自动触发commit操作(又叫flush操作)。commit操作的具体内容有:

  1. 将buffer中的数据刷新到一个新的index segment中 ,index segment写入到OS Cache并打开index segment为搜索提供服务;
  2. 执行一个commit point操作,将OS Cache中所有的index segment标识记录在这个commit point中,并持久化到系统磁盘Disk
  3. commit point操作会触发fsync操作(file sync),将内存中已经写入数据的index segment落盘(强制刷新)到Disk磁盘上,持久化成文件。
  4. 清空本次持久化的index segment对应在translog中的日志。

1.3.3  segment Merge 原理

按照上述的流程来看,每1秒会生成一个index segment文件,每30分钟会将index segment文件流持久化到磁盘,照这样来看,磁盘中的index segment文件会非常多,从而需要处于开启状态的index segment也非常多,在执行搜索操作时,找到数据对应的index segment就会比较费时了。但不用担心,因为ES会定期进行Merge操作。

merge的大致流程如下:

后台分词 es es 分词原理_后台分词 es_04

  1. ES会选取一些大小相近的segment文件流,合并成一个大的segment文件流(注意: segment可能是尚未持久化到磁盘的segment file,也可能是已经持久化到磁盘的segment file,不管是哪种状态的segment file,此时它们都在OS Cache中)。
  2. 执行commit操作,在Disk中记录commit point,这个commit point不仅包含新增的segment,还包含需要被删除的segment(标记删除)。
  3. commit操作结束后,ES会将merge后的segment文件重新打开,为搜索提供服务,而那些需要被删除的segment文件则进行关闭并物理删除。

1.4 删除原理

ES为了保证数据的近实时搜索能力,不会直接在物理磁盘中删除目标数据所在的segment文件,而是先把待删除的数据放入一个.del文件中,在执行segment merge操作时,通过参考.del文件,忽略掉已被删除的数据,最终把大量的segment file合并成一个或几个segment file。

在buffer数据写入到segment的同时,会生成一个.del文件专门记录哪一个index segment中哪一条document是deleted状态(在merge后,这个.del文件会被更新)。因此ES搜索时,如果在多个index segment中查到了不同版本(version)的相同id值的doc时,会根据.del文件中的记录来继续过滤,保证搜索结果的唯一性和正确性(比如segment1中包含一条document version=1,对应新增状态;而segment2中包含相同id的document version=2,对应更新状态。由于后者的版本号更新,因此在.del中,version1被视作旧document,会被标记成deleted状态,从而在搜索时就会得到segment2中包含的version=2的数据了)。

1.5 查询原理(ID路由)

假设客户端请求查询_id=10的数据。

请求发送到ES集群中的任意节点,此时该节点成为本次请求的协调节点。协调节点默认根据数据的_id作为routing(可以手动指定,只需要在查询时增加_routing参数即可)进行hash算法,Hash(routing) % number_of_shards,假设计算出目标数据存放的shard的下标是3。接着,协调节点请求master节点,获取下标为3的shard所在节点的访问路径、端口等信息,并将查询请求转发至目标节点中。(注意:显然下标为3的shard不一定只有一个,有可能存在一个primary shard和多个replica shard的场景,至于到底把请求发送到哪一个shard上,取决于随机轮寻算法round-robin)

目标节点在目标分片内根据_id轻松的查询到数据,并将数据回传给协调节点。

协调节点将数据返回给客户端。

1.5 搜索原理(文本搜索)

假设客户端请求查询某一个field的值为"hello java"。则请求发送到ES集群中的任意节点,此时该节点成为本次请求的协调节点。此时协调节点不知道目标数据到底存放在哪个节点的那个分片上,因此协调节点会把请求转发到ES集群当中的每一个节点中。

后台分词 es es 分词原理_倒排索引_05

1.5.1 query phase 搜索数据原理

搜索请求发送到某一个coordinate node,构构建一个priority queue,长度以paging操作from和size为准,默认为10,coordinate node将请求转发到所有shard,每个shard本地搜索,并构建一个本地的priority queue,各个shard将自己的priority queue返回给coordinate node,并构建一个全局的priority queue。

后台分词 es es 分词原理_倒排索引_06

1.5.2 fetch phase 拉取数据原理

(1)coordinate node构建完priority queue之后,就发送mget请求去所有shard上获取对应的document

(2)各个shard将document返回给coordinate node

(3)coordinate node将合并后的document结果返回给client客户端

一般搜索,如果不加from和size,就默认搜索前10条,按照_score排序

后台分词 es es 分词原理_后台分词 es_07

1.6 ES jvm堆内存设置注意事项

buffer和尚未写入系统缓存的index segment(就是一段倒排索引)存储在堆内存,受jvm参数控制。

系统缓存 OScache在这里可以被看做是"文件系统缓存",用于缓存打开后的segment file(段文件),存储在非堆内存,受操作系统控制。

非堆内存越大,能够打开并缓存的segment file(段文件)就越多,搜索和聚合时,能够直接从内存中获取的热数据也就越多(不需要通过IO,在磁盘中找到尚未打开的segment file,读取文件内容)。

搜索数据时,首先在OS Cache中进行搜索,如果找不到数据,则在磁盘中找到对应的index segment文件并打开,读取数据至堆内存中,接着,在堆内存中对数据进行聚合、排序等操作,最后把数据返回给协调节点,最终交给调用方。此外,新读取到堆内存的segment file会被Lucense缓存至非堆内存中。

1.7 bouncing results数据跳跃原理

两个document排序,field值相同;不同的shard上,可能排序不同;每次请求轮询打到不同的shard shard上;每次页面上看到的搜索结果的排序都不一样。这就是bouncing result,也就是跳跃的结果。

_primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz, _shards:2,3

解决方案就是将preference设置为一个字符串,比如说user_id,让每个user每次搜索的时候,都使用同一个replica shard去执行,就不会看到bouncing results了。

1.8 relevance score(相关度分数)算法

简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度

Elasticsearch使用的是 Term Frequency/Inverse Document Frequency算法,简称为TF/IDF算法

1.14.1 Term Frequency(TF算法)

搜索文本中的各个词条在field文本中出现了多少次,出现次数越多,就越相关

搜索请求:hello world

doc1:hello you, and world is very good

doc2:hello, how are you

1.14.2 Inverse Document Frequency(IDF算法)

搜索文本中的各个词条在整个索引的所有文档中出现了多少次,出现的次数越多,就越不相关

搜索请求:hello world

doc1:hello, today is very good

doc2:hi world, how are you

比如说,在index中有1万条document,hello这个单词在所有的document中,一共出现了1000次;world这个单词在所有的document中,一共出现了100次

doc2更相关

1.14.2 Field-length norm

field长度,field越长,相关度越弱

搜索请求:hello world

doc1:{ "title": "hello article", "content": "babaaba 1万个单词" }

doc2:{ "title": "my article", "content": "blablabala 1万个单词,hi world" }

hello world在整个index中出现的次数是一样多的 doc1更相关,title field更短

1.9 Doc values 正排索引介绍

ES存储document时,会根据数据对应的field类型建立对应的索引。通常来说只创建倒排索引,倒排索引是为了搜索而存在的,但如果对数据进行排序、聚合、过滤等操作时,再使用倒排索引就明显不适合了。这个时候就需要在ES中创建正排索引(doc values)。doc values保存在磁盘中,如果OS Cache系统缓存的空间足够大,ES会缓存doc values,因此性能还是很不错的。

问: 为什么说倒排索引不适合做聚合、排序等操作?

{
  “name” : “张三”,
  “remark” : “java开发工程师”,
  "_id": 1
}
{
  “name” : “李四”,
  “remark” : “java架构工程师”,
  "_id": 2
}

为remark字段创建倒排索引:

分词

doc1

doc2

java

*

*

开发

*

 

架构

 

*

工程师

*

*

如果在倒排索引的基础上进行聚合,那么到底是根据java进行聚合呢、还是根据开发或者架构来聚合呢?不难发现,用哪一个都不合适。排序也是如此。倒排索引因分词得到了许多好处,但也因此留下了弊端。

所以,为了应对这种不需要分词的需求和场景,ES设计了正排索引Doc values。Doc values不会对字段作任何分词处理,皆保留原值。

正排索引的大致结构如下:

doc values

values

doc1

java开发工程师

doc2

java架构工程师

ES会根据document中每个字段是否分词,有选择性的实现字段对应的倒排索引或正排索引(Doc values)。

比如,如果字段类型为keyword,long,date,那么这些类型修饰的字段一定会有正排索引(Doc values)。

比如,如果字段类型为text,则只会有倒排索引,不会主动创建正排索引。

如果想在同一个字段中,既实现正排索引,又实现倒排索引,只需要使用fielddata即可,实现方式如下(我们前面讲解过1.13 ):

若遇到聚合、排序等需求时,ES使用test_field.keyword,若遇到搜索需求时,ES使用test_field。

{
	"mappings": {
		"type": {
			"properties": {
				"test_field": {
					"type": "text",
					"analyzer": "standard",
					"fields": {
						"keyword": {
							"type": "keyword"
						}
					}
				}
			}
		}
	}
}

二 index(索引)管理

2.1 index 的基本管理操作

关于对索引的操作

2.1.1 查询 创建 删除 index

查询 GET /index

创建

PUT /test
{
    "settings" : {
        "number_of_shards" : 1
    },
    "mappings" : {
        "properties" : {
            "field1" : { "type" : "text" }
        }
    }
}

删除 DELETE /index

2.1.2  修改 index settings(number_of_replicas)

PUT /twitter/_settings
{
    "index" : {
        "number_of_replicas" : 2
    }
}

2.2 自定义分词器

1、默认的分词器

standard

standard tokenizer:以单词边界进行切分

standard token filter:什么都不做

lowercase token filter:将所有字母转换为小写

stop token filer(默认被禁用):移除停用词,比如a the it等等

2、修改分词器的设置

启用english停用词token filter

PUT /my_index
{
 "settings": {
   "analysis": {
     "analyzer": {
       "es_std": {
         "type": "standard",
         "stopwords": "_english_"
       }
     }
   }
 }
}

测试

GET /my_index/_analyze
{
 "analyzer": "standard",  
 "text": "a dog is in the house"
}
GET /my_index/_analyze
{
 "analyzer": "es_std",
 "text":"a dog is in the house"
}

3、定制化自己的分词器

PUT /my_index
{
 "settings": {
   "analysis": {
     "char_filter": {
       "&_to_and": {
         "type": "mapping",
         "mappings": ["&=> and"]
       }
     },
     "filter": {
       "my_stopwords": {
         "type": "stop",
         "stopwords": ["the", "a"]
       }
     },
     "analyzer": {
       "my_analyzer": {
         "type": "custom",
         "char_filter": ["html_strip", "&_to_and"],
         "tokenizer": "standard",
         "filter": ["lowercase", "my_stopwords"]
       }
     }
   }
 }
}

测试

GET /my_index/_analyze
{
 "text": "tom&jerry are a friend in the house, <a>, HAHA!!",
 "analyzer": "my_analyzer"
}
PUT /my_index/_mapping/my_type
{
 "properties": {
   "content": {
     "type": "text",
     "analyzer": "my_analyzer"
   }
 }
}

2.3 零停机重建索引

一个field的设置是不能被修改的,如果要修改一个Field,那么应该重新按照新的mapping,建立一个index,然后将数据批量查询出来,重新用bulk api写入index中。批量查询的时候,建议采用scroll api,并且采用多线程并发的方式来reindex数据,每次scoll就查询指定日期的一段数据,交给一个线程即可。

场景模拟:

     依靠dynamic mapping 插入数据 但是不小心有些数据是2017-01-01这种日期格式的,所以title这种field被自动映射为了date类型,实际上它应该是string类型的。

PUT /qq_index/_doc/1
{
 "title": "2017-01-03"
}

查看mapping

GET /qq_index/_mapping
{
  "qq_index" : {
    "mappings" : {
      "properties" : {
        "title" : {
          "type" : "date"
        }
      }
    }
  }
}

当后期向索引中加入string类型的title值的时候,就会报错

PUT /qq_index/_doc/2
{
 "title": "my first article"
}

结果报错:failed to parse field [title] of type [date] in document with id '2'. Preview of field's value: 'my first article'"

如果此时想修改title的类型,是不可能的。此时,唯一的办法,就是进行reindex,也就是说,重新建立一个索引,将旧索引的数据查询出来,再导入新索引。

如果说旧索引的名字,是old_index,新索引的名字是new_index,终端java应用,已经在使用old_index在操作了,难道还要去停止java应用,修改使用的index为new_index,才重新启动java应用吗?这个过程中,就会导致java应用停机,可用性降低。

隐藏,给java应用一个别名,这个别名是指向旧索引的,java应用先用着,java应用先用goods_index alias来操作,此时实际指向的是旧的my_index。

1.旧索引给个别名              PUT /qq_index/_alias/goods_index

2.新建一个index,调整其title的类型为string

PUT /qq_index_new
{
 "mappings": {
     "properties": {
       "title": {
         "type": "text"
       }
     }
   }
}

使用scroll api将数据批量查询出来

GET /qq_index/_search?scroll=1m
{
   "query": {
       "match_all": {}
   },
   "sort": ["_doc"],
   "size":  1
}

查询结果

{
  "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFG5HSVprM0lCTVZGLTNNOWZMU3ZZAAAAAAAAA0sWMzJ3RmpnUHNTVVNRVHdWQ0c2NFMyZw==",
  "took" : 15,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "qq_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "title" : "2017-01-03"
        },
        "sort" : [
          0
        ]
      }
    ]
  }
}

采用bulk api将scoll查出来的一批数据,批量写入新索引

POST /_bulk
{ "index":  { "_index": "qq_index_new",  "_id": "2" }}
{ "title":    "2017-01-02" }

反复查询一批又一批的数据出来,采取bulk api将每一批数据批量写入新索引,将goods_index alias切换到my_index_new上去,java应用会直接通过index别名使用新的索引中的数据,java应用程序不需要停机,零提交,高可用

POST /_aliases
{
   "actions": [
       { "remove": { "index": "qq_index", "alias": "goods_index" }},
       { "add":    { "index": "qq_index_new", "alias": "goods_index" }}
   ]
}

直接通过goods_index别名来查询,是否ok

GET /goods_index/_search

三 mapping 原理解析

3.1 mapping 基本概念

 mapping 自动或手动为index中的type建立的一种数据结构和相关配置,简称为mapping

3.1.1 初始化数据

PUT /website/article/1
{
 "post_date": "2017-01-01",
 "title": "my first article",
 "content": "this is my first article in this website",
 "author_id": 11400
}
PUT /website/article/2
{
 "post_date": "2017-01-02",
 "title": "my second article",
 "content": "this is my second article in this website",
 "author_id": 11400
}
PUT /website/article/3
{
 "post_date": "2017-01-03",
 "title": "my third article",
 "content": "this is my third article in this website",
 "author_id": 11400
}

3.1.2 mapping 解析

GET /website/_mapping

{
  "website" : {
    "mappings" : {
      "properties" : {
        "author_id" : {
          "type" : "long"
        },
        "content" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "post_date" : {
          "type" : "date"
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

由查询结果得知,dynamic mapping自动为我们建立index,创建type,以及type对应的mapping,mapping中包含了每个field对应的数据类型。

  1.  往es里面直接插入数据,es会自动建立索引,同时建立type以及对应的mapping
  2.  mapping中就自动定义了每个field的数据类型
  3.  不同的数据类型(比如说text和date),可能有的是exact value(建立倒排索引的时候,分词的时候,是将整个值一起作为一个关键词建立到倒排索引中的),有的是full text(会经历各种各样的处理,然后分词,再normaliztion(时态转换,同义词转换,大小写转换),才会建立到倒排索引中)(这个在后面搜索的时候就能体会到)
  4. exact value和full text类型的field就决定了,搜索的时候,对field (exact value 或者full text field)搜索行为是不一样的,会跟建立倒排索引的行为保持一致;比如说exact value搜索的时候,就是直接按照整个值进行匹配,full text query string,也会进行分词和normalization再去倒排索引中去搜索
  5. 最后我们可以用es的dynamic mapping,让其自动建立mapping,包括自动设置数据类型;也可以提前手动创建index和type的mapping,自己对各个field进行设置,包括数据类型,包括索引行为,包括分词器,等等

mapping它是index的type的元数据,每个type都有一个自己的mapping,它决定了数据类型,建立倒排索引的行为,还有进行搜索的行为。(后面我们在1.7.3 搜索的时候会讲到)

3.2 mapping 的基本类型

9个基本类型 keyword  byte,short,integer,long  float,double  boolean  date

3.3 dynamic mapping 根据值自动推断

  1. true or false --> boolean
  2. 123  --> long
  3. 123.45  --> double
  4. 2017-01-01 --> date
  5. "hello world" --> keyword/text

3.4 手动创建mapping

只能创建index时手动建立mapping,或者新增field mapping,但是不能update field mapping

手动创建  PUT /website

{
  "website" : {
    "mappings" : {
      "properties" : {
        "author_id" : {
          "type" : "long"
        },
        "content" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "post_date" : {
          "type" : "date"
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

我们尝试修改field mapping ,我们前面说过不能修改。PUT /website

{
 "mappings": {
   "article": {
     "properties": {
       "author_id": {
         "type": "text"
       }
     }
   }
 }
}

结果是不行的

后台分词 es es 分词原理_倒排索引_08

但是我们可以新增field mapping

PUT /website/_mapping

{
 "properties" : {
   "new_field" : {
     "type" :    "keyword",
     "index":    "false"
   }
 }
}

测试 mapping 分词   GET /website/_analyze

{
 "field": "content",
 "text": "my-dogs"  
}

 

分词为  my  dogs  GET website/_analyze

{
 "field": "new_field",
 "text": "my dogs"
}

这个就不会分词

3.5 dynamic mapping定制化策略

true:遇到陌生字段,就进行dynamic mapping

false:遇到陌生字段,就忽略

strict:遇到陌生字段,就报错

PUT /my_index
{
  "mappings": {
      "dynamic": "strict",
      "properties": {
        "title": {
          "type": "text"
        },
        "address": {
          "type": "object",
          "dynamic": "true"
        }
      }
    }
}

测试(错误的)

POST /my_index/_doc/1
{
  "title": "my article",
  "content": "this is my article",
  "address": {
    "province": "guangdong",
    "city": "guangzhou"
  }
}

遇到陌生字段,就报错:mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed

测试(正确的)

POST /my_index/_doc/1
{
  "title": "my article",
  "address": {
    "province": "guangdong",
    "city": "guangzhou"
  }
}

date_detection

默认会按照一定格式识别date,比如yyyy-MM-dd。但是如果某个field先过来一个2017-01-01的值,就会被自动dynamic mapping成date,后面如果再来一个"hello world"之类的值,就会报错。可以手动关闭某个type的date_detection,如果有需要,自己手动指定某个field为date类型。

PUT /my_index/_mapping
{
   "date_detection": false
}

定制自己的dynamic mapping template(type level)

插入模板

PUT /dd_index
{
    "mappings": {
            "dynamic_templates": [
                { "en": {
                      "match":              "*_en", 
                      "match_mapping_type": "string",
                      "mapping": {
                          "type":           "text",
                          "analyzer":       "english"
                      }
                }}
            ]
}}

插入数据

PUT /dd_index/_doc/1
{
  "title": "this is my first article"
}

PUT /dd_index/_doc/2
{
  "title_en": "this is my first article"
}

搜索验证  GET /dd_index/_search?q=is

查询结果只能查询到1。

title没有匹配到任何的dynamic模板,默认就是standard分词器,不会过滤停用词,is会进入倒排索引,用is来搜索是可以搜索到的。

title_en匹配到了dynamic模板,就是english分词器,会过滤停用词,is这种停用词就会被过滤掉,用is来搜索就搜索不到了。

四 ES 常用搜索技术解析

4.1 搜索结果解析

4.1.1 解析搜索结果的含义&timeout机制。

GET /_search

{
  "took" : 123,
  "timed_out" : false,
  "_shards" : {
    "total" : 11,
    "successful" : 11,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : ".async-search",
        "_type" : "_doc",
        "_id" : "n21t3OMWQoG0IHCGOq9fPA",
        "_score" : 1.0,
        "_source" : {
          "result" : "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",
          "headers" : {
            "Authorization" : "Basic ZWxhc3RpYzpjc2I5NGREV3FhMjJtZzdtMXFFSw==",
            "_xpack_audit_request_id" : "bMmZ1JDSSxOa7JumDn-LDA",
            "_xpack_security_authentication" : "k8OvAwAHZWxhc3RpYwEJc3VwZXJ1c2VyCgEJX3Jlc2VydmVkBQEAAAEABGVzMDEIcmVzZXJ2ZWQIcmVzZXJ2ZWQAAAoA"
          },
          "expiration_time" : 1591410890327,
          "response_headers" : { }
        }
      },
      .......
    ]
  }
}
  1. took:整个搜索请求花费了多少毫秒
  2. hits.total:本次搜索,返回了几条结果
  3. hits.max_score:本次搜索的所有结果中,最大的相关度分数是多少,越相关,_score分数越大,排位越靠前
  4. hits.hits:他是是个数据的数组包含前10条,默认查询前10条数据,_score降序排序
  5. shards:shards fail的条件(primary和replica全部挂掉),不影响其他shard。默认情况下来说,一个搜索请求,会打到一个index的所有primary shard上去,包括replica 上。
  6. timeout:默认无timeout(即查多久,就得等多久),latency平衡completeness,手动指定timeout,timeout查询执行机制 timeout=10ms,timeout=1s,timeout=1m

后台分词 es es 分词原理_后台分词 es_09

4.2 搜索是否合法检测

一般用在那种特别复杂庞大的搜索下,比如你一下子写了上百行的搜索,这个时候可以先用validate api去验证一下,搜索是否合法。

比如一个有错的 请求

GET /test_index/test_type/_validate/query?explain
{
  "query": {
    "math": {
      "test_field": "test"
    }
  }
}

他会提示错误

{
  "valid" : false,
  "error" : "ParsingException[unknown query [math] did you mean [match]?]; nested: NamedObjectNotFoundException[[3:13] unknown field [math]];; org.elasticsearch.common.xcontent.NamedObjectNotFoundException: [3:13] unknown field [math]"
}

4.3 精确匹配和全文检索基本概念

4.3.1 精确匹配 exact value

搜索的时候,搜索文本必须是全匹配才能搜索到

4.3.2 全文检索 full text

他不是单纯的只是匹配完整的一个值,而是可以对搜索文本进行拆分词语后(分词)进行匹配,也可以通过缩写、时态、大小写、同义词等进行匹配。

4.4 query string & _all metadata

4.4.1 query string语法

url?q=field:search content

GET /test_index/test_type/_search?q=test_field:test

GET /test_index/test_type/_search?q=+test_field:test   (必须包含test,可以省略)

GET /test_index/test_type/_search?q=-test_field:test   (必须不包含test)

4.4.2 _all metadata的原理和作用

搜索document 中所有的field

GET /test_index/test_type/_search?q=test

es中的_all元数据,在建立索引的时候,我们插入一条document,它里面包含了多个field,此时,es会自动将多个field的值,全部用字符串的方式串联起来,变成一个长的字符串,作为_all field的值,同时建立索引,后面如果在搜索的时候,没有对某个field指定搜索,就默认搜索_all field,其中是包含了所有field的值。

生产环境不使用

4.4.3 _all & 指定field 查询结果不一致

我们还是查询前面初始化好的数据

  1. GET /website/article/_search?q=2017                           3条结果              
  2. GET /website/article/_search?q=2017-01-01                  3条结果
  3. GET /website/article/_search?q=post_date:2017-01-01   1条结果
  4. GET /website/article/_search?q=post_date:2017             1条结果

搜索结果为什么不一致,因为es自动建立mapping的时候,设置了不同的field不同的data type。不同的data type的分词、搜索等行为是不一样的。所以出现了_all field和post_date field的搜索表现完全不一样。

GET /_search?q=2017 搜索的是_all field。document所有的field都会拼接成一个大串,进行分词

2017-01-02 my second article this is my second article in this website 11400

分词

doc1

doc2

doc3

2017

*

*

*

01

*

*

*

02

 

*

 

03

 

 

*

自然会搜索到3个docuemnt

GET /_search?q=2017-01-01

_all,2017-01-01,query string会用跟建立倒排索引一样的分词器去进行分词 2017 01 01 所以也能查询三个

GET /_search?q=post_date:2017-01-01

date 会作为exact value去建立索引

分词

doc1

doc2

doc3

2017-01-01

*

 

 

2017-01-02

 

*

 

2017-01-03

 

 

*

所以只能查询到一个。

4.5 from size deep page scroll 分页搜索

分页使用场景很常见,我们看看怎么使用。

size :每页几条,from :开始点。

例如:

GET /_search?size=10

GET /_search?size=10&from=0

GET /_search?size=10&from=20

4.5.1 分页语法

查询测试数据

GET /test_index/test_type/_search

返回结果

"hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },

5条数据分成3页,每一页是2条数据,来实验一下这个分页搜索的效果

GET /test_index/test_type/_search?from=0&size=2

其结果是:

第一页 from= 0 ,结果是2条

第二页 from= 2 ,结果是2条

第三页 from= 4 ,结果是1条

4.5.2 deep paging 原理

深度分页,就是翻页的数据很大,即也就是很深,如果没有优化处理,会引发相关性能问题,甚至服务不可用。

后台分词 es es 分词原理_搜索_10

4.5.3 scroll 滚动分页解决深度分页

使用scroll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来。

  1. scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的,采用基于_doc进行排序的方式,性能较高。
  2. 每次发送scroll请求,我们还需要指定一个scroll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以。
GET /test_index/test_type/_search?scroll=1m
{
 "query": {
   "match_all": {}
 },
 "sort": [ "_doc" ],
 "size": 3
}

获得的结果会有一个scoll_id,下一次再发送scoll请求的时候,必须带上这个scoll_id

GET /_search/scroll
{
   "scroll": "1m",  
   "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAACxeFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYBY0b25zVFlWWlRqR3ZJajlfc3BXejJ3AAAAAAAALF8WNG9uc1RZVlpUakd2SWo5X3NwV3oydwAAAAAAACxhFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAsYhY0b25zVFlWWlRqR3ZJajlfc3BXejJ3"
}

4.6 filter与query 搜索比较

4.6.1 初始化数据

PUT /company/employee/2
{
 "address": {
   "country": "china",
   "province": "jiangsu",
   "city": "nanjing"
 },
 "name": "tom",
 "age": 30,
 "join_date": "2016-01-01"
}
PUT /company/employee/3
{
 "address": {
   "country": "china",
   "province": "shanxi",
   "city": "xian"
 },
 "name": "marry",
 "age": 35,
 "join_date": "2015-01-01"
}

搜索请求:年龄必须大于等于30,同时join_date必须是2016-01-01

4.6.2  filter与query初体验

GET /company/employee/_search
{
 "query": {
   "bool": {
     "must": [
       {
         "match": {
           "join_date": "2016-01-01"
         }
       }
     ],
     "filter": {
       "range": {
         "age": {
           "gte": 30
         }
       }
     }
   }
 }
}
  1. filter,仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响
  2. query,会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序
  3. 一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用query;如果你只是要根据一些条件筛选出一部分数据,不关注其排序,那么用filter

4.6.3 filter与query性能

filter,不需要计算相关度分数,不需要按照相关度分数进行排序,同时还有内置的自动cache最常使用filter的数据

query,相反,要计算相关度分数,按照分数进行排序,而且无法cache结果

4.7 query 搜索常用语法

4.7.1 match all 搜索

GET /_search
{
   "query": {
       "match_all": {}
   }
}

4.7.2 match 搜索

GET /_search
{
   "query": { "match": { "name": "tom" }}
}

4.7.3 multi match(多字段匹配)搜索

GET /test_index/test_type/_search
{
 "query": {
   "multi_match": {
     "query": "test",
     "fields": ["test_field", "test_field1"]
   }
 }
}

4.7.4 range query(范围匹配)搜索

GET /company/employee/_search  
{
 "query": {
   "range": {
     "age": {
       "gte": 30
     }
   }
 }
}

4.7.5 term query(项匹配)搜索

GET /test_index/test_type/_search  
{
 "query": {
   "term": {
     "test_field": "test2"
   }
 }
}

4.7.6 terms query(多值匹配)搜索

GET /_search
{
   "query": { "terms": { "tags": [ "suv", "daqi", "zhongxing" ] }}
}

4.7.7 mach query 组合搜索

GET /website/article/_search
{
 "query": {
   "bool": {
     "must": [
       {
         "match": {
           "title": "article"
         }
       }
     ],
     "should": [
       {
         "match": {
           "content": "article"
         }
       }
     ],
     "must_not": [
       {
         "match": {
           "author_id": 111
         }
       }
     ]
   }
 }
}

bool must,must_not,should,filter

每个子查询都会计算一个document针对它的相关度分数,然后bool综合所有分数,合并为一个分数,当然filter是不会计算分数的。

4.8 定制化排序

GET /website/_search
{
   "query" : {
       "bool" : {
           "filter" : {
               "term" : {
                   "author_id" : 11400
               }
           }
       }
   }
}
GET /website/_search
{
   "query" : {
       "constant_score" : {
           "filter" : {
               "term" : {
                   "author_id" : 11400
               }
           }
       }
   }
}

filter 是不排序的

定制化排序

GET /website/_search
{
 "query": {
   "constant_score": {
    "filter" : {
               "term" : {
                   "author_id" : 11400
               }
           }
   }
 },
 "sort": [
   {
     "post_date": {
       "order": "desc"
     }
   }
 ]
}

4.9 string field建立两次索引解决排序问题

如果对一个string field进行排序,结果往往不准确,因为分词后是多个单词,再排序就不是我们想要的结果了 通常解决方案是,将一个string field建立两次索引,一个分词,用来进行搜索;一个不分词,用来进行排序

PUT /website  

DELETE /website
PUT /website
{
 "mappings": {
     "properties": {
       "title": {
         "type": "text",
         "fields": {
           "keyword": {
             "type": "keyword"
           }
         }
       },
       "content": {
         "type": "text"
       },
       "post_date": {
         "type": "date"
       },
       "author_id": {
         "type": "long"
       }
     }
 }
}

初始化数据

POST /website/_doc/1
{
 "post_date": "2017-01-01",
 "title": "my first article",
 "content": "this is my first article in this website",
 "author_id": 11400
}
PUT /website/_doc/2
{
 "post_date": "2017-01-02",
 "title": "my second article",
 "content": "this is my second article in this website",
 "author_id": 11400
}
PUT /website/_doc/3
{
 "post_date": "2017-01-03",
 "title": "my third article",
 "content": "this is my third article in this website",
 "author_id": 11400
}

字符串排序

GET /website/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "title.keyword": {
        "order": "asc"
      }
    }
  ]
}