es待整理笔记

Elasticsearch入门
基本概念
文档
elasticsearch是面向文档的,文档是所有可搜索数据的最小单位
文档会被序列化成json格式
每个文档都有一个unique id
一篇文档包含一系列字段,类似数据库表中一条记录
json文档,格式灵活,不需要预先定义格式
字段的类型可以指定或通过es自动推算
支持数组/支持嵌套
2019101115707263107291.png
文档的元数据
元数据用于标注文档的相关信息
_index 文档所属的索引名
_type 文档所属的类型名
_id 文档唯一id
_source 文档的原始json数据
_all 整合所有字段内容到该字段,已被废除
_version 文档的版本信息
_score 相关性打分

    索引
        索引是文档的容器,是一类文档的结合
            index体现了逻辑空间概念: 每个索引都有自己的mapping定义,用于定义包含的文档的字段名和字段类型
            shard体现了物理空间的概念: 索引中的数据分散在shard上
        索引的mapping于settings
            mapping定义文档字段的类型
            setting定义不同的数据分布
        type
            在7.0之前,一个index可以设置多个type
            6.0开始,type已经被废弃,一个索引只能创建一个type "doc"

        抽象与类比
            table index
            row   document
            column field
            shema mapping
            sql dsl
        index相关API
            
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#查看索引相关信息
GET kibana_sample_data_ecommerce
#查看索引的文档总数
GET kibana_sample_data_ecommerce/_count
#查看前10条文档,了解文档格式
POST kibana_sample_data_ecommerce/_search
{
}
#_cat indices API
#查看indices
GET /_cat/indices/kibana*?v&s=index
#查看状态为绿的索引
GET /_cat/indices?v&health=green
#按照文档个数排序
GET /_cat/indices?v&s=docs.count:desc
#查看具体的字段
GET /_cat/indices/kibana*?pri&v&h=health,index,pri,rep,docs.count,mt
#How much memory is used per index?
GET /_cat/indices?v&h=i,tm&s=tm:desc
分布式系统 分布式系统的可用性与扩展性 高可用性 服务可用性-允许有节点停止服务 数据可用性-部分节点丢失,不会丢失数据 可扩展性 请求量提升/数据不断增长 分布式特性 es分布式架构的优点 存储的水平扩容 提高系统的可用性,部分节点停止服务,整个集群服务不受影响 es的分布式架构 不同的集群通过不同的名称来区分 通过配置文件修改或在命令行中-E cluster.name=xxx进行设定 一个集群可以有一个或多个节点 节点 节点是一个es实例 本质是一个java进程 一个机器可以永兴多个es进程 每个节点启动后,会分配一个uid,保存在data目录下 节点类型 master-eligible nodes和master node 每个节点启动后,默认就是一个master eligible节点 可以设置node.master:false禁止 master-eligible节点可以参加选主,成为master节点 当第一个节点启动时,它会将自己选举成master节点 每个节点都保存了集群的状态,只有master节点才能修改集群的状态信息 集群状态,维护了一个集群的必要信息 所有节点信息 所有索引和其相关的mapping于setting信息 分片的路由信息 任意节点都能修改信息会导致数据的不一致性 data node和coordinating node 可以保存数据的节点叫做data node。负责保存分片数据。 coordinating node负责接受client请求,将请求分发到合适的节点,最终把结果汇集到一起 每个节点默认都起到了coordinating node的职责 hot & warm node 不同硬件配置的data node,用来实现hot &warm架构,降低集群部署的成本 machine learning node 负责跑机器学习的job,用来做异常检测 配置节点类型 开发环境中一个节点可以承担多个角色 生产环境中,应该设置单一的角色的节点 分片 主分片,用以解决数据水平扩展的问题。通过主分片,可以将数据分布到集群内的所有节点上 一个分片是一个运行的lucene实例 主分片数在索引创建时指定,后续不允许修改,除非reindex 副本,用以解决数据高可用问题,分片是主分片的拷贝 副本分片数,可以动态调整 增加副本数,可以一定程度上提高服务的吞吐 ![20191011157073161716134.png](http://pic.aipp.vip/20191011157073161716134.png) 分片的设定 生产环境分片的设定需要提前做好规划 分片数设置过小 导致后续无法增加节点实现水平扩展 单个分片数据量太大,导致数据重新分配耗时 分片设置过大,7.0开始默认主分片设置为1,解决over-sharding问题 影响搜索结果的相关性打分,影响通偶记结果的准确性 单个节点上过多的分片,会导致资源浪费,同时也会影响性能 集群 查看集群健康状况 GET _cluster/health green 主分片与副本分片都正常分配 yellow 主分片全部正常分配,有副本分片未能正常分配 red 有主分片未能分配 例如当服务器磁盘容量超过85%时,去创建一个新索引 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CAT Nodes API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cat-nodes.html
get _cat/nodes?v
GET /_nodes/es7_01,es7_02
GET /_cat/nodes?v
GET /_cat/nodes?v&h=id,ip,port,v,m
Cluster API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cluster.html
GET _cluster/health
GET _cluster/health?level=shards
GET /_cluster/health/kibana_sample_data_ecommerce,kibana_sample_data_flights
GET /_cluster/health/kibana_sample_data_flights?level=shards
#### cluster state
The cluster state API allows access to metadata representing the state of the whole cluster. This includes information such as
GET /_cluster/state
#cluster get settings
GET /_cluster/settings
GET /_cluster/settings?include_defaults=true
CAT Shards API https://www.elastic.co/guide/en/elasticsearch/reference/7.1/cat-shards.html
GET _cat/shards
GET _cat/shards?h=index,shard,prirep,state,unassigned.reason
文档基本操作 create 支持自动生成文档id和指定文档id两种方式 通过调用"POST /usrs/_doc"系统会自动生成id 使用PUT user/_create/1创建时,URI中显示指定_create,此时如果id文档已经存在,操作失败 get 元信息 同一个id的文档,即使被删除,version也会不断增加 找不到文档返回404 index 与create不同点在于,如果文档不存在就索引新的文档。否则现有文档会被删除,新的文档被索引,同时版本信息+1 update update不会删除原来的文档,而是实现真正的数据更新 post方法 payload需要包含在"doc"中
1
2
3
4
5
6
POST users/_update/1
{
"doc":{
"albums":["Album1","Album2"]
}
}
bulk api 一次rest请求中对不同的索引进行操作 支持四种类型操作 index/create/update/delete 可以在uri中指定index,也可以在请求的payload中进行 操作中哦单条操作失败,并不会影响其他操作 返回结果包含了每一条操作执行的结果 批量读取-mget 批量操作,减少网络连接产生的开销,提高性能 批量查询-msearch 常见错误返回 429 集群过于繁忙 4xx 请求体格式有误 500 集群内部错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
############Create Document############
#create document. 自动生成 _id
POST users/_doc
{
"user" : "Mike",
"post_date" : "2019-04-15T14:12:12",
"message" : "trying out Kibana"
}
#create document. 指定Id。如果id已经存在,报错
PUT users/_doc/1?op_type=create
{
"user" : "Jack",
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
#create document. 指定 ID 如果已经存在,就报错
PUT users/_create/1
{
"user" : "Jack",
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
### Get Document by ID
#Get the document by ID
GET users/_doc/1
### Index & Update
#Update 指定 ID (先删除,在写入)
GET users/_doc/1
PUT users/_doc/1
{
"user" : "Mike"
}
#GET users/_doc/1
#在原文档上增加字段
POST users/_update/1/
{
"doc":{
"post_date" : "2019-05-15T14:12:12",
"message" : "trying out Elasticsearch"
}
}
### Delete by Id
# 删除文档
DELETE users/_doc/1
### Bulk 操作
#执行两次,查看每次的结果
#执行第1
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
#执行第2
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
### mget 操作
GET /_mget
{
"docs" : [
{
"_index" : "test",
"_id" : "1"
},
{
"_index" : "test",
"_id" : "2"
}
]
}
#URI中指定index
GET /test/_mget
{
"docs" : [
{
"_id" : "1"
},
{
"_id" : "2"
}
]
}
GET /_mget
{
"docs" : [
{
"_index" : "test",
"_id" : "1",
"_source" : false
},
{
"_index" : "test",
"_id" : "2",
"_source" : ["field3", "field4"]
},
{
"_index" : "test",
"_id" : "3",
"_source" : {
"include": ["user"],
"exclude": ["user.location"]
}
}
]
}
### msearch 操作
POST kibana_sample_data_ecommerce/_msearch
{}
{"query" : {"match_all" : {}},"size":1}
{"index" : "kibana_sample_data_flights"}
{"query" : {"match_all" : {}},"size":2}
### 清除测试数据
#清除数据
DELETE users
DELETE test
DELETE test2
倒排索引 正排索引与倒排索引 ![20191011157073351468491.png](http://pic.aipp.vip/20191011157073351468491.png) 倒排索引核心组成 两部分 单词词典,记录所有文档的单词,记录单词到倒排列表的关联关系 单词词典一般比较大,可以通过B+树或哈希拉链法实现,以满足高性能的插入与查询 倒排列表,记录了单词对应的文档结合,由倒排索引项组成 倒排索引项 文档ID 词频TF 该词在文档中出现的次数,用于相关性打分 位置 单词在文档中分词的位置,用于语句搜索(phrase query) 便宜 记录单词的开始结束位置,实现高亮显示 elasticsearch的倒排索引 es的json文档中的每个字段,都有自己的倒排索引 可以指定对某些字段不做索引 analyzer分词 analysis与analyzer analysis文本分析是把全文本转换成一系列单词(term/token)的过程,也叫恩赐 analysis是通过analyzer来实现的 可使用es内置的分析器/或按需定制分析器 除了在数据写入时转换词条,匹配quey语句时候也需要用相同的分析器对查询语句进行分析 ![20191011157073396692105.png](http://pic.aipp.vip/20191011157073396692105.png) analyzer的组成 分词器是专门处理分词的组件,analyzer由三部分组成 character filters(针对原始文本处理,例如去除html)、tokenizer(按照规则切分单词)、token filter(将切分的单词进行加工,小写,删除stopwords,增加同义词) ![20191011157073408175529.png](http://pic.aipp.vip/20191011157073408175529.png) es内置的分词器 使用_analyzer API 直接指定analyzer进行测试 指定索引字段进行测试 自定义分词器进行测试 ![2019101115707342294258.png](http://pic.aipp.vip/2019101115707342294258.png) standard analyzer 默认分词器 按词切分 小写处理 simple analyzer 按照非字母切分,非字母的都被去除 小写处理 whitespace analyzer 按空格切分 stop analyzer 相比simple analyzer多了stop filter 会把the a is等修饰词去除 keyword analyzer 不分词,直接将数据当一个term输出 pattern analyzer 通过正则表达式分词 默认\W+,非字符的符号进行分隔 language analyzer 中文分词的特点 中文橘子,切分成一个一个词(不是一个一个字) 英文中,单词有自然的空格作为分隔 一句中文,在不同的上下文,有不同的理解 一些例子 他说的确实在理/这事确定不下来 elasticsearch-plugin install analysis-icu ik thulac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#Simple Analyzer – 按照非字母切分(符号被过滤),小写处理
#Stop Analyzer – 小写处理,停用词过滤(the,a,is)
#Whitespace Analyzer – 按照空格切分,不转小写
#Keyword Analyzer – 不分词,直接将输入当作输出
#Patter Analyzer – 正则表达式,默认 \W+ (非字符分隔)
#Language – 提供了30多种常见语言的分词器
#2 running Quick brown-foxes leap over lazy dogs in the summer evening
#查看不同的analyzer的效果
#standard
GET _analyze
{
"analyzer": "standard",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
#simpe
GET _analyze
{
"analyzer": "simple",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
GET _analyze
{
"analyzer": "stop",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
#stop
GET _analyze
{
"analyzer": "whitespace",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
#keyword
GET _analyze
{
"analyzer": "keyword",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
GET _analyze
{
"analyzer": "pattern",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
#english
GET _analyze
{
"analyzer": "english",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
POST _analyze
{
"analyzer": "icu_analyzer",
"text": "他说的确实在理”"
}
POST _analyze
{
"analyzer": "standard",
"text": "他说的确实在理”"
}
POST _analyze
{
"analyzer": "icu_analyzer",
"text": "这个苹果不大好吃"
}
searchAPI概览 URI search 在URL中使用查询参数 Request body search 使用elasticsearch提供的,基于json格式的更加完备的query domain specific language 指定查询的索引 /_search 集群上所有的索引 /index1/_search index1 /index1,index-2/_search index1和index2 /index*/_search URI查询 使用q,置顶查询字符串 curl -XGET xxx:9200/kibana_sample/_search?q=customer_name:eddie Request body curl -XGET "xxx:9200/kibana_data/_search" -H 'Content-Type:application/json' -d ' { "query":{ "match_all":{} } }' 搜索response ![20191011157073521159047.png](http://pic.aipp.vip/20191011157073521159047.png) 搜索的相关性relevance 搜索是用户和搜索引擎的对话 用户关心的是搜索结果的相关性 是否可以找到所有相关的内容 有多少不相关的内容被返回了 文档的打分是否合理 结合业务平衡排名 web搜索 page rank算法 不仅仅是结果 更重要是内容的可信度 电商搜索 搜索引擎扮演销售的角色 提高用户购物体验 提升网站销售业绩 去库存 衡量相关性 precision(查准率) 尽可能返回较少的无关文档 recall(查全率) 尽量返回较多的光管文档 ranking 是否能够按照相关度进行排序 precision & recall ![20191011157073545190872.png](http://pic.aipp.vip/20191011157073545190872.png) URI Search 通过URI query实现搜索 GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s { "profile":true } q指定查询语句,使用query string syntax df默认字段,不指定时会对所有字段进行查询 sort排序/from和size用于分页 profile可以查看查询是如何被执行的 query string syntax 指定字段 vs 泛查询 q=title:2012/q=2012 term vs phrase beautiful mind等效于beautiful or mind "beautiful mind"等效于beautiful and mind。phrase查询,还要求前后顺序保持一致 分组与引号 title:{beautiful and mind} title="beautiful mind" 布尔操作 AND/OR/NOT 或者 &&/||/! 必须大写 title:(matrix NOT reloaded) 分组 + 表示must - 表示must not title:(+matrix -reloaded) 范围查询 区间表示: []闭区间,{}开区间 year: {2019 TO 2018} year: [* TO 2018] 算数符号 year: >2010 year:(>2010 && <=2018) year:(+>2010 +<=2018) 通配符查询(通配符查询效率低,占用内存大,不建议使用) ?代表1个字符,*代表0或多个字符 title:mi?d title:be* 正则表达 title:[bt]oy 模糊匹配与近似查询 title:befutifl~1 title:“lord rings”~2 demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#基本查询
GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout=1s
#带profile
GET /movies/_search?q=2012&df=title
{
"profile":"true"
}
#泛查询,正对_all,所有字段
GET /movies/_search?q=2012
{
"profile":"true"
}
#指定字段
GET /movies/_search?q=title:2012&sort=year:desc&from=0&size=10&timeout=1s
{
"profile":"true"
}
# 查找美丽心灵, Mind为泛查询
GET /movies/_search?q=title:Beautiful Mind
{
"profile":"true"
}
# 泛查询
GET /movies/_search?q=title:2012
{
"profile":"true"
}
#使用引号,Phrase查询
GET /movies/_search?q=title:"Beautiful Mind"
{
"profile":"true"
}
#分组,Bool查询
GET /movies/_search?q=title:(Beautiful Mind)
{
"profile":"true"
}
#布尔操作符
# 查找美丽心灵
GET /movies/_search?q=title:(Beautiful AND Mind)
{
"profile":"true"
}
# 查找美丽心灵
GET /movies/_search?q=title:(Beautiful NOT Mind)
{
"profile":"true"
}
# 查找美丽心灵
GET /movies/_search?q=title:(Beautiful %2BMind)
{
"profile":"true"
}
#范围查询 ,区间写法
GET /movies/_search?q=title:beautiful AND year:[2002 TO 2018%7D
{
"profile":"true"
}
#通配符查询
GET /movies/_search?q=title:b*
{
"profile":"true"
}
//模糊匹配&近似度匹配
GET /movies/_search?q=title:beautifl~1
{
"profile":"true"
}
GET /movies/_search?q=title:"Lord Rings"~2
{
"profile":"true"
}
request body search 将查询语句通过http request body发送给elasticsearch query dsl
1
2
3
4
5
6
7
POST /movies,404_idx/_search?ignore_unavailable=true
{
"profile":true,
"query":{
"match_all":{}
}
}
分页
1
2
3
4
5
6
{
"from":10,
"size":20
}
from0开始,默认范围10个结果
获取靠后的翻页成本较高
排序
1
2
3
{
"sort":[{"order_date":"desc"}]
}
最好在数字型和日期型字段上排序 因为对于多值类型或分析过的字段排序,系统会选一个值,无法得知该值 _source filtering 如果_source没有存储,那就只返回匹配的文档的元数据 _source支持使用通配符 _source["name*","order_date"] 脚本字段 用例:订单中有不同的汇率,需要结合汇率对订单价格进行排序
1
2
3
4
5
6
7
8
{
"script_fields":{
"new_field":{
"lang":"painless",
"source":"doc['order_date'].value+'hello'"
}
}
}
使用查询表达式-Match
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
GET /comments/_doc/_search
{
"query":{
"match":{
"comment":"last christmas"
}
}
}
GET /comments/_doc/_search
{
"query":{
"match":{
"comment":"last christmas"
"operator":"AND"
}
}
}
短语查询
GET /comments/_doc/_search
{
"query":{
"match":{
"comment":"last christmas"
"slop":1
}
}
}
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#ignore_unavailable=true,可以忽略尝试访问不存在的索引“404_idx”导致的报错
#查询movies分页
POST /movies,404_idx/_search?ignore_unavailable=true
{
"profile": true,
"query": {
"match_all": {}
}
}
POST /kibana_sample_data_ecommerce/_search
{
"from":10,
"size":20,
"query":{
"match_all": {}
}
}
#对日期排序
POST kibana_sample_data_ecommerce/_search
{
"sort":[{"order_date":"desc"}],
"query":{
"match_all": {}
}
}
#source filtering
POST kibana_sample_data_ecommerce/_search
{
"_source":["order_date"],
"query":{
"match_all": {}
}
}
#脚本字段
GET kibana_sample_data_ecommerce/_search
{
"script_fields": {
"new_field": {
"script": {
"lang": "painless",
"source": "doc['order_date'].value+'hello'"
}
}
},
"query": {
"match_all": {}
}
}
POST movies/_search
{
"query": {
"match": {
"title": "last christmas"
}
}
}
POST movies/_search
{
"query": {
"match": {
"title": {
"query": "last christmas",
"operator": "and"
}
}
}
}
POST movies/_search
{
"query": {
"match_phrase": {
"title":{
"query": "one love"
}
}
}
}
POST movies/_search
{
"query": {
"match_phrase": {
"title":{
"query": "one love",
"slop": 1
}
}
}
}
query string&simple query string query string 类似 URI Query
1
2
3
4
5
6
7
8
9
POST users/_search
{
"query":{
"query_string":{
"default_field":"name",
"query":"ruan AND yiming"
}
}
}
simple query string 类似query string,但是会忽略错误的语法,同时只支持部分查询语法 不支持AND OR NOT,会当做字符串处理 Term之间默认的关系是OR,可以指定operator 支持部分逻辑 +替代AND |替代OR -替代NOT dynamic mapping 什么是mapping mapping类似数据库中schema的定义 定义索引中的字段的名称 定义字段的数据类型,例如字符串,数字,布尔。。 字段,倒排索引的相关配置 mapping会把json文档映射成lucene所需的扁平格式 一个mapping属于一个索引的type 每个文档都属于一个type 一个type有一个mapping定义 7.0开始不需要在mapping定义中指定type信息 字段的数据类型 简单类型 text/keyword date interger/floating boolean ipv4/ipv6 复杂类型 对象/嵌套类型 特殊类型 geo_point&geo_shape/percolator 什么是dynamic mapping 在写入文档时,如果索引不存在会自动创建索引 dynamic mapping的机制,使得我们无需手动定义mappings elasticsearch会自动根据文档信息推算出字段的类型 但有时会推算的不对 当类型如果设置不对时,会导致一些功能无法正常运行,比如range查询 类型的自动识别 json类型 elasticsearch类型 字符串 匹配日期类型,设置成date 配置数字类型为float或long,该选项默认关闭 设置为text,并且增加Keyword子字段 布尔值 boolean 浮点值 float 整数 long 对象 object 数组 由第一个非空数值的类型所决定 空值 忽略 能否更改mapping的字段类型 两种情况 新增加字段 dynamic设为true时,一旦有新增字段的文档写入,mapping也同时被更新 dynamic设为false,mapping不会更新,新增字段的数据无法被索引,但是信息会出现在_source中 dynamic设置为strict,文档写入失败 对已有字段,一旦已经有数据写入,就不再支持修改字段定义 lucene实现的倒排索引,一旦生成后,就不允许修改 如果希望改变字段类型,比如reindex API,重建索引 原因 如果修改了字段的数据类型,会导致已被索引的数据无法被搜索 但是如果是新增的字段就不会有这样的影响 控制dynamic mappings true false strict 文档可索引 YES YES NO 字段可索引 YES NO NO mapping被更新YES NO NO 当dynamic被设置成false时,存在新增字段的数据写入,该数据可以被索引,但是新增字段被丢弃 当设置成strict模式时,数据写入直接出错 example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#写入文档,查看 Mapping
PUT mapping_test/_doc/1
{
"firstName":"Chan",
"lastName": "Jackie",
"loginDate":"2018-07-24T10:29:48.103Z"
}
#查看 Mapping文件
GET mapping_test/_mapping
#Delete index
DELETE mapping_test
#dynamic mapping,推断字段的类型
PUT mapping_test/_doc/1
{
"uid" : "123",
"isVip" : false,
"isAdmin": "true",
"age":19,
"heigh":180
}
#查看 Dynamic
GET mapping_test/_mapping
#默认Mapping支持dynamic,写入的文档中加入新的字段
PUT dynamic_mapping_test/_doc/1
{
"newField":"someValue"
}
#该字段可以被搜索,数据也在_source中出现
POST dynamic_mapping_test/_search
{
"query":{
"match":{
"newField":"someValue"
}
}
}
#修改为dynamic false
PUT dynamic_mapping_test/_mapping
{
"dynamic": false
}
#新增 anotherField
PUT dynamic_mapping_test/_doc/10
{
"anotherField":"someValue"
}
#该字段不可以被搜索,因为dynamic已经被设置为false
POST dynamic_mapping_test/_search
{
"query":{
"match":{
"anotherField":"someValue"
}
}
}
get dynamic_mapping_test/_doc/10
#修改为strict
PUT dynamic_mapping_test/_mapping
{
"dynamic": "strict"
}
#写入数据出错,HTTP Code 400
PUT dynamic_mapping_test/_doc/12
{
"lastField":"value"
}
DELETE dynamic_mapping_test
显式mapping设置 如何显式定义一个mapping
1
2
3
4
5
6
PUT movies
{
"mappings":{
//define your mappings here
}
}
自定义mapping的一些建议 可以参考API手写 为了减少输入的工作量,可以依照以下步骤 创建一个临时的index,写入一些样本数据 通过访问mapping api获得该临时文件的动态mapping定义 修改后用,使用该配置创建你的索引 删除临时索引 控制当前字段是否被索引 index 控制当前字段是否被索引。默认为true,如果设置成false,该字段不可被搜索 index options 四种不同级别的index options配置,可以控制倒排索引记录的内容 docs-记录doc id freqs-记录doc id和term frequencies positions-记录doc id/term frequencies/term position offsets-doc id/term frequencies/term posistion/charactor offects text类型默认记录为positions,其他默认为docs 记录内容越多,占用存储空间越大 null_value 需要对null值实现搜索 只有keyword类型支持设定null_value copy_to设置 _all在7中已被copy_to所替代 满足一些特定的搜索需求 copy_to将字段的数值拷贝到目标字段,实现类似_all的作用 copy_to的目标字段不出现在_source中 数组类型 elasticsearch中不提供专门的数组类型。但是任何字段都可以包含多个相同类型的数值 example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#设置 index 为 false
DELETE users
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
"index": false
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming",
"mobile": "12345678"
}
POST /users/_search
{
"query": {
"match": {
"mobile":"12345678"
}
}
}
#设定Null_value
DELETE users
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "keyword",
"null_value": "NULL"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming",
"mobile": null
}
PUT users/_doc/2
{
"firstName":"Ruan2",
"lastName": "Yiming2"
}
GET users/_search
{
"query": {
"match": {
"mobile":"NULL"
}
}
}
#设置 Copy to
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Ruan",
"lastName": "Yiming"
}
GET users/_search?q=fullName:(Ruan Yiming)
POST users/_search
{
"query": {
"match": {
"fullName":{
"query": "Ruan Yiming",
"operator": "and"
}
}
}
}
#数组类型
PUT users/_doc/1
{
"name":"onebird",
"interests":"reading"
}
PUT users/_doc/1
{
"name":"twobirds",
"interests":["reading","music"]
}
POST users/_search
{
"query": {
"match_all": {}
}
}
GET users/_mapping
多字段特性及mapping中配置自定义analyzer 多字段类型 厂商名字实现精确匹配 增加一个keyword字段 使用不同的analyzer 不同语言 pinyin字段的搜索 还支持为搜索和索引置顶不同的analyzer exact values vs full text exact value: 包括数字、日期、具体一个自渡船 例如"apple store" elasticsearch中的keyword 全文本,非结构化的文本数据 elasticsearch中的text exact value不需要被分词 es为每个字段创建一个倒排索引 exact value在索引时,不需要做特殊的分词处理 ![2019101215708116787507.png](http://pic.aipp.vip/2019101215708116787507.png) 自定义分词 当es自带分词器无法满足时,可以自定义分词器,通过组合不同的组件实现 charactor filter tokenizer token filter charactor filter 在tokenizer之前对文本进行处理,例如增加删除替换字符。可以配置多个charactor filters。会影响tokenizer的position和offset信息 一些自带的character filters HTML strip 去除html标签 Mapping 字符串替换 Pattern replace 正则匹配替换 tokenizer 将原始的文本按照一定的规则,切分为词(term或token) es内置的tokenizers whitespace/standard/uax_url_email/pattern/keyword/path hierarchy 可以用java开发插件,实现自己的tokenizer token filters 将tokenizer输出的单词(term)进行增加、删除、修改 自带的token filters lowrcase/stop/synonym(添加近义词) 设置一个custom analyzer example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
PUT logs/_doc/1
{"level":"DEBUG"}
GET /logs/_mapping
POST _analyze
{
"tokenizer":"keyword",
"char_filter":["html_strip"],
"text": "<b>hello world</b>"
}
POST _analyze
{
"tokenizer":"path_hierarchy",
"text":"/user/ymruan/a/b/c/d/e"
}
#使用char filter进行替换
POST _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type" : "mapping",
"mappings" : [ "- => _"]
}
],
"text": "123-456, I-test! test-990 650-555-1234"
}
//char filter 替换表情符号
POST _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type" : "mapping",
"mappings" : [ ":) => happy", ":( => sad"]
}
],
"text": ["I am felling :)", "Feeling :( today"]
}
// white space and snowball
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["stop","snowball"],
"text": ["The gilrs in China are playing this game!"]
}
// whitespace与stop
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["stop","snowball"],
"text": ["The rain in Spain falls mainly on the plain."]
}
//remove 加入lowercase后,The被当成 stopword删除
GET _analyze
{
"tokenizer": "whitespace",
"filter": ["lowercase","stop","snowball"],
"text": ["The gilrs in China are playing this game!"]
}
//正则表达式
GET _analyze
{
"tokenizer": "standard",
"char_filter": [
{
"type" : "pattern_replace",
"pattern" : "http://(.*)",
"replacement" : "$1"
}
],
"text" : "http://www.elastic.co"
}
index template和dynamic template 什么是index template index template 帮助你设定mappings和settings并按照一定的规则,自动匹配到新创建的索引之上 模板仅在一个索引被新创建时才会产生作用,修改模板不会影响已创建的索引 你可以设置多个索引模板,这些设置会被merge在一起 你可以指定order的数值,控制merging的过程 index template工作方式 当一个索引被新创建时 应用es默认的settings和mappings 应用order数值低的index template中的设定 应用order高的index template中的设定,之前的设定会被覆盖 应用创建索引时,用户指定setting和mapping并覆盖之前模板中的设定 什么是dynamic template(用来影响dynamic mapping动态判断) 根据es识别的数据类型,结合字段名称,来动态设定字段类型 所有字符串类型设置成keyword,或者关闭keyword字段 is开头的字段都设置成boolean long_开头的都设置成long类型 dynamic template是定义在某个索引的mapping中 template有一个名称 匹配规则是一个数组 为匹配到字段设置mapping example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#数字字符串被映射成text,日期字符串被映射成日期
PUT ttemplate/_doc/1
{
"someNumber":"1",
"someDate":"2019/01/01"
}
GET ttemplate/_mapping
#Create a default template
PUT _template/template_default
{
"index_patterns": ["*"],
"order" : 0,
"version": 1,
"settings": {
"number_of_shards": 1,
"number_of_replicas":1
}
}
PUT /_template/template_test
{
"index_patterns" : ["test*"],
"order" : 1,
"settings" : {
"number_of_shards": 1,
"number_of_replicas" : 2
},
"mappings" : {
"date_detection": false,
"numeric_detection": true
}
}
#查看template信息
GET /_template/template_default
GET /_template/temp*
#写入新的数据,index以test开头
PUT testtemplate/_doc/1
{
"someNumber":"1",
"someDate":"2019/01/01"
}
GET testtemplate/_mapping
get testtemplate/_settings
PUT testmy
{
"settings":{
"number_of_replicas":5
}
}
put testmy/_doc/1
{
"key":"value"
}
get testmy/_settings
DELETE testmy
DELETE /_template/template_default
DELETE /_template/template_test
#Dynaminc Mapping 根据类型和字段名
DELETE my_index
PUT my_index/_doc/1
{
"firstName":"Ruan",
"isVIP":"true"
}
GET my_index/_mapping
DELETE my_index
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"strings_as_boolean": {
"match_mapping_type": "string",
"match":"is*",
"mapping": {
"type": "boolean"
}
}
},
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
]
}
}
DELETE my_index
#结合路径
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"full_name": {
"path_match": "name.*",
"path_unmatch": "*.middle",
"mapping": {
"type": "text",
"copy_to": "full_name"
}
}
}
]
}
}
PUT my_index/_doc/1
{
"name": {
"first": "John",
"middle": "Winston",
"last": "Lennon"
}
}
GET my_index/_search?q=full_name:John
聚合分析简介 什么是聚合(aggregation) es除搜索外,还提供了针对es数据进行统计分析的功能 实时性高 通过聚合,我们回得到一个数据的概览,是分析和总结全套的数据,而不是寻找单个文档 尖沙咀和香港岛的客房数量 不同的价格区间,可预订的经济性酒店和五星级酒店的数量 高性能,只需要一条语句,就可以从elasticsearch得到分析结果 无需在客户端自己去实现分析逻辑 可视化报表-聚合分析 公司程序员的工作岗位分布 公司采用的编程框架分布 公司员工薪水拼布 客户的地理位置分布 订单的增长情况等 集合的分类 bucket aggregation 一些满足特定条件的文档的集合 metric aggregation 一些数学计算,可以对文档字段进行统计分析 pipeline aggregation 对其他的聚合结果进行二次聚合 matrix aggregation 支持对多个字段的操作并提供一个结果矩阵 bucket & metric bucket 一组满足条件的文档 group metric 一些系列的统计方法 ![20191012157081367936349.png](http://pic.aipp.vip/20191012157081367936349.png) bucket elasticsearch提供了很多类型的bucket,帮助你用多种方式划分文档 term&range(时间/年龄区间/地理位置) metric metric会基于数据集计算结果,除了支持在字段上进行计算,同样也支持在脚本(painless script)产生的结果之上进行计算 大多数metric是数学计算,仅输出一个值 min/max/sum/avg/cardinality 部分metric支持数据多个值 stats/percentiles/percentile_ranks bucket示例 ![20191012157081389824477.png](http://pic.aipp.vip/20191012157081389824477.png) 加入metrics ![20191012157081397711966.png](http://pic.aipp.vip/20191012157081397711966.png) 嵌套 ![20191012157081406761072.png](http://pic.aipp.vip/20191012157081406761072.png)

深入搜索
基于词项和基于全文的搜索
基于词项的查询
term
term是表达语义的最小单位。搜索和利用统计语言模型进行自然语言处理都需要处理term
特点

            * term level query: term query/range query/exists query/prefix query/wildcard query
            * 在ES中,term查询,不对对输入做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,并且使用相关性算分公式为每个包含该词项的文档进行相关度算分-例如"apple store",如果需要完全匹配,可以使用"field.keyword"查询
            * 可以通过constant score 将查询转换成一个filtering,避免算分,并利用缓存,提高性能
        复合查询-constant score转为filter
            将query转为filter,忽略TF-IDF计算,避免相关性算分的开销
            filter可以有效利用缓存
            ![](http://pic.aipp.vip/20191017232434.png)
    基于全文的查询
        基于全文本的查询
            match query/match phrase query/query string/query
        特点
            * 索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表
            * 查询时候,会先对输入的查询进行分词,然后每个词项逐个进行底层的查询,最终将结果进行合并。并为每个文档生成一个算分-例如查询"Matrix reloaded",会查到包括Matrix或reload的所有结果
        ![](http://pic.aipp.vip/20191018022201.png)
    Match Query Result
        
1
2
3
4
5
6
7
8
9
10
POST movies/_search
{
"query":{
"match":{
"title":{
"query":"Matrix reloaded"
}
}
}
}
Operator
1
2
3
4
5
6
7
8
9
10
11
POST movies/_search
{
"query":{
"match":{
"title":{
"query":"Matrix reloaded",
"operator":"AND"
}
}
}
}
Minimum_should_match Match Phrase Query 精确匹配查询的短语,需要全部单词和顺序要完全一样,标点符号除外 slop 参数告诉match_phrase查询词条相隔多远扔能匹配到
1
2
3
4
5
6
7
8
9
10
11
POST movies/_search
{
"query":{
"match_phrase":{
"title":{
"query":"Matrix reloaded",
"slop":1
}
}
}
}
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
DELETE products
PUT products
{
"settings": {
"number_of_shards": 1
}
}
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }
{ "index": { "_id": 2 }}
{ "productID" : "KDKE-B-9947-#kL5","desc":"iPad" }
{ "index": { "_id": 3 }}
{ "productID" : "JODL-X-1937-#pV7","desc":"MBP" }
GET /products
POST /products/_search
{
"query": {
"term": {
"desc": {
//"value": "iPhone"
"value":"iphone"
}
}
}
}
POST /products/_search
{
"query": {
"term": {
"desc.keyword": {
//"value": "iPhone"
//"value":"iphone"
}
}
}
}
POST /products/_search
{
"query": {
"term": {
"productID": {
"value": "XHDK-A-1293-#fJ3"
}
}
}
}
POST /products/_search
{
//"explain": true,
"query": {
"term": {
"productID.keyword": {
"value": "XHDK-A-1293-#fJ3"
}
}
}
}
POST /products/_search
{
"explain": true,
"query": {
"constant_score": {
"filter": {
"term": {
"productID.keyword": "XHDK-A-1293-#fJ3"
}
}
}
}
}
#设置 position_increment_gap
DELETE groups
PUT groups
{
"mappings": {
"properties": {
"names":{
"type": "text",
"position_increment_gap": 0
}
}
}
}
GET groups/_mapping
POST groups/_doc
{
"names": [ "John Water", "Water Smith"]
}
POST groups/_search
{
"query": {
"match_phrase": {
"names": {
"query": "Water Water",
"slop": 100
}
}
}
}
POST groups/_search
{
"query": {
"match_phrase": {
"names": "Water Smith"
}
}
}
结构化搜索 结构化数据 结构化搜索是指对结构化数据的搜索 日期、布尔类型和数字都是结构化的 文本也可以是结构化的 如彩色笔可以有离散的颜色集合: 红、绿、蓝 一个博客可能被标记了标签,如分布式和搜索 电商网站上商品都有UPCs(通用产品吗)或其他唯一标识,他们都需要遵循严格规定的、结构化的格式 ES中的结构化搜索(使用term) 布尔、时间、日期和数字这类结构化数据: 有精确的格式,我们可以对这些格式进行逻辑操作。包括比较数字或时间范围,或判定两个值的大小 结构化的文本可以做精确匹配或部分匹配 term查询/prefix前缀查询 结构化结果只有"是"或"否"两个值 根据场景需要,可以决定结构化搜索是否打分 term对于查询是包含的关系,如果要查相等则需使用field.keyword 结构化精确匹配使用term,全文检索用match range查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
GET products/_search
{
"query":{
"constant_score":{
"filter":{
"range":{
"price":{
"gte":20,
"lte":30
}
}
}
}
}
}
GET products/_search
{
"query":{
"constant_score":{
"filter":{
"range":{
"date":{
"gte":"now-1d"
}
}
}
}
}
}
//查询date字段非空的数据
GET products/_search
{
"query":{
"constant_score":{
"filter":{
"exists":"date"
}
}
}
}
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#结构化搜索,精确匹配
DELETE products
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }
GET products/_mapping
#对布尔值 match 查询,有算分
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"term": {
"avaliable": true
}
}
}
#对布尔值,通过constant score 转成 filtering,没有算分
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"constant_score": {
"filter": {
"term": {
"avaliable": true
}
}
}
}
}
#数字类型 Term
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"term": {
"price": 30
}
}
}
#数字类型 terms
POST products/_search
{
"query": {
"constant_score": {
"filter": {
"terms": {
"price": [
"20",
"30"
]
}
}
}
}
}
#数字 Range 查询
GET products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"range" : {
"price" : {
"gte" : 20,
"lte" : 30
}
}
}
}
}
}
# 日期 range
POST products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"range" : {
"date" : {
"gte" : "now-1y"
}
}
}
}
}
}
#exists查询
POST products/_search
{
"query": {
"constant_score": {
"filter": {
"exists": {
"field": "date"
}
}
}
}
}
#处理多值字段
POST /movies/_bulk
{ "index": { "_id": 1 }}
{ "title" : "Father of the Bridge Part II","year":1995, "genre":"Comedy"}
{ "index": { "_id": 2 }}
{ "title" : "Dave","year":1993,"genre":["Comedy","Romance"] }
#处理多值字段,term 查询是包含,而不是等于
POST movies/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"genre.keyword": "Comedy"
}
}
}
}
}
#字符类型 terms
POST products/_search
{
"query": {
"constant_score": {
"filter": {
"terms": {
"productID.keyword": [
"QQPX-R-3956-#aD8",
"JODL-X-1937-#pV7"
]
}
}
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"match": {
"price": 30
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"term": {
"date": "2019-01-01"
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"match": {
"date": "2019-01-01"
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"constant_score": {
"filter": {
"term": {
"productID.keyword": "XHDK-A-1293-#fJ3"
}
}
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"term": {
"productID.keyword": "XHDK-A-1293-#fJ3"
}
}
}
#对布尔数值
POST products/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"avaliable": "false"
}
}
}
}
}
POST products/_search
{
"query": {
"term": {
"avaliable": {
"value": "false"
}
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"term": {
"price": {
"value": "20"
}
}
}
}
POST products/_search
{
"profile": "true",
"explain": true,
"query": {
"match": {
"price": "20"
}
}
}
}
POST products/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"must_not": {
"exists": {
"field": "date"
}
}
}
}
}
}
}
搜索的相关性算分 相关性和相关性算分 相关性-Relevance 搜索的相关性算分,描述了一个文档和查询语句匹配的程度。ES会对每个匹配查询条件的结果进行算分_score 打分的本质是排序,需要把最符合用户需求的文档排在前面。ES5前默认相关性算分采用TF-IDF,现在采用BM25 词频(TF) Term Frequency: 检索词在一篇文档中出现的频率 检索词出现的次数除以文档的总字数 度量一条查询和结果文档相关性的简单方法: 简单将搜索中每一个词的TF进行相加 TF(区块链)+TF(的)+TF(应用) Stop Word(停用词) "的"在文档中出现了很多次,但是对贡献相关度几乎没什么用处,不应该考虑他们的TF 逆文档频率IDF DF: 检索词在所有文档中出现的频率 "区块链"在相对比较少的文档中出现 "应用"在相对比较多的文档中出现 "stop word"在大量文档中出现 inverse document frequency:简单说 = log(全部文档数/检索词出现过的文档总数) TF-IDF本质就是将TF求和变成加权求和 TF(区块链)*IDF(区块链) + TF(的)*IDF(的)+TF(应用)*IDF(应用) ![](http://pic.aipp.vip/20191023164440.png) TF-IDF的概念 TF-IDF被公认为是信息检索领域最重要的发明 现代搜索引擎,对TF-IDF进行了大量细微的优化 Lucene中的TF-IDF评分公式 ![](http://pic.aipp.vip/20191023165257.png) BM25 从ES5开始,默认算法改为BM25 和经典的TF-IDF相比,当TF无限增加时,BM25算分会趋于一个数值 ![](http://pic.aipp.vip/20191023165430.png) 定制similarity 相似度算分定制 通过explain API查看TF-IDF 在ES中任意查询可以增加explain参数,可以帮助了解查询是如何打分的 boosting relevance boosting是控制相关度的一种手段 索引,字段或查询子条件 参数boost的含义 当boost>1时,打分的相关度相对性提升 当0<boost<1时,打分的权重相对性降低 当boost<0时,贡献负分 Query & filtering与多字符串多字段查询 Query Context & Filter Context 高级搜索的功能: 支持多项文本输入,针对多个字段进行搜索 搜索引擎一般也提供基于时间,价格等条件的过滤 在ES中,有Query和Filter两种不同的Context * Query Context: 相关性算分 * Filter Context: 不需要算分(Yes or No),可以利用Cache,获得更好的性能 条件组合 假设要搜索一部电影,包含以下条件 评论中包含了Guitar,用户打分高于3分,同时上映时间要在1993到2000年之间 这个搜索其实包含三段逻辑,针对不同的字段 评论字段中要包含Guitar/用户评分大于3/上映时间需要在给定的范围 同时包含这三个逻辑,并且有比较好的性能? 复合查询: bool Query bool查询 一个bool查询,是一个或多个查询子句的组合 总共包括4种子句。其中2种会影响算分,2种不影响算分 相关性并不只是全文本检索的专利。也适用于yes|no的子句,匹配的子句越多,相关性评分越高。如果多条查询子句被合并为一条符合查询语句,比如bool查询,则每个查询子句计算得出的评分会被合并到总的相关性评分中 must 必须匹配,贡献算分 should 选择性匹配,贡献算分 must_not filter_context查询语句,必须不能匹配,不贡献算分 filter filter-context必须匹配,不贡献算分 bool查询语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /products/_search
{
"query":{
"bool":{
"must":{
"term":{"price":"30"}
},
"filter":{
"term":{"avaliable":"true"}
},
"must_not":{
"range":{
"price":{"lte":10}
}
},
"should":[
{"term"<{"productId.keyword":"JODL-X-1937"}},
{"term"<{"productId.keyword":"JODL-X-19X8"}}
],
"minimum_should_match":1
}
}
}
子查询可以任意顺序出现 可以嵌套多个查询 如果你的bool查询中,没有must条件,should中必须至少满足一条查询 如何解决结构化查询-”包含而不是相等“的问题
1
2
3
4
5
6
7
8
9
10
11
12
POST movies/_search
{
"query":{
"constant_score":{
"filter":{
"term":{
"genre.keyword":"Comedy"
}
}
}
}
}
Filter Context-不影响算分 Query Context-影响算分 bool嵌套 查询语句的结构,会对相关度算分产生影响 同一层级下的竞争字段,具有相同的权重 通过嵌套bool查询,可以改变对算分的影响 控制字段的boosting Boosting是控制相关度的一种手段 索引,字段或查询子条件 参数boost的含义 当boost>1时,打分的相关度相对性提高 当0<boost<1时,打分的权重相对性降低 当boost<0时,贡献负分 Not Quite Not 要求苹果公司的产品信息优先 查询分类 terms查询用于结构化数据查询,全文用match查询,而bool属于一种符合查询,可以结合terms查询和match查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }
#基本语法
POST /products/_search
{
"query": {
"bool" : {
"must" : {
"term" : { "price" : "30" }
},
"filter": {
"term" : { "avaliable" : "true" }
},
"must_not" : {
"range" : {
"price" : { "lte" : 10 }
}
},
"should" : [
{ "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } },
{ "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } }
],
"minimum_should_match" :1
}
}
}
#改变数据模型,增加字段。解决数组包含而不是精确匹配的问题
POST /newmovies/_bulk
{ "index": { "_id": 1 }}
{ "title" : "Father of the Bridge Part II","year":1995, "genre":"Comedy","genre_count":1 }
{ "index": { "_id": 2 }}
{ "title" : "Dave","year":1993,"genre":["Comedy","Romance"],"genre_count":2 }
#must,有算分
POST /newmovies/_search
{
"query": {
"bool": {
"must": [
{"term": {"genre.keyword": {"value": "Comedy"}}},
{"term": {"genre_count": {"value": 1}}}
]
}
}
}
#Filter。不参与算分,结果的score是0
POST /newmovies/_search
{
"query": {
"bool": {
"filter": [
{"term": {"genre.keyword": {"value": "Comedy"}}},
{"term": {"genre_count": {"value": 1}}}
]
}
}
}
#Filtering Context
POST _search
{
"query": {
"bool" : {
"filter": {
"term" : { "avaliable" : "true" }
},
"must_not" : {
"range" : {
"price" : { "lte" : 10 }
}
}
}
}
}
#Query Context
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }
POST /products/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"productID.keyword": {
"value": "JODL-X-1937-#pV7"}}
},
{"term": {"avaliable": {"value": true}}
}
]
}
}
}
#嵌套,实现了 should not 逻辑
POST /products/_search
{
"query": {
"bool": {
"must": {
"term": {
"price": "30"
}
},
"should": [
{
"bool": {
"must_not": {
"term": {
"avaliable": "false"
}
}
}
}
],
"minimum_should_match": 1
}
}
}
#Controll the Precision
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "price" : "30" }
},
"filter": {
"term" : { "avaliable" : "true" }
},
"must_not" : {
"range" : {
"price" : { "lte" : 10 }
}
},
"should" : [
{ "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } },
{ "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } }
],
"minimum_should_match" :2
}
}
}
POST /animals/_search
{
"query": {
"bool": {
"should": [
{ "term": { "text": "brown" }},
{ "term": { "text": "red" }},
{ "term": { "text": "quick" }},
{ "term": { "text": "dog" }}
]
}
}
}
POST /animals/_search
{
"query": {
"bool": {
"should": [
{ "term": { "text": "quick" }},
{ "term": { "text": "dog" }},
{
"bool":{
"should":[
{ "term": { "text": "brown" }},
{ "term": { "text": "brown" }},
]
}
}
]
}
}
}
DELETE blogs
POST /blogs/_bulk
{ "index": { "_id": 1 }}
{"title":"Apple iPad", "content":"Apple iPad,Apple iPad" }
{ "index": { "_id": 2 }}
{"title":"Apple iPad,Apple iPad", "content":"Apple iPad" }
POST blogs/_search
{
"query": {
"bool": {
"should": [
{"match": {
"title": {
"query": "apple,ipad",
"boost": 1.1
}
}},
{"match": {
"content": {
"query": "apple,ipad",
"boost":
}
}}
]
}
}
}
DELETE news
POST /news/_bulk
{ "index": { "_id": 1 }}
{ "content":"Apple Mac" }
{ "index": { "_id": 2 }}
{ "content":"Apple iPad" }
{ "index": { "_id": 3 }}
{ "content":"Apple employee like Apple Pie and Apple Juice" }
POST news/_search
{
"query": {
"bool": {
"must": {
"match":{"content":"apple"}
}
}
}
}
POST news/_search
{
"query": {
"bool": {
"must": {
"match":{"content":"apple"}
},
"must_not": {
"match":{"content":"pie"}
}
}
}
}
POST news/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"content": "apple"
}
},
"negative": {
"match": {
"content": "pie"
}
},
"negative_boost": 0.5
}
}
}
单字符串多字段查询:Dis Max Query 单字符串查询的实例

分布式特性及分布式搜索的机制
集群分布式模型与脑裂问题
分布式特性
es分布式架构带来的好处
存储的水平扩容,支持PB级数据
提高系统的可用性,部分节点停止服务,整个集群的服务不受影响
es的分布式架构
不同的集群通过不同的名字来区分,默认”elasticsearch”
通过配置文件修改,或在命令行中-E cluster.name=cname进行设定
节点
节点是一个es的实例
其本质就是一个jsva进程
一台机器可以运行多个es进程
每个节点都有名字,通过配置文件配置,或者启动时候-E node.name=nname指定
每一个节点启动后,会分配一个UID,保存在data目录下

    coordinating node(协调节点)
        处理请求的节点,叫coordinating node
            路由请求到正确的节点,例如创建索引的请求,需要路由到master节点
        所有节点默认都是coordinating node
        通过将其他类型设置成false,使其成为dedicated coordinating node
    data node
        可以保存数据的节点,叫做data node
            节点启动后,默认就是数据节点。可以设置ndoe.data:false禁止
        data node的职责
            保存分片数据。在数据扩展上起至关重要的作用(由master node决定如何把分片分发到数据节点上)
        通过增加数据节点
            可以解决数据水平扩展和解决数据单点问题
    master node
        master node的职责
            处理创建,删除索引等请求/决定分片被分配到哪个结点/负责索引的创建与删除
            维护更新cluster state
        master node的最佳实践
            master 节点非常重要,在部署上需要考虑解决单点的问题
            为一个集群设置多个master节点/每个节点只承担master的单一角色
    master eligible nodes & 选主流程
        一个集群,支持配置多个master eligible节点。这些节点可以在必要时(如master节点出现故障,网络故障时)参与选主流程,成为master节点
        每个节点启动后,默认就是一个master eligible节点
            可以设置node.master:false禁止
        当集群内第一个master eligible节点启动时,它会将 自己选举成master节点
    集群状态
        集群状态信息(cluster state),维护了一个集群中,必要的信息
            所有的节点信息
            所有的索引和其相关的mapping与setting信息
            分片的路由信息
        在每个节点上都保存了集群的状态信息
        但是,只有master节点才能修改集群的状态信息,并负责同步给其它节点
            因为,任意节点都能修改信息会导致cluster state信息不一致
    master eligible nodes & 选主流程
        互相ping对方,node id低的会成为被选举的节点
        其它节点会加入集群,但是不承担master节点的角色。一旦发现被选中的主节点丢失,就会选举出新的master节点
    脑裂问题
        split-brain,分布式系统的经典网络问题,当出现网络问题,一个节点和其他结点无法连接
            node2 和 node 3会重新选举master
            node 1 自己还是作为master,组成一个集群,同时更新cluster state
            导致2个master,维护不同的cluster state。当网络恢复时,无法选择正确恢复
            ![20191015157111504988545.png](http://pic.aipp.vip/20191015157111504988545.png)
    如何避免脑裂问题    
        限定一个选举条件,设定quorum(仲裁),只有master eligible节点数大于quorum时,才能进行选举
            quorum=(master节点总数/2)+1
            当3个master eligible时,设置discovery.zen.minimum_master_nodes为2,即可避免脑裂
    从7.0开始,无需设置这个配置 
        移除minimum_master_nodes参数,让elasticsearch自己选择可以行程仲裁的节点
        典型的主节点选举现在只需要很短的时间就可以完成。集群的伸缩变得更安全、更容易,并且可能造成丢失数据的系统配置项更少了
        节点更清楚地记录他们的状态,有助于诊断为什么他们不能加入集群或为什么无法选举出主节点
    配置节点类型
        一个节点默认情况下是一个master eligible,data and ingest node
            节点类型                配置参数        默认值
            master eligible         node.master     true
            data                    node.data       true
            ingest                  node.ingest     true
            coordinating only       无               设置上面三个参数全部为flase
            machine learning        node.ml         true(需要enable x-pack)

分片与集群的故障转移
    replica shard-提高数据可用性
        数据可用性
            通过引入副本分片(replica shard)提高数据的可用性。一旦主分片丢失,副本分片可以promote成为主分片。副本分片数可以动态调整每个节点上都独有完备的数据。如果不设置副本分片,一旦出现节点硬件故障,就有可能造成数据丢失
        提升系统的读取性能
            副本分片由主分片(primary shard)同步。通过增加replica个数,一定程度可以提高读取吞吐量
    分片数的设定
        如何规划一个索引的主分片和副本分片数
            主分片数过小: 例如创建了一个primary shard的index
                如果该索引增长很快,集群无法通过增加节点实现对这个索引的数据扩展
        主分片数设置过大: 导致单个shard容量很小,引发一个节点上有过多分片,影响性能
        副本分片数设置过多: 会降低集群整体的写入性能
    单节点集群
        
1
2
3
4
5
6
7
PUT tmdb
{
"settings":{
"number_of_shards":3,
"number_of_replicas":1
}
}
副本无法分片,集群状态黄色,副本分片必须与主分片分配在不同的节点上 增加一个节点 集群状态转为绿色 集群具备故障转义能力 尝试着将replica设置成2和3,查看集群的状况 ![2019101515711162565698.png](http://pic.aipp.vip/2019101515711162565698.png) 在增加一个节点 集群具备故障转义能力 master节点会决定分片分配到哪个结点 通过计算节点,提高集群的计算能力 故障转移 3个节点共同组成。包含了1个索引,索引设置了3个primary shard和1个 replica 节点1是master节点,节点意外出现故障。集群重新选举master节点 node3 上的R0提升成P0,集群变黄 R0和R1分配,集群变绿 ![20191015157111647617237.png](http://pic.aipp.vip/20191015157111647617237.png) 集群健康状态 green : 健康,所有主分片和副本分片都可用 yellow: 亚健康,所有主分片可用,部分副本分片不可用 red: 不健康状态,部分主分片不可用 文档分布式存储 文档存储在分片上 文档会存储在具体的某个主分片和副本分片上: 例如文档1,会存储在P0和R0分片上 文档到分片的映射算法 确保文档能均匀分布在分片上,充分利用硬件资源,避免部分机器空闲,部分机器繁忙 潜在的算法 随机/Round Robin。当查询文档1,分片数很多,需要多次查询才可能查到文档1 维护文档到分片的映射关系,当文档数据量大的时候,维护成本高 实时计算,铜鼓哦文档1,自动算出,需要去哪个分片上获取文档 文档到分片的路由算法 shard = hash(_routing)%number_of_primary_shards hash算法确保文档均匀分散到分偏重 默认的_routing值是文档id 可以自行制定routing数值,例如用相同国家的商品,都分配到指定的shard 设置index settings后,primary数不能随意修改的根本原因 更新文档 ![20191015157111727718955.png](http://pic.aipp.vip/20191015157111727718955.png) > 注意区分更新文档和更新索引 删除一个文档 ![20191015157111748437920.png](http://pic.aipp.vip/20191015157111748437920.png) 分片及其生命周期 分片的内部原理 什么是ES的分片(shard) ES中最小的工作单元,即一个lucene的index 一些问题 为什么ES的搜索是近实时的(1秒后被搜到) ES如何保证在断电时数据也不会丢失 为什么删除文档,并不会立即释放空间 倒排索引的不可变性 倒排索引采用immutable design,一旦生成,不可更改 不可变性带来的好处 无需考虑并发写文件的问题,避免了锁机制带来的性能问题 一旦读入内核的文件系统缓存,便留在那里。只要文件系统由足够的空间,大部分请求就会直接请求内存,不会命中磁盘,提升了很大的性能 缓存容易生成和维护数据可以被压缩 不可变更性,带来了的挑战: 如果需要让一个新的文档可以被搜索,需要重建整个索引 lucene index 在lucene中,单个倒排索引文件被称为segment。segment是自包含的,不可变更的。多个segments汇总在一起,称为lucene的index,其对应的就是ES中的shard 当有新文档写入时,会生成新segment,查询时会同时查询所有segments,并对结果汇总。lucene中有一个文件,用来记录所有segments信息,叫做commit point。查询时会同时查询所有的segmetns,并且对结果汇总 删除的文档信息,保存在".del"文件中 segment采用不可变设计。在更新或新建文档时,先标记删除,然后创建新文档。flush的时候从标记的.del中删掉 segment会定期merge,合并成一个,同时会删除已删除的文档 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zcw0warzj30jw0fadib.jpg) 什么是refresh * 将index buffer写入segment的过程叫refresh。refresh不执行fsync操作 refresh频率: 默认1秒发生一次,可通过index.frefresh_interval配置。refresh后,数据就可以被搜索到了。这也是为什么es被称为近实时搜索 * 如果系统由大量的数据写入,那就会产生很多的segment * index buffer被占满时,会触发refresh,默认值为jvm的10% ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zd1mnnc0j314i0pujwg.jpg) 什么是transaction log * segment写入磁盘的过程相对耗时,借助文件系统缓存,refresh时,先将segment写入缓存以开放查询 * 为保证数据不丢失。所以在index文档时,同时写transaction log,高版本开始,transaction log默认落盘。每个分片有一个transaction log * 在es refresh时,index buffer被清空,transaction log不会清空 * es断电时重启时系统会从transactionlog中把数据recover ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zddq7r1kj30va0u0don.jpg) 什么是flush es flush & lucene commit flush调用时会做以下几件事情 调用refresh,index buffer清空并refresh 调用fsync,将缓存中的segments写入磁盘 清空(删除)transaction log flush操作默认30分钟调用一次 transaction log满(默认512MB)时,也会触发flush操作 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zdh5wzamj30z80u0jup.jpg) merge segments很多,需要被定期合并,这就需要merge操作 减少segments,将多个segment合并为一个/对.del中的文件做真正删除 ES和lucene会自动进行merge操作 如果需要人为强制merge,可以使用`POST my_index/_forcemerge` 剖析分布式查询及相关性算分 分布式搜索的运行机制 elasticsearch的搜索,会分两阶段进行 第一阶段-query 第二阶段-fetch query-then-fetch query阶段 用户发出搜索请求到ES节点。节点收到请求后,会以coordinating节点的身份,在6个主副分片中随机选择3个分片,发送查询请求 被选中的分片执行查询,进行排序。然后,每个分片都会返回From+Size个排序后的文档id和排序值给coordinating节点 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zejwdccoj30u00u7wm1.jpg) fetch阶段 coordinating node会将query阶段,从每个分片获取的排序后的文档id列表重新进行排序。选择From到From+Size个文档的Id 以multi get请求的方式,到相应的分片获取详细的文档数据 query then fetch 性能问题 每个分片上需要查的文档个数 = from+size 最终协调节点需要处理: number_of_shard*(from+size) 深度分页 相关性算分 每个分片都是基于自己的分片上的数据进行相关性计算。这会导致打分偏离的情况,特别是数据量很小时,相关性算分在分片之间的相互独立。当文档总数很少的情况下,如果主分片大于1,,主分片数越多,相关性算分会越不准 解决算分不准的方法 数据量不大时,可以将主分片设置为1 当数据量足够大时,只要保证文档均匀分散在各个节点上,结果一般就不会出现偏差 使用DFS Query Then Fetch 搜索的URL中指定参数"_search?search_type=dfs_query_then_fetch" 到每个分频啊把个分片的词频和文件频率进行搜集,然后完整的进行一次相关性算分,耗费更多的CPU和内存,执行性能低下,一般不建议使用 example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
DELETE message
PUT message
{
"settings": {
"number_of_shards": 20
}
}
GET message
POST message/_doc?routing=1
{
"content":"good"
}
POST message/_doc?routing=2
{
"content":"good morning"
}
POST message/_doc?routing=3
{
"content":"good morning everyone"
}
POST message/_search
{
"explain": true,
"query": {
"match_all": {}
}
}
POST message/_search
{
"explain": true,
"query": {
"term": {
"content": {
"value": "good"
}
}
}
}
POST message/_search?search_type=dfs_query_then_fetch
{
"query": {
"term": {
"content": {
"value": "good"
}
}
}
}
排序及Doc Values & Fielddata 排序 elasticsearch默认采用相关性算分对结果进行降序排序 可以通过设定sorting参数,自行设定排序 如果不指定_score,算分为Null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /index/_search
{
"size":5,
"query":{
"match_all":{
}
},
"sort":[
{
"order_date":{
"order":"desc"
}
}
]
}
排序的过程 排序是针对字段原始内容进行的。倒排索引无法发挥作用 需要用到正排索引,通过文档id和字段快速得到字段原始内容 es有两种实现方法 * fielddata * doc values(列式存储,对text类型无效) ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zyr3t63aj31yv0u0dw9.jpg) 关闭doc values 默认启用,可以通过mapping设置关闭 增加索引速度/减少磁盘空间 如果重新打开,需要重建索引 什么时候需要关闭 明确不需要做排序及聚合分析 example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#单字段排序
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"order_date": {"order": "desc"}}
]
}
#多字段排序
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"order_date": {"order": "desc"}},
{"_doc":{"order": "asc"}},
{"_score":{ "order": "desc"}}
]
}
GET kibana_sample_data_ecommerce/_mapping
#对 text 字段进行排序。默认会报错,需打开fielddata
POST /kibana_sample_data_ecommerce/_search
{
"size": 5,
"query": {
"match_all": {
}
},
"sort": [
{"customer_full_name": {"order": "desc"}}
]
}
#打开 text的 fielddata
PUT kibana_sample_data_ecommerce/_mapping
{
"properties": {
"customer_full_name" : {
"type" : "text",
"fielddata": true,
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
#关闭 keyword的 doc values
PUT test_keyword
PUT test_keyword/_mapping
{
"properties": {
"user_name":{
"type": "keyword",
"doc_values":false
}
}
}
DELETE test_keyword
PUT test_text
PUT test_text/_mapping
{
"properties": {
"intro":{
"type": "text",
"doc_values":true
}
}
}
DELETE test_text
DELETE temp_users
PUT temp_users
PUT temp_users/_mapping
{
"properties": {
"name":{"type": "text","fielddata": true},
"desc":{"type": "text","fielddata": true}
}
}
Post temp_users/_doc
{"name":"Jack","desc":"Jack is a good boy!","age":10}
#打开fielddata 后,查看 docvalue_fields数据
POST temp_users/_search
{
"docvalue_fields": [
"name","desc"
]
}
#查看整型字段的docvalues
POST temp_users/_search
{
"docvalue_fields": [
"age"
]
}
分页与遍历: from,size,search after & scroll API From/Size 默认情况下,查询按照相关度算分排序,返回前10条记录 容易理解的分页方案 From开始位置 Size期望获取文档的总数 分布式系统中深度分页的问题 ES天生就是分布式的。查询信息,但是数据分别保存在多个分片,多台机器上,ES天生就需要满足排序的需求(按照相关性算分) 当一个查询: From=990,Size=10 * 会在每个分片上都获取1000个文档。然后通过Coordinating Node聚合所有结果。最后再通过排序选取前1000个文档 * 页数越深,占用内存越多。为了避免深度分页带来的内存开销。ES有一个设定,默认限定到10000个文档 index.max_result_window 超过后报错 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zyyh78a9j30j60bign8.jpg) Search After避免深度分页的问题 避免深度分页的性能问题,可以实时获取下一页文档信息 不支持指定页数 只能往下翻 第一步搜索需要制定sort,并且保证值是唯一的(可以通过加入_id保证唯一性) 然后使用上一次,最后一个文档的sort值进行查询
1
2
3
4
5
6
7
8
9
POST users/_serach
{
...
"search_after":[13,"aA0vYGsBrR8X3IP7_wrd"],
"sort":[
{"age":"desc"},
{"_id":"asc"}
]
}
Search After是如何解决深度分页的问题 假定size是10 当查询990-1000 通过唯一排序值定位,将每次要处理的文档数都控制在10 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zz6qpx5sj30kg0dimyq.jpg) Scroll API 创建一个快照,有新的数据写入以后,无法被查询到 每次查询后,输入上一次的scroll Id 不同搜索类型和使用场景 Regular 需要实时获取顶部部分文档。例如查询最新的订单 Scroll 需要全部文档,例如导出全部数据 Pagination From和Size 如果需要深度分页,则选用Search After example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
POST tmdb/_search
{
"from": 10000,
"size": 1,
"query": {
"match_all": {
}
}
}
#Scroll API
DELETE users
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":11}
POST users/_doc
{"name":"user2","age":12}
POST users/_doc
{"name":"user2","age":13}
POST users/_count
POST users/_search
{
"size": 1,
"query": {
"match_all": {}
},
"sort": [
{"age": "desc"} ,
{"_id": "asc"}
]
}
POST users/_search
{
"size": 1,
"query": {
"match_all": {}
},
"search_after":
[
10,
"ZQ0vYGsBrR8X3IP75QqX"],
"sort": [
{"age": "desc"} ,
{"_id": "asc"}
]
}
#Scroll API
DELETE users
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":20}
POST users/_doc
{"name":"user3","age":30}
POST users/_doc
{"name":"user4","age":40}
#5m表示5分钟
POST /users/_search?scroll=5m
{
"size": 1,
"query": {
"match_all" : {
}
}
}
POST users/_doc
{"name":"user5","age":50}
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAWAWbWdoQXR2d3ZUd2kzSThwVTh4bVE0QQ=="
}
并发控制 并发控制的必要性 两个Web程序同时更新某个文档,如果缺乏有效的并发,会导致更改的数据丢失 悲观并发控制 假定有变更冲突的可能。会对资源加锁,防止冲突。例如数据库行锁 乐观并发控制 假定冲突不会发生,不会阻塞正在尝试的操作。如果数据在读写中被修改,更新将会失败。应用程序决定如何解决冲突,例如重新更新,使用新的数据,或将错误报告给客户 ES采用的是乐观并发控制 ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zzrwx2fkj30g40f20ug.jpg) ES的乐观并发控制 * ES中的文档是不可变更的。如果你更新一个文档,会将文档标记为删除,同时增加一个全新的文档。同时文档的version字段加1 * 内部版本控制 if_seq_no + if_primary_term * 使用外部版本(使用其他数据库作为主要数据存储) version+version_type = external ![](https://tva1.sinaimg.cn/large/006y8mN6ly1g7zztuec0gj30dk0e477h.jpg) 每个文档都有_seq_no、_primary_term
1
2
3
4
PUT test/_doc/3KD1tm0BnzsfJa1YdAJp?if_seq_no=1&if_primary_term=2
{
"count":"2"
}
example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DELETE products
PUT products
PUT products/_doc/1
{
"title":"iphone",
"count":100
}
GET products/_doc/1
PUT products/_doc/1?if_seq_no=1&if_primary_term=1
{
"title":"iphone",
"count":100
}
PUT products/_doc/1?version=30000&version_type=external
{
"title":"iphone",
"count":100
}

生产环境中的集群运维
生产环境常用配置和上线清单
Development vs Production Mode
从ES 5开始支持这两种运行模式
ES是通过http.host和transport.bind_host来判断模式
Bootstrap Checks
集群运行在生产模式时,启动必须通过所有Bootstrap检测,否则会启动失败
Boostrap Checks可分为两类 JVM & Linux Checks.
JVM设定
从ES6开始,只支持64位JVM
配置config/jvm.options
避免修改默认配置
将内存Xms和Xmx设置成一样,避免heap resize时引发停顿
Xms设置不要超过物理内存的50%(因为需要把剩下的50%交给lucene进行全文检索);单个节点上,最大内存建议不要超过32G内存
生产环境,JVM必须使用Server模式
关闭JVM Swapping
集群参数设定
静态设置和动态设置

            * 静态配置文件尽量简洁: 按照文档设置所有相关系统参数.elasticsearch.yml配置文件中尽量只写必备参数
        其他设置项可以通过API动态设置。动态设定分transient和persistent两种,都会覆盖elasticsearch.yaml中的设置
            * transient在集群中期后会丢失
            * persistent在集群重启后不会丢失
        配置优先级
        ![](http://pic.aipp.vip/20191016234116.png)
    系统设置
        参考Setup Elasticsearch> Important System Configuration
    最佳实践: 网络
        单个集群不要跨数据中心进行部署(不要使用WAN)
        节点之间的跳数越少越好
        如果有多块网卡,最好将transport和http绑定到不同的网卡,并设置不同的防火墙rules
        按需为coordinating node或ingest node配置负载均衡
    最佳实践: 内存设定计算实例
        内存大小要根据Node需要存储的数据来进行估算 (内存和磁盘配比)
            搜索类的比例建议: 1:16
            日之类: 1:48-1:96之间
        总数据量1T,设置一个副本=2T总数据量
            如果搜索类的项目,每个节点31*16=496G,加上预留空间,所以每个节点最多400G数据,至少需要5个数据节点
            如果是日志类项目,每个节点31*50 = 1550GB,2个数据节点即可
    最佳实践: 存储
        推荐使用SSD,使用本地存储,避免远程存储
        可以本地指定多个path.data,以支持多块磁盘
        ES本身提供了很好的HA机制;无需使用RAID1/5/10
    最佳实践: 服务器硬件
        建议使用中等配置的机器,不建议使用过于强劲的硬件配置
        不建议在一台服务器上运行多个节点
    集群配置: Throttles限流
        为Relocation和Recovery设置限流,避免过多任务对集群产生性能影响
        Recovery
            cluster.routing.allocation.node_concurrent_recoveries:2
        relocation
            cluster.routing.allocation.cluster_concurrent_rebalance:2
    集群设置: 关闭Dynamic indexes
        可以考虑关闭动态索引创建的功能
        
1
2
3
4
5
6
PUT _cluster/settings
{
"persistent":{
"action.auto_create_index":false
}
}
或通过模板设置白名单
1
2
3
4
5
6
PUT _cluster/settings
{
"persisteng":{
"action.auto_create_index":"logstash-*,.kibana*"
}
}
为Elasticsearch和Kibana配置安全功能 打开authentication & authorization 实现索引和字段级的安全控制 节点间通信加密 enable https audit log 监控elasticsearch集群 监控API ES提供了了多个监控相关的API * Node Stats: _nodes/stats * Cluster Stats: _cluster/stats * Index Stats: index_name/_stats 查看Task相关的api Pending Cluster Tasks API: `GET _cluster/pending_tasks` Task Management API: `GET _tasks(可以用来cancel一个task)` 监控Thread Pools GET _nodes/thread_pool GET _nodes/stats/thread_pool GET _cat/thread_pool?v GET _nodes/hot_threads The index & Query Slow Log 支持将分片上,Search和Fetch阶段的慢查询写入文件 支持为Query和Fetch分别定义阈值 索引级的动态设置,可以按需设置,或者通过index template统一设定 slow log文件通过log4j2.properties配置 ![](http://pic.aipp.vip/20191017000543.png) 如何创建监控dashboard * 开发es plugin,通过读取相关监控API,将数据发送到ES,或者TSDB * 使用Metricbeats搜集相关指标 * 使用kibana或grafana创建dashboard * 可以开发elasticsearch exporter,通过prometheus监控elasticsearch集群 example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# Node Stats:
GET _nodes/stats
#Cluster Stats:
GET _cluster/stats
#Index Stats:
GET kibana_sample_data_ecommerce/_stats
#Pending Cluster Tasks API:
GET _cluster/pending_tasks
# 查看所有的 tasks,也支持 cancel task
GET _tasks
GET _nodes/thread_pool
GET _nodes/stats/thread_pool
GET _cat/thread_pool?v
GET _nodes/hot_threads
GET _nodes/stats/thread_pool
# 设置 Index Slowlogs
# the first 1000 characters of the doc's source will be logged
PUT my_index/_settings
{
"index.indexing.slowlog":{
"threshold.index":{
"warn":"10s",
"info": "4s",
"debug":"2s",
"trace":"0s"
},
"level":"trace",
"source":1000
}
}
# 设置查询
DELETE my_index
//"0" logs all queries
PUT my_index/
{
"settings": {
"index.search.slowlog.threshold": {
"query.warn": "10s",
"query.info": "3s",
"query.debug": "2s",
"query.trace": "0s",
"fetch.warn": "1s",
"fetch.info": "600ms",
"fetch.debug": "400ms",
"fetch.trace": "0s"
}
}
}
GET my_index
集群运维面临的挑战 用户集群数量多,业务场景差异大 使用与配置不当,优化不够 如何让用户更加高效的正确的使用ES 如何让用户更全面的了解自己的集群的使用状况 发现问题之后,需要防患于未然 Elastc有提供support diagnostics tool-https://github.com/elastic/support-diagnostics 为什么要诊断集群的潜在问题 防患与未然 master节点/数据节点宕机-负载过高,导致节点失联 副本丢失,导致数据可靠性 受损 集群压力过大,数据写入失败 提升集群性能 数据节点负载不均衡(避免单点瓶颈) 优化分片,segment 规范操作方式(利用别名/避免dynamic mapping引发过多字段,对索引的合理性进行管控) 阿里云 -EYOU 解决集群yellow/red问题 集群健康度 分片健康 红: 至少有一个主分片没有分配 黄: 至少有一个副本分片没有分配 绿: 主副分片全部正常分配 索引健康: 最差的分片状态 集群健康: 最差的索引状态 Health相关API
1
2
3
4
5
6
GET _cluster/health 集群状态
GET _cluster/health?level=indices 所有索引的健康状态(查看有问题的索引)
GET _cluster/health/my_index 单个索引的健康状态
GET _cluster/health?level=shards 分片级的索引
GET _cluster/allocation/explain 返回第一个未分配shard的原因
GET _cat/allocation?v 磁盘占用情况
案例1 症状: 集群变红 分析: 通过allocatin explain API发现创建索引失败,因为无法找到标记相应box type的节点 解决: 删除索引,集群变绿。重新创建索引,并制定正确的routing box type,索引创建成功,集群保持绿色状态 症状2 症状: 集群变黄 分析: 通过allocation explain API发现无法在相同节点上创建副本 解决: 将索引的副本数设置为0,或者通过增加节点解决 分片没有被分配的一些原因 INDEX_CREATE: 创建索引导致。在索引的全部分频啊分配完成之前,会有短暂red,不一定代表有问题 CLUSTER_RECOVER: 集群重启阶段,会有这个问题 INDEX_REOPEN: open一个之前close的索引 DANGLING_INDEX_IMPORTED: 一个节点离开集群期间,有索引被删除。这个节点重新返回时,会导致dangling问题 常见问题与解决思路 集群变红,需要检查是否有节点离线。如果有,通常通过重启离线节点可以解决问题 由于配置导致的问题,需要修复相关的配置(比如错误的box_type,错误的副本数) 如果是测试的索引,可以直接删除 因为磁盘空间限制,分片规则(shard filtering)引发的,需要调整规则或增加节点 对于节点返回集群,导致的dangling变红,可直接删除danglinig索引 集群red&yellow问题总结 red&yellow是集群运维中常见的问题 除了集群故障,一些创建,增加副本等操作,都会导致集群短暂的red和yellow,所以监控和报警时需要设置一定的延时 通过检查节点数,使用ES提供的相关API,找到真正原因 可以指定move或reallocate分片 提供集群写性能 提高写性能的方法 写性能优化的目标: 增大吞吐量,越高越好 客户端: 多线程,批量写 可以通过性能测试,确定最佳文档数量 多线程,需要观察是否有HTTP429返回,实现retry以及线程数量的自动调节 服务器端: 单个性能问题往往是多个因素造成的。 使用更好的硬件 线程切换/堆栈状况 服务器端优化写入性能的一些手段 降低IO操作 使用ES自动生成的文档ID、一些相关的ES配置,如refresh interval 降低CPU和存储开销 减少不必要分词/避免不需要的doc_values/文档的在端你尽量保证相同的顺序,可以提高文档的压缩率 尽可能做到写入和分片的均衡负载,实现水平扩展 shard filtering/write load balancer 调整bulk线程池和队列 关闭无关的功能 如果只需要聚合不需要搜索,index设置成false 不需要算分,Norms设置成false 不要对字符串使用默认的dynamic mapping。字段过多,会对性能产生比较大的影响 index_options控制在创建倒排索引时,哪些内容会被添加到倒排索引中。优化这些设置,一定程度可以节约CPU 关闭_source,减少IO操作(适合指标性数据) 针对性能的取舍 如果要追求极致的写入速度,可以牺牲数据可靠性及搜索实时性以换取性能 牺牲可靠性: 将副本分片设置为0,写入完毕再调整回去 牺牲搜索实时性: 增加refresh interval的时间 牺牲可靠性: 修改translog配置 数据写入的过程 refresh 将文档先保存在index buffer中,以refresh_interval为间隔时间,定期清空buffer,生成segment,借助文件系统缓存的特性,先将segment放在文件系统缓存中,并开发查询,以提升搜索的实时性 translog segment没有写入磁盘即便发生宕机,数据也能恢复,默认配置是每次请求都会落盘 flush 删除旧的trans文件 生成segment并写入磁盘/更新commit point并写入磁盘。ES自动完成,可优化点不多 refresh interval 降低refresh频率 增加refresh_interval数值。默认1s,如果设置成-1,会进制自动refersh 避免过于频繁的refresh,而生成过多的segment文件 但是会降低搜索的实时性 增大静态你配置参数indices.memory.index_buffer_size 默认10%,会导致自动触发refresh translog 降低写磁盘的频率,但是会降低容灾能力 inde.translog.durability: 默认是request,每个请求都落盘。设置成async,异步写入 index.translog.sync_interval设置成60s,每分钟执行一次 index.translog.flush_threshod_size: 默认512MB,可以适当调大,当translog超过该值触发flush 分片设定 副本在写入时设定为0,完成后再增加 合理设置主分片数,确保均匀分配在所有数据节点上 index.routing.allocation.total_shards_per_node:限定每个索引在每个节点上可分配的主分片数 5个节点的集群。索引有5个主分片,1个副本,应该如何设置? (5+5)/5=2 生产环境中要适当调大这个数字,避免有些节点下线时,分片无法正常迁移 bulk,线程池和队列大小 客户端 单个bulk请求体的数据量不要太大,官方建议大约5~15MB 写入端尽量将数据轮训打到不同节点 服务端 索引创建属于计算密集型任务,应该使用固定大小的线程池来配置。来不及处理的放入队列,线程数应该配置成CPU核心数+1,避免过多的上下文切换 队列大小可以适当增加,不要过大,否则占用的内存会成为GC的负担 ![](http://pic.aipp.vip/20191017005941.png) 提升集群读性能 尽量Denormalize数据 ES不等于关系型数据库 尽可能Denormalize数据,从而获取最佳性能 使用nested类型的数据,查询速度慢很多 使用Parent/Child关系,查询速度会慢超级多 数据建模 尽量使用filter context,利用缓存机制,减少不必要的算分 结合profile,explain API分析慢查询问题,持续优化数据模型 严禁使用*开头通配符Terms查询 避免查询时脚本 可以在index文档时,使用ingest pipeline,计算并写入某字段 ![](http://pic.aipp.vip/20191017010428.png) 常见查询性能问题-使用query context ![](http://pic.aipp.vip/20191017010529.png) 聚合文档消耗内存 聚合查询会消耗内存,特别是针对很大的数据集进行聚合运算 当需要使用不同的query scope,可以使用filter bucket ![](http://pic.aipp.vip/20191017010628.png) 通配符开始的正则表达 通配符开头的正则,性能会非常糟糕,需避免使用 优化分片 避免over sharding 一个查询需要范文每一个分频啊,分片过多,会导致不必要的查询开销 结合应用场景,控制单个分片的尺寸 search: 20GB logging: 40GB force-merge read-only索引 基于时间序列的索引,将只读的索引进行force merge,减少segment数量 压力测试 ES本身提供了REST API,所以可以通过很多传统的性能测试工具 jMeter Gatling 专门为ES设计的工具 ES Pref & Elasticsearch-stress-test Elastic Rally ES Rally github.com/elastic/rally 安装运行 pip3 install esrally esrally configure 运行 esrally -distribbution-version=7.1.0 运行1000条测试数据: esrally -distribbution-version=7.1.0 --test-mode 段合并优化及注意事项 merge优化 降低分段产生的数量/频率 可将refresh intervel调整到分钟级/indices.memory.index_buffer_size(默认10%) 降低最大分段大小,避免较大的分段继续参与merge,节省系统资源(最终会有多个分段) index.merge.policy.segments_per_tier,默认为10,越小需要越多的合并操作 index.merge.policy.max_merged_segment,默认5GB,超过此大小后,就不在参与后续的合并操作 force merge 当index不再有写入操作时,建议对齐进行force merge 最终分成几个segments比较好 越少越好,最好可以force merge成一个,但是force merge会占用大量的网络IO CPU 如果不能再业务高峰期之前做完,就需要考虑增大最终的分段数 Shard的大小/index.merge.polocy.max_merged_segment的大小 ![](http://pic.aipp.vip/20191017012047.png) 缓存及使用breaker限制内存使用 ES缓存主要分三大类 Node query cache(filter context) Shard query cache(cache query结果) fielddata cache ![](http://pic.aipp.vip/20191017012329.png) Node query cache 每个节点有一个node query缓存 该节点所有shard共享,之缓存filter context相关内容 cache采用LRU算法 静态配置,需要设置在data node上 node level:indices.queries.cache.size:"10%" index level: index.queries.cache.enabled:true Shard Request Cache 缓存每个分片上的查询结果 只会换粗你设置了size=0的查询对应的结果。不会缓存hits,但是会缓存aggregations和suggestions cache key LRU算法,将整个json查询串作为key,与json对象的顺序相关 静态配置 数据节点indices.requests.cache.size:"1%" ![](http://pic.aipp.vip/20191017012648.png) fielddata cache 除text类型,默认都采用doc_values,节约了内存 aggregation的global ordinals也保存在fielddata cache中 text类型字段要打开fielddata才能对其进行聚合和排序 text经过分词,排序和聚合效果不佳,建议不要轻易使用 配置 可以控制indices。fielddata.cache。size避免产生GC(默认无限制) 管理内存的重要性 可用内存一半分配给JVM,一半留给操作系统,缓存索引文件 内存引发问题 长时间GC,影响节点,导致集群响应缓慢 OOM,导致丢节点 一些常见的内存问题 segments个数过多,导致full gc 现象: 几区年整体响应缓慢,也没有特别多的数据读写,但是发现节点再持续进行full gc 分析: 查看es的内存使用,发现segments.memory占用很大空间 解决: 通过forc merge 把segments合并为1个 建议: 对于不再写入和更新的索引,可以将其设置为只读。同时,进行force_merge操作。如果问题依旧存在,则需要考虑扩容。 field data cache过大,导致full gc 现象: 集群整体响应缓慢,也没有特别多的数据读写,但是发现节点再持续进行full gc 分析: 查看ES内存使用,发现fielddata.memory.size占用很大空间,同时数据不存在写入和更新,也执行过segments merge 解决: 将indices.fielddata.cache.size设小,重启节点,堆内存恢复正常 建议: field data cache的构建比较重,ES不会主动释放,所以这个值应该设置的保守一些。如果业务上确实需要,可以通过增加节点,扩容解决 circuit breaker 包含多种断路器,避免不合理操作引发的OOM,每个断路器可以指定内存使用的限制 ![](http://pic.aipp.vip/20191017013519.png) circuit breaker统计信息 GET /_nodes/stats/breaker? tripped大于0,说明有过熔断 ![](http://pic.aipp.vip/20191017013617.png)

https://time.geekbang.org/course/detail/197-112076

集群性能排查
查看线程池工作情况,了解实际事件消费情况
http://127.0.0.1:9200/_cat/thread_pool?v
查看集群健康
http://10.75.30.125:9200/_cat/health?v
查看集群节点状况(内存/cpu占用情况,cpu负载,各节点角色)
http://127.0.0.1:9200/_cat/nodes?v
查看集群节点磁盘使用情况
http://127.0.0.1:9200/_cat/allocation?v
查看索引使用情况,并按照磁盘使用大小倒叙排序
http://127.0.0.1:9200/_cat/indices?v&s=store.size:desc
查看集群文档数量
http://127.0.0.1:9200/_cat/count?v
查看集群每个数据节点fielddata使用的内存量
http://127.0.0.1:9200/_cat/fielddata?v
查看等待中的任务
http://127.0.0.1:9200/_cat/pending_tasks?v
查看扩展信息
http://127.0.0.1:9200/_cat/plugins?v
查看恢复情况(节点启动、节点失败、副本配置级别改动等都会进行恢复动作)
http://127.0.0.1:9200/_cat/recovery?v
查看节点上包含哪些分片
http://127.0.0.1:9200/_cat/shards
查看分片中的segment信息
http://127.0.0.1:9200/_cat/segments?v
查看模板信息
http://127.0.0.1:9200/_cat/templates?v

> _cat API每个都可以使用help参数查看帮助,例如http://127.0.0.1:9200/_cat/thread_pool?help

只有master节点能够创建索引?
开发工具中command+/ 跳转到官方文档
reindex指的是mapping发生变化,或分词器发生变化,对倒排索引进行重建

1)客户端发起数据写入请求,对你写的这条数据根据_routing规则选择发给哪个Shard。
确认Index Request中是否设置了使用哪个Filed的值作为路由参数,
如果没有设置,则使用Mapping中的配置,
如果mapping中也没有配置,则使用_id作为路由参数,然后通过_routing的Hash值选择出Shard,最后从集群的Meta中找出出该Shard的Primary节点。
2)写入请求到达Shard后,先把数据写入到内存(buffer)中,同时会写入一条日志到translog日志文件中去。
当写入请求到shard后,首先是写Lucene,其实就是创建索引。
索引创建好后并不是马上生成segment,这个时候索引数据还在缓存中,这里的缓存是lucene的缓存,并非Elasticsearch缓存,lucene缓存中的数据是不可被查询的。
3)执行refresh操作:从内存buffer中将数据写入os cache(操作系统的内存),产生一个segment file文件,buffer清空。
写入os cache的同时,建立倒排索引,这时数据就可以供客户端进行访问了。
默认是每隔1秒refresh一次的,所以es是准实时的,因为写入的数据1秒之后才能被看到。
buffer内存占满的时候也会执行refresh操作,buffer默认值是JVM内存的10%。
通过es的restful api或者java api,手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中,让数据立马就可以被搜索到。
若要优化索引速度, 而不注重实时性, 可以降低刷新频率。
4)translog会每隔5秒或者在一个变更请求完成之后,将translog从缓存刷入磁盘。
translog是存储在os cache中,每个分片有一个,如果节点宕机会有5秒数据丢失,但是性能比较好,最多丢5秒的数据。。
可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多。
可以通过配置增加transLog刷磁盘的频率来增加数据可靠性,最小可配置100ms,但不建议这么做,因为这会对性能有非常大的影响。
5)每30分钟或者当tanslog的大小达到512M时候,就会执行commit操作(flush操作),将os cache中所有的数据全以segment file的形式,持久到磁盘上去。
第一步,就是将buffer中现有数据refresh到os cache中去。
清空buffer 然后强行将os cache中所有的数据全都一个一个的通过segmentfile的形式,持久到磁盘上去。
将commit point这个文件更新到磁盘中,每个Shard都有一个提交点(commit point), 其中保存了当前Shard成功写入磁盘的所有segment。
把translog文件删掉清空,再开一个空的translog文件。
flush参数设置:
index.translog.flush_threshold_period:
index.translog.flush_threshold_size:
#控制每收到多少条数据后flush一次
index.translog.flush_threshold_ops:
6)Segment的merge操作:
随着时间,磁盘上的segment越来越多,需要定期进行合并。
Es和Lucene 会自动进行merge操作,合并segment和删除已经删除的文档。
我们可以手动进行merge:POST index/_forcemerge。一般不需要,这是一个比较消耗资源的操作。

分片对于ES而言相当关键,她是ES真正存储数据的地方,她是ES实现天然水平扩展的基础,她是保证ES高可用高性能的基石。
分片的本质是啥呢?一个ES分片本质上就是一个Lucene Index,那Lucene Index的本质又是什么呢?Lucene Index 的本质是一个倒排索引的存储结构,那倒排索引是啥呢?倒排索引是索引的一种,是相对于正排索引来命名的,索引的存在就是加快查询的速度,倒排索引通过分词和文档ID的关系来加快文档的查询速度。那索引中包括文档的所有内容嘛?这个一直是我的疑惑,如果包含,那这个索引就太重了,如果不包含,那拿到文档ID后还需要再找一次文档的内容,希望老师能回答一下!
最后Lucene Index 对应的倒排索引不是一直固定唯一的,她是有许多的小的倒排索引合并后才形成的一个唯一的倒排索引。小的倒排索引在Lucene中叫做Segment,单个的Segment倒排索引文件是自包含的,不可变的。当有新文档写入时,并且执行了Refresh,就会生产一个新的Segment倒排索引文件。在Lucene中有一个文件,用来记录所有Segment倒排索引文件的信息,这个文件叫做Commmit Point。
在查询时会查询所有Segment倒排索引文件,并对结果汇总。
当删除文件信息时会保存在.del文件之中,查询后进行过滤,所以,被删除的文件并没有立刻被删除,只是记录到一个文件之中了,这是为什么删除ES文档时为什么磁盘空间不降反升的原因。
Segment倒排索引文件会定期合并,最终合并成为一个Segment倒排索引文件,同时也会真正的清除已删除的文件。此时才会真正的释放出来被删除的文档所占用的磁盘空间。
至此分片和倒排索引都联系起来了,不过还是那个疑惑没解开,倒排索引中是否包括所有的文档内容?
我猜测不包含,通过倒排索引只是拿到了查询的分词和文档ID的关系,还需要根据文档ID获取文档的内容。否则这个倒排索引多重呀!当然,文档ID是文档的唯一标识,拿到文档ID其实等于拿到了文档的内容。
作者回复: 👍

分布式检索
搜索被执行成一个两阶段过程,我们称之为query then fetch
查询阶段
在初始 查询阶段 时, 查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的 优先队列。
当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点。 这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端
查询阶段包含以下三个步骤:
客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
Node 3 将查询请求转发到索引的每个主分片或副本分片中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。

    https://www.elastic.co/guide/cn/elasticsearch/guide/cn/_query_phase.html
取回阶段
    分布式阶段由以下步骤构成:
        协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
        每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。
        一旦所有的文档都被取回了,协调节点返回结果给客户端。
https://www.elastic.co/guide/cn/elasticsearch/guide/current/distributed-search.html#distributed-search

分布式原理
一个lucene index对应ES中的一个shard
refresh操作
将index buffer内容刷新至系统file cache中

什么时间执行refresh操作
    默认每隔1秒执行一次

什么是translog
    translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变更操作。
    这是一项WAL技术,类似mysql中的redo log(Write-Ahaed Logging)


translog根据配置有两种持久化策略,默认为同步操作
    1.同步操作,每次请求后fsync到磁盘,在fsync成功后返回客户端200
    2.异步操作,可以是每N秒(默认5秒)被刷新到磁盘

flush操作
    index buffer文档被写入新的段,并清空index buffer
    文件系统缓存通过fsync刷新至磁盘,并生成新的commit point
    translog清空

什么时候执行flush操作
    translog达到最大值
    主动调用flush
    30分钟自动flush
    translog过大(默认512MB)

https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html#translog

安全
-Expack.security.enabled=true
-Expack.security.transport.ssl.enabled

bin/elasticsearch-setup-passwords interactive -u "https://10.75.30.125:9201"

/usr/share/elasticsearch/elastic-stack-ca.p12

searchwb