前言
我们通过前面两篇文章的学习,基本解es,但还不足以应对我们平时的开发任务,因此我们还需要全面深入的学习es技术。本篇文章会讲述很多底层内核级原理,所以我们需要集中精力深入体会。
一 ES 内核级原理及相关概念
1.1 分词器原理&介绍
它指把一段语句,拆分成单个的单词。同时对每个单词进行normalization (时态转换,单复数转换)处理,以提升recall召回率(搜索的时候,增加能够搜索到的结果的数量)。
1.1.1 分词器的主要构成
大部分的分词器都是有三块组成 预处理 、分词 、 过滤处理:
- character filter:在一段文本进行分词之前,先进行预处理,比如说最常见的就是,过滤html标签(<span>hello<span> --> hello),& --> and(I&you --> I and you)
- tokenizer:分词,hello you and me --> hello, you, and, me
- 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)
- standard analyzer:set, the, shape, to, semi, transparent, by, calling, set_trans, 5(默认的是standard)
- simple analyzer:set, the, shape, to, semi, transparent, by, calling, set, trans
- whitespace analyzer:Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
- 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 |
I | * | * |
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 |
I | * | * |
|
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 倒排索引的数据结构
倒排索引他主要是适合用于进行搜索的,因此倒排索引的结构是:
- 包含这个关键词的doc list
- 包含这个关键词的所有doc的数量:IDF(inverse document frequency)
- 这个关键词在每个doc中出现的次数:TF(term frequency)
- 这个关键词在这个doc中的次序
- 每个doc的长度:length norm
- 包含这个关键词的所有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将本次请求要操作的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在将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操作的具体内容有:
- 将buffer中的数据刷新到一个新的index segment中 ,index segment写入到OS Cache并打开index segment为搜索提供服务;
- 执行一个commit point操作,将OS Cache中所有的index segment标识记录在这个commit point中,并持久化到系统磁盘Disk
- commit point操作会触发fsync操作(file sync),将内存中已经写入数据的index segment落盘(强制刷新)到Disk磁盘上,持久化成文件。
- 清空本次持久化的index segment对应在translog中的日志。
1.3.3 segment Merge 原理
按照上述的流程来看,每1秒会生成一个index segment文件,每30分钟会将index segment文件流持久化到磁盘,照这样来看,磁盘中的index segment文件会非常多,从而需要处于开启状态的index segment也非常多,在执行搜索操作时,找到数据对应的index segment就会比较费时了。但不用担心,因为ES会定期进行Merge操作。
merge的大致流程如下:
- ES会选取一些大小相近的segment文件流,合并成一个大的segment文件流(注意: segment可能是尚未持久化到磁盘的segment file,也可能是已经持久化到磁盘的segment file,不管是哪种状态的segment file,此时它们都在OS Cache中)。
- 执行commit操作,在Disk中记录commit point,这个commit point不仅包含新增的segment,还包含需要被删除的segment(标记删除)。
- 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集群当中的每一个节点中。
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。
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排序
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对应的数据类型。
- 往es里面直接插入数据,es会自动建立索引,同时建立type以及对应的mapping
- mapping中就自动定义了每个field的数据类型
- 不同的数据类型(比如说text和date),可能有的是exact value(建立倒排索引的时候,分词的时候,是将整个值一起作为一个关键词建立到倒排索引中的),有的是full text(会经历各种各样的处理,然后分词,再normaliztion(时态转换,同义词转换,大小写转换),才会建立到倒排索引中)(这个在后面搜索的时候就能体会到)
- exact value和full text类型的field就决定了,搜索的时候,对field (exact value 或者full text field)搜索行为是不一样的,会跟建立倒排索引的行为保持一致;比如说exact value搜索的时候,就是直接按照整个值进行匹配,full text query string,也会进行分词和normalization再去倒排索引中去搜索
- 最后我们可以用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 根据值自动推断
- true or false --> boolean
- 123 --> long
- 123.45 --> double
- 2017-01-01 --> date
- "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"
}
}
}
}
}
结果是不行的
但是我们可以新增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" : { }
}
},
.......
]
}
}
- took:整个搜索请求花费了多少毫秒
- hits.total:本次搜索,返回了几条结果
- hits.max_score:本次搜索的所有结果中,最大的相关度分数是多少,越相关,_score分数越大,排位越靠前
- hits.hits:他是是个数据的数组包含前10条,默认查询前10条数据,_score降序排序
- shards:shards fail的条件(primary和replica全部挂掉),不影响其他shard。默认情况下来说,一个搜索请求,会打到一个index的所有primary shard上去,包括replica 上。
- timeout:默认无timeout(即查多久,就得等多久),latency平衡completeness,手动指定timeout,timeout查询执行机制 timeout=10ms,timeout=1s,timeout=1m
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 查询结果不一致
我们还是查询前面初始化好的数据
- GET /website/article/_search?q=2017 3条结果
- GET /website/article/_search?q=2017-01-01 3条结果
- GET /website/article/_search?q=post_date:2017-01-01 1条结果
- 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 原理
深度分页,就是翻页的数据很大,即也就是很深,如果没有优化处理,会引发相关性能问题,甚至服务不可用。
4.5.3 scroll 滚动分页解决深度分页
使用scroll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来。
- scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的,采用基于_doc进行排序的方式,性能较高。
- 每次发送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
}
}
}
}
}
}
- filter,仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响
- query,会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序
- 一般来说,如果你是在进行搜索,需要将最匹配搜索条件的数据先返回,那么用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"
}
}
]
}