ES中nest join

nested 类型是一个特殊object数据类型,允许数组的object的字段可以被独立的查询出来。

数据类型是如何被封装的

在lucene中没有嵌套object的概念,所以ES的用一个简单的数据数据列表来表示一个复杂的层次数据实体,例如一个博客和评论的实体:

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
PUT  nesttest/_doc
{
"blog_title": "开篇-es的nest的使用",
"blog_content": "从本篇文章开始,我会分享给大家canvas绘制的各种基础图形和酷炫的图形",
"tags": [
"Java",
"es"
],
"hit_count": 5,
"commet": [
{
"commet_user": "tom",
"commet_content": "good Job",
"commet_time": "2019-02-26",
"commet_location": "beijing"
},
{
"commet_user": "john",
"commet_content": "clearly,tks",
"commet_time": "2019-02-23"
,
"commet_location": "shanghai"
},
{
"commet_user": "lily",
"commet_content": "it's too hard ",
"commet_time": "2019-02-22"
,
"commet_location": "shenzhen"
}
],
"create_time": "2019-02-26"
}

其中commet类型会被转化成一个数组的形式如下:

1
2
3
4
5
6
{
...
"commet.commet_user":["tom","john","lily"],
...
"commet.commet_location":["beijing","shanghai","shenzhen"],
}

而当执行查询的时候:

1
2
3
4
5
6
7
8
9
10
11
12
GET nesttest/_search
{
"query": {
"bool": {
"must": [
{ "match": { "commet.commet_user": "john" }},
{ "match": { "commet.commet_location": "shenzhen" }}
]
}
}
}

发现查询结果如下:

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
{
...

"hits" : [
{
"_index" : "nest",
"_type" : "_doc",
"_id" : "pe0iKWkBulkJdQfMSgyV",
"_score" : 0.5753642,
"_source" : {
"blog_title" : "2019-01-05",
"blog_content" : "从本篇文章开始,我会分享给大家canvas绘制的各种基础图形和酷炫的图形",
"tags" : [
"Java",
"es"
],
"hit_count" : 5,
"commet" : [
{
"commet_user" : "tom",
"commet_content" : "good Job",
"commet_time" : "2019-02-26",
"commet_location" : "beijing"
},
{
"commet_user" : "john",
"commet_content" : "clearly,tks",
"commet_time" : "2019-02-23",
"commet_location" : "shanghai"
},
{
"commet_user" : "lily",
"commet_content" : "it's too hard ",
"commet_time" : "2019-02-22",
"commet_location" : "shenzhen"
}
],
"create_time" : "2019-02-26"
}
}
]
}
}

上面把所有的结果都列出来了, 但是 john的不在shenzhen啊?所以需要把commet定义为nested 类型。

定义nested类型

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
PUT nest_new/_mapping/_doc
{
"properties": {
"blog_title": {
"type": "text"
},
"blog_content": {
"type": "text"
},
"tags": {
"type": "keyword"
},
"hit_count": {
"type": "keyword"
},
"commet": {
"type": "nested",//这里
"properties": {
"commet_user": {
"type": "text"
},
"commet_content": {
"type": "text"
},
"commet_time": {
"type": "date"
},
"commet_location": {
"type": "keyword"
}
}
},
"create_time": {
"type": "date"
},
"datachange_lasttime": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}

ES嵌套深度被限制在50层

ES 中join的使用

引出问题

在mysql中,可以使用join来实现表与表之间的数据连接,在es中如何实现这个问题?

相对于mysql来说,es有几个不同的地方

  1. 不支持跨index的join
  2. 一个index只能包含一个类型
  3. 分布式的存储方式,对于数据的搜寻造成障碍

对于上面的几个问题,es的解决方案是**在一个索引下,借助父子关系,实现类似Mysql中多表关联的操作**

定义类型和join索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUT myorder
{
"mappings": {
"_doc": {
"properties": {
"order_join": {
"type": "join",
"relations": {
"order": "suborder"
}
}
}
}
}
}

定义join关系为order_join,其中order是父文档,suborder是子文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
put  myorder/_mapping/_doc
{
"properties": {
"orderId": {
"type": "keyword"
},
"shortTime": {
"type": "date"
},
"name": {
"type": "keyword"
},
"amount": {
"type": "double"
},
"desc": {
"type": "text"
}
}
}

插入主单数据

1
2
3
4
5
6
7
8
9
PUT myorder/_doc/10001
{
"shortTime": "2019-01-05",
"orderId": "10001",
"name": "user2",
"amount": 123.09,
"desc": "其他收入",
"order_join": "order"
}

order_join定义为order类型

插入子单数据

使用自定义ID用PUT方法

1
2
3
4
5
6
7
8
9
10
11
12
POST myorder/_doc?routing=1
{
"shortTime": "2019-01-05",
"orderId": "10001",
"name": "user2",
"amount": 12.09,
"desc": "收入",
"order_join": {
"name": "suborder",
"parent":"10001"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

POST myorder/_doc?routing=1
{
"shortTime": "2019-01-05",
"orderId": "10002",
"name": "user2",
"amount": 122.09,
"desc": "收入",
"order_join": {
"name": "suborder",
"parent":"10001"
}
}

建立父子关系索引,routing 参数是必须的,因为父子文档必须在同一个分片上

查询主单

1
2
3
4
5
6
7
8
9
10
11
12
13
GET myorder/_search
{
"query": {
"has_child" : {
"type" : "suborder",
"query" : {
"match_all" : {

}
}
}
}
}

查询子单

1
2
3
4
5
6
7
8
9
10
11
12
13
GET myorder/_search
{
"query": {
"has_parent" : {
"parent_type" : "order",
"query" : {
"match_all" : {

}
}
}
}
}

聚合查询

  • 主单聚合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    GET myorder/_search
    {
    "query": {
    "parent_id": {
    "type": "suborder",
    "id": "10001"
    }
    },
    "aggs": {
    "parents12312": {
    "terms": {
    "field": "order_join#order"
    },
    "aggs": {
    "sumAmount": {
    "stats": {
    "field": "amount"
    }
    }
    }
    }
    }
    }

  • 子单聚合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    GET myorder/_search
    {
    "size": 0,
    "aggs": {
    "parent": {
    "children": {
    "type": "suborder"
    },
    "aggs": {
    "sumAmount": {
    "stats": {
    "field": "amount"
    }
    }
    }
    }
    }
    }
  • 聚合加筛选:

    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
    GET myorder/_search
    {
    "query": {
    "has_child" : {
    "type" : "suborder",
    "query" : {
    "match_all" : {

    }
    }
    }
    },
    "aggs": {
    "parent": {
    "children": {
    "type": "suborder"
    },
    "aggs": {
    "fields": {
    "terms": {
    "field": "orderId"
    },
    "aggs": {
    "sumAmount": {
    "sum": {
    "field": "amount"
    }
    },
    "having": {
    "bucket_selector": {
    "buckets_path": {
    "orderCount": "_count",
    "sumAmount": "sumAmount"
    },
    "script": {
    "source": "params.sumAmount >= 100 && params.orderCount >=0"
    }
    }
    }
    }
    }
    }
    }
    }
    }

定义一对多的索引


一对一的索引模型很难满足日常业务的数据处理,es也支持一对多的join

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PUT myorder
{
"mappings": {
"_doc": {
"properties": {
"order_join": {
"type": "join",
"relations": {
"order": ["suborder1", "suborder2"],
"suborder2":"suborder3"
}
}
}
}
}
}

上面的索引的关联的关系如下:

1
2
3
4
5
      order
/ \
suborder1 suborder2
\
suborder3

es反向索引

传统对比

在传统的DB中,存储的数据都是一种树的结构,es使用的反向索引,简单的对比如下:

传统DB:

id name
1 user
2 user1
3 user3
4 user
5 user1
6 user2

ES存储格式:

key doc
user 1,4
user1 2,5
user2 6
user3 3

分词功能

ES高级查询

高级查询

范围查询

1
2
3
4
5
6
7
8
9
10
{ 
"query": {
"range": {
"amount": {
"gte" :1,
"lte":100
}
}
}
}

相当于 amount>=1 and amount<=100

短语查询

1
2
3
4
5
6
7
{
"query": {
"match_phrase": {
"desc": "收入"
}
}
}

script查询

script查询 可以对查询出的字段进行再次计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
GET order/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"test1": {
"script": {
"lang": "painless",
"source": "doc['amount'].value * 2"
}
},
"test2": {
"script": {
"lang": "painless",
"params": {
"factor": 2
},
"source": "doc['amount'].value + params.factor"
}
}
}
}

过滤和查询


区别:

Filter:在查询过程中,Filter只判断该文档是否满足条件,只有YES或者NO。
ES会对它的结果进行缓存,所以相较于Query而言Filter的速度会更快一些。

Query: 除了问YES或NO,还会问匹配的程度。

过滤查询已被弃用,并在ES 5.0中删除。现在使用bool查询代替。

bool 查询是一个组合查询,返回一个bool值。 包含must,should,filter等查询

must:相当于and,必须满足

should:相当于or,代表或者意思  

filter:是一个bool表达式。    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"query": {
"bool": {
"must": {
"match":{"desc": "收入"}
},
"should": {
"match":{"name": "user"}
},
"filter":{
"range":{
"amount":{
"gte" :10,
"lte":50
}
}
}
}
}
}

相当于mysql中的 1=1 and ((desc like ‘%收入%’ and amount>=10 and amount<=50>) or name =’user’)

聚合

在mysql中,聚合用group by,对于聚合后的计算用sum,avg等聚合函数计算,在es中,groupby 后面的字段称为桶,sum等聚合函数称为指标。
如:

1
select sex,sum(age) from user group by sex 

上面的sql中,sex和sum都是查询的指标,sex是桶。

聚合的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"size": 0, //不显示原来数据
"aggs": {
"buckets": {
"terms": {
"field": "orderId", //需要聚合的字段,桶
"order":{"sumAmount" : "asc" }//按查询结果排序
},
"aggs": { //聚合后需要怎么处理字段,成为指标
"sumAmount": { // 字段
"sum": {
"field": "amount"
}
}
}
}
}
}

对于聚合来说,es中的聚合函数有,sum,avg,stats,max,min等,聚合方式可以归为以下几类:

  1. 单字段单指标聚合
  2. 单字段多指标聚合
  3. 多字段单指标聚合
  4. 聚合后筛选

单字段单指标聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"size": 0,
"aggs": {
"buckets": {
"terms": {
"field": "orderId",
"order":{"sumAmount.avg" : "asc" }
},
"aggs": {
"sumAmount" : {
"stats" : { "field" : "amount" }
}
}
}
}
}

单字段多指标聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"size": 0,
"aggs": {
"bucket1": {
"terms": {
"field": "orderId",
"order":{"sumAmount" : "asc" }
},
"aggs": {
"sumAmount": {
"sum": {
"field": "amount"
}
},
"avgAmount":{
"avg": {
"field": "amount"
}
}
}
}
}
}

多字段单指标聚合

对索引中的两个字段一起聚合,相当于group by xxx,yyy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"size": 0,
"aggs": {
"bulket1": {
"terms": {
"field": "orderId"
},
"aggs": {
"bulket2": {
"terms": {
"field": "name"
},
"aggs": {
"sumAmount": {
"sum": {
"field": "amount"
}
}
}
}
}
}
}
}

聚合后的筛选:

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
{
"size": 0,
"aggs": {
"groupUserId": {
"terms": {
"field": "shortTime"
},
"aggs": {
"sumAmount": {
"sum": {
"field": "amount"
}
},
"having": {
"bucket_selector": {
"buckets_path": {
"orderCount": "_count",
"sumAmount": "sumAmount"
},
"script": {
"source": "params.sumAmount >= 100 && params.orderCount >=2"
}
}
}
}
}
}
}

ES中有一个区别于传统DB的聚合方式,对索引中的两个字段分别聚合,相当于mysql中group by 'xxx', group by 'yyy',统计后的结果分布在各个桶里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"size": 0,
"aggs": {
"bulket1": {
"terms": {
"field": "shortTime"
}
},
"bulket2": {
"terms": {
"field": "name"
}
}
}
}

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
"aggregations" : {
"bulket2" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "user",
"doc_count" : 4
},
{
"key" : "user1",
"doc_count" : 2
}
]
},
"bulket1" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 1546905600000,
"key_as_string" : "2019-01-08T00:00:00.000Z",
"doc_count" : 3
},
{
"key" : 1546646400000,
"key_as_string" : "2019-01-05T00:00:00.000Z",
"doc_count" : 2
},
{
"key" : 1546992000000,
"key_as_string" : "2019-01-09T00:00:00.000Z",
"doc_count" : 1
}
]
}
}

也可以多个字段各自统计

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
{
"size": 0,
"aggs": {
"bucket1": {
"terms": {
"field": "orderId",
"order":{"sumAmount" : "asc" }
},
"aggs": {
"sumAmount": {
"sum": {
"field": "amount"
}
}
}
},
"bucket2": {
"terms": {
"field": "name",
"order":{"avgAmount" : "asc" }
},
"aggs": {
"avgAmount": {
"sum": {
"field": "amount"
}
}
}
}
}
}

聚合后结果如下:

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
"aggregations" : {
"bucket2" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "user1",
"doc_count" : 2,
"avgAmount" : {
"value" : 137.07
}
},
{
"key" : "user",
"doc_count" : 4,
"avgAmount" : {
"value" : 246.18
}
}
]
},
"bucket1" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "10000",
"doc_count" : 3,
"sumAmount" : {
"value" : 51.16
}
},
{
"key" : "10001",
"doc_count" : 3,
"sumAmount" : {
"value" : 332.09000000000003
}
}
]
}
}

ES配置详解和主从搭建

配置详细

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

##################### Elasticsearch Configuration Example #####################
#
# 只是挑些重要的配置选项进行注释,其实自带的已经有非常细致的英文注释了!
# https://www.elastic.co/guide/en/elasticsearch/reference/current/modules.html
#
################################### Cluster ###################################
# 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的.
# es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。
# cluster.name可以确定你的集群名称,当你的elasticsearch集群在同一个网段中elasticsearch会自动的找到具有相同cluster.name的elasticsearch服务.
# 所以当同一个网段具有多个elasticsearch集群时cluster.name就成为同一个集群的标识.

# cluster.name: elasticsearch

#################################### Node #####################################
# https://www.elastic.co/guide/en/elasticsearch/reference/5.1/modules-node.html#master-node
# 节点名称同理,可自动生成也可手动配置.
# node.name: node-1

# 允许一个节点是否可以成为一个master节点,es是默认集群中的第一台机器为master,如果这台机器停止就会重新选举master.
# node.master: true

# 允许该节点存储数据(默认开启)
# node.data: true

# 配置文件中给出了三种配置高性能集群拓扑结构的模式,如下:
# 1. 如果你想让节点从不选举为主节点,只用来存储数据,可作为负载器
# node.master: false
# node.data: true
# node.ingest: true #默认true

# 2. 如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器
# node.master: true
# node.data: false
# node.ingest: true

# 3. 如果想让节点既不称为主节点,又不成为数据节点,那么可将他作为搜索器,从节点中获取数据,生成搜索结果等
# node.master: false
# node.data: false
# node.ingest: true
#

# 4. 仅作为协调器
# node.master: false
# node.data: false
# node.ingest: false

# 监控集群状态有一下插件和API可以使用:
# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
# Node Info API [http://localhost:9200/_nodes] or GUI tools # such as <http://www.elasticsearch.org/overview/marvel/>,


# A node can have generic attributes associated with it, which can later be used
# for customized shard allocation filtering, or allocation awareness. An attribute
# is a simple key value pair, similar to node.key: value, here is an example:
# 每个节点都可以定义一些与之关联的通用属性,用于后期集群进行碎片分配时的过滤
# node.rack: rack314

# 默认情况下,多个节点可以在同一个安装路径启动,如果你想让你的es只启动一个节点,可以进行如下设置
# node.max_local_storage_nodes: 1

#################################### Index ####################################
# 设置索引的分片数,默认为5
#index.number_of_shards: 5

# 设置索引的副本数,默认为1:
#index.number_of_replicas: 1

# 配置文件中提到的最佳实践是,如果服务器够多,可以将分片提高,尽量将数据平均分布到大集群中去
# 同时,如果增加副本数量可以有效的提高搜索性能
# 需要注意的是,"number_of_shards" 是索引创建后一次生成的,后续不可更改设置
# "number_of_replicas" 是可以通过API去实时修改设置的

#################################### Paths ####################################
# 配置文件存储位置
# path.conf: /path/to/conf

# 数据存储位置(单个目录设置)
# path.data: /path/to/data
# 多个数据存储位置,有利于性能提升
# path.data: /path/to/data1,/path/to/data2

# 临时文件的路径
# path.work: /path/to/work

# 日志文件的路径
# path.logs: /path/to/logs

# 插件安装路径
# path.plugins: /path/to/plugins

#################################### Plugin ###################################
# 设置插件作为启动条件,如果一下插件没有安装,则该节点服务不会启动
# plugin.mandatory: mapper-attachments,lang-groovy

################################### Memory ####################################
# 当JVM开始写入交换空间时(swapping)ElasticSearch性能会低下,你应该保证它不会写入交换空间
# 设置这个属性为true来锁定内存,同时也要允许elasticsearch的进程可以锁住内存,linux下可以通过 `ulimit -l unlimited` 命令
# bootstrap.mlockall: true

# 确保 ES_MIN_MEM 和 ES_MAX_MEM 环境变量设置为相同的值,以及机器有足够的内存分配给Elasticsearch
# 注意:内存也不是越大越好,一般64位机器,最大分配内存别才超过32G

############################## Network And HTTP ###############################
# 设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0
# network.bind_host: 192.168.0.1 #只有本机可以访问http接口

# 设置其它节点和该节点交互的ip地址,如果不设置它会自动设置,值必须是个真实的ip地址
# network.publish_host: 192.168.0.1

# 同时设置bind_host和publish_host上面两个参数
# network.host: 192.168.0.1 #绑定监听IP

# 设置节点间交互的tcp端口,默认是9300
# transport.tcp.port: 9300

# 设置是否压缩tcp传输时的数据,默认为false,不压缩
# transport.tcp.compress: true

# 设置对外服务的http端口,默认为9200
# http.port: 9200

# 设置请求内容的最大容量,默认100mb
# http.max_content_length: 100mb

# 使用http协议对外提供服务,默认为true,开启
# http.enabled: false

###################### 使用head等插件监控集群信息,需要打开以下配置项 ###########
# http.cors.enabled: true
# http.cors.allow-origin: "*"
# http.cors.allow-credentials: true

################################### Gateway ###################################
# gateway的类型,默认为local即为本地文件系统,可以设置为本地文件系统
# gateway.type: local

# 下面的配置控制怎样以及何时启动一整个集群重启的初始化恢复过程
# (当使用shard gateway时,是为了尽可能的重用local data(本地数据))

# 一个集群中的N个节点启动后,才允许进行恢复处理
# gateway.recover_after_nodes: 1

# 设置初始化恢复过程的超时时间,超时时间从上一个配置中配置的N个节点启动后算起
# gateway.recover_after_time: 5m

# 设置这个集群中期望有多少个节点.一旦这N个节点启动(并且recover_after_nodes也符合),
# 立即开始恢复过程(不等待recover_after_time超时)
# gateway.expected_nodes: 2

############################# Recovery Throttling #############################
# 下面这些配置允许在初始化恢复,副本分配,再平衡,或者添加和删除节点时控制节点间的分片分配
# 设置一个节点的并行恢复数
# 1.初始化数据恢复时,并发恢复线程的个数,默认为4
# cluster.routing.allocation.node_initial_primaries_recoveries: 4

# 2.添加删除节点或负载均衡时并发恢复线程的个数,默认为2
# cluster.routing.allocation.node_concurrent_recoveries: 2

# 设置恢复时的吞吐量(例如:100mb,默认为0无限制.如果机器还有其他业务在跑的话还是限制一下的好)
# indices.recovery.max_bytes_per_sec: 20mb

# 设置来限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5
# indices.recovery.concurrent_streams: 5
# 注意: 合理的设置以上参数能有效的提高集群节点的数据恢复以及初始化速度

################################## Discovery ##################################
# 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点.默认为1,对于大的集群来说,可以设置大一点的值(2-4)
# discovery.zen.minimum_master_nodes: 1
# 探查的超时时间,默认3秒,提高一点以应对网络不好的时候,防止脑裂
# discovery.zen.ping.timeout: 3s

# For more information, see
# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>

# 设置是否打开多播发现节点.默认是true.
# 当多播不可用或者集群跨网段的时候集群通信还是用单播吧
# discovery.zen.ping.multicast.enabled: false

# 这是一个集群中的主节点的初始列表,当节点(主节点或者数据节点)启动时使用这个列表进行探测
# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]

# Slow Log部分与GC log部分略,不过可以通过相关日志优化搜索查询速度

################ X-Pack ###########################################
# 官方插件 相关设置请查看此处
# https://www.elastic.co/guide/en/x-pack/current/xpack-settings.html
#
############## Memory(重点需要调优的部分) ################
# Cache部分:
# es有很多种方式来缓存其内部与索引有关的数据.其中包括filter cache

# filter cache部分:
# filter cache是用来缓存filters的结果的.默认的cache type是node type.node type的机制是所有的索引内部的分片共享filter cache.node type采用的方式是LRU方式.即:当缓存达到了某个临界值之后,es会将最近没有使用的数据清除出filter cache.使让新的数据进入es.

# 这个临界值的设置方法如下:indices.cache.filter.size 值类型:eg.:512mb 20%。默认的值是10%。

# out of memory错误避免过于频繁的查询时集群假死
# 1.设置es的缓存类型为Soft Reference,它的主要特点是据有较强的引用功能.只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收.另外,这些引用对象还能保证在Java抛出OutOfMemory异常之前,被设置为null.它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory.在es的配置文件加上index.cache.field.type: soft即可.

# 2.设置es最大缓存数据条数和缓存失效时间,通过设置index.cache.field.max_size: 50000来把缓存field的最大值设置为50000,设置index.cache.field.expire: 10m把过期时间设置成10分钟.
# index.cache.field.max_size: 50000
# index.cache.field.expire: 10m
# index.cache.field.type: soft

# field data部分&&circuit breaker部分:
# 用于fielddata缓存的内存数量,主要用于当使用排序,faceting操作时,elasticsearch会将一些热点数据加载到内存中来提供给客户端访问,但是这种缓存是比较珍贵的,所以对它进行合理的设置.

# 可以使用值:eg:50mb 或者 30%(节点 node heap内存量),默认是:unbounded #indices.fielddata.cache.size: unbounded
# field的超时时间.默认是-1,可以设置的值类型: 5m #indices.fielddata.cache.expire: -1

# circuit breaker部分:
# 断路器是elasticsearch为了防止内存溢出的一种操作,每一种circuit breaker都可以指定一个内存界限触发此操作,这种circuit breaker的设定有一个最高级别的设定:indices.breaker.total.limit 默认值是JVM heap的70%.当内存达到这个数量的时候会触发内存回收

# 另外还有两组子设置:
#indices.breaker.fielddata.limit:当系统发现fielddata的数量达到一定数量时会触发内存回收.默认值是JVM heap的70%
#indices.breaker.fielddata.overhead:在系统要加载fielddata时会进行预先估计,当系统发现要加载进内存的值超过limit * overhead时会进行进行内存回收.默认是1.03
#indices.breaker.request.limit:这种断路器是elasticsearch为了防止OOM(内存溢出),在每次请求数据时设定了一个固定的内存数量.默认值是40%
#indices.breaker.request.overhead:同上,也是elasticsearch在发送请求时设定的一个预估系数,用来防止内存溢出.默认值是1

# Translog部分:
# 每一个分片(shard)都有一个transaction log或者是与它有关的预写日志,(write log),在es进行索引(index)或者删除(delete)操作时会将没有提交的数据记录在translog之中,当进行flush 操作的时候会将tranlog中的数据发送给Lucene进行相关的操作.一次flush操作的发生基于如下的几个配置
#index.translog.flush_threshold_ops:当发生多少次操作时进行一次flush.默认是 unlimited #index.translog.flush_threshold_size:当translog的大小达到此值时会进行一次flush操作.默认是512mb
#index.translog.flush_threshold_period:在指定的时间间隔内如果没有进行flush操作,会进行一次强制flush操作.默认是30m #index.translog.interval:多少时间间隔内会检查一次translog,来进行一次flush操作.es会随机的在这个值到这个值的2倍大小之间进行一次操作,默认是5s
#index.gateway.local.sync:多少时间进行一次的写磁盘操作,默认是5s

# 以上的translog配置都可以通过API进行动态的设置 - See more at: http://bigbo.github.io/pages/2015/04/10/elasticsearch_config/#sthash.AvOSUcQ4.dpuf

主从搭建

Elasticsearch head的安装

Elasticsearch head是操作es的界面,可以在可视化界面上面管理es的索引和类型。

  1. 下载 es head的源码文件 https://github.com/mobz/elasticsearch-head

  2. 下载安装nodejs,执行npm -v

    1
    2
    3
    4
    $ npm -v

    5.5.1

  3. 在源码文件中,运行npm install,会下载对应的依赖文件

    npminstall

  4. 等运行完成,运行npm run start,访问9100端口,链接状态是灰色,说明没有是实例链接成功

    npminstall

  5. 修改es的配置文件

    因为es的端口是9200,访问会存在跨域的问题,需要允许es跨域访问,在es的config目录下修改elasticsearch.yml文件。

    1
    2
    3
    4
    5
    6
    #开启跨域访问支持,默认为false
    http.cors.enabled: true

    #跨域访问允许的域名地址,(允许所有域名)以上使用正则
    http.cors.allow-origin: /.*/

  6. 打开es链接上对应的服务器。

    npminstall

elasticsearch快速入门

ES是什么

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。我们可以这么去理解:

  • 分布式的实时文件存储,每个字段都被索引并可被搜索
  • 分布式的实时分析搜索引擎
  • 可以扩展到上百台服务器,处理PB级结构化或非结构化数据

索引,类型,文档的含义

在搜索引擎之前,使用的基本上都是关系型的数据库(mysql,mssql,oracle),在关系型数据库中有Database,Table和row.

对于es来说,有Index ,Type,Document来与之对应,关系如下:
















关系数据库 数据库
ES Index Type Document


注意: 每个 Index (即数据库)的名字必须是小写。
Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type

Index管理


创建Index

1
2
3

put /fwdatabase

成功返回:

1
2
3
4
5
6
{
"acknowledged":true,
"shards_acknowledged":true,
"index":"fwdatabase"
}

查询系统Index

1
2
3

GET /_cat/indices?v

es入门

或者用_mget返回json信息

1
GET /_mget   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"docs": [
{
"_index": "fwdatabase",
"_type": "order",
"_id": "nCYUrGgBn1FEHZKMQWsk",
"found": false
},
{
"_index": "fwdatabase1",
"_type": "order",
"_id": "myYUrGgBn1FEHZKMQWsk",
"_version": 1,
"found": true,
"_source": {}
}
]
}

删除Index

1
2
3

delete /fwdatabase

Type 创建和删除


新建type

在关系型数据库中,我们需要指定表字段和字段的类型,在ES也可以松散的定义数据,也可以订制指定的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
put  /fwdatabase/_mapping/order
{
"properties": {
"orderId": {
"type": "keyword"
},
"shortTime": {
"type": "date"
},
"name": {
"type": "keyword"
},
"amount": {
"type": "double"
},
"desc": {
"type": "text"
}
}
}

curl -X PUT -H ‘Content-type: application/json’ -d ‘{“properties”:{“orderId”:{“type”:”keyword”},”shortTime”:{“type”:”keyword”},”amount”:{“type”:”double”},”desc”:{“type”:”text”}}}’ http://localhost:9200/fwdatabase/_mapping/order

查询type

  1. 查询Index所包含的Type

    1
    2
    3

    GET /_mapping?pretty=true

    es入门

  2. 其他一些查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /_search
    在所有索引的所有类型中搜索

    /gb/_search
    在索引gb的所有类型中搜索

    /gb,us/_search
    在索引gb和us的所有类型中搜索

    /g*,u*/_search
    在以g或u开头的索引的所有类型中搜索

    /gb/user/_search
    在索引gb的类型user中搜索

    /gb,us/user,tweet/_search
    在索引gb和us的类型为user和tweet中搜索

    /_all/order,bill/_search
    在所有索引中搜索order,bill类型

修改type和删除type

修改Type一般可以只对数据进行局部更新,或者删除当前Index后重新创建一个新的type,此处略去不去过多的讲述。

document的管理


document相当于mysql中的行数据,我们可以通过接口对es的数据进行管理。

插入数据

  1. 单条插入数据

    1
    2
    3
    4
    5
    6
    7
    POST  /fwdatabase/order
    {
    "orderId":"10000",
    "name":"test",
    "amount":12.09,
    "desc":"测试"
    }

    插入成功返回信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "fwdatabase", //index名称
    "_type": "order", //类型名称
    "_id": "aI9AvGgBpdtUL3hsAU2l", //生成的id
    "_version": 1,//版本号
    "result": "created",
    "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 2
    }

    插入的数据可以通过_search接口查询出来:

    1
    2
    3

    GET /fwdatabase/order/_search

    查询结果和相关注释如下:

    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
    {
    "took": 4, //耗时
    "timed_out": false,//是否超时
    "_shards": { //分片信息
    "total": 5, //分片数量
    "successful": 5,//
    "skipped": 0,
    "failed": 0
    },
    "hits": {
    "total": 1,//查询命中的记录数
    "max_score": 1,
    "hits": [
    {
    "_index": "fwdatabase",//索引名称
    "_type": "order",
    "_id": "RH0AvWgBNfbilfsxyX-V",//自增ID
    "_score": 1,//相关得分
    "_source": {//返回数据
    "orderId": "10000",
    "name": "test",
    "amount": 12.09,
    "desc": "测试"
    }
    }
    ]
    }
    }

其中 _index 和 _type是对应的索引和类型的名称,_id是系统自动指定的唯一标识的主键。也可以自己之指定:

1
2
3
4
5
6
7
8
POST  /fwdatabase/order/1 //这里指定_id的值
{
"_id":1,
"orderId":"10000",
"name":"test",
"amount":12.09,
"desc":"测试"
}

批量增加数据

批量插入数据可以减少与es的网络请求,从而加快插入的效率,调用的是_bulk的接口,写法如下:(为了方便后面的演示,在此处插入点数据):

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

{"index":{}}
{"shortTime":"2019-01-05","orderId":"10000","name":"user1","amount":12.09,"desc":"水果收入"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10001","name":"user2","amount":110.00,"desc":"苹果收入"}
{"index":{}}
{"shortTime":"2019-01-09","orderId":"10002","name":"user3","amount":53.98,"desc":"香蕉收入"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10003","name":"user4","amount":-53.09,"desc":"支出"}
{"index":{}}
{"shortTime":"2019-01-18","orderId":"10004","name":"user5","amount":-66.00,"desc":"转账"}
{"index":{}}
{"shortTime":"2019-01-28","orderId":"10005","name":"user6","amount":-102.50,"desc":"转账"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10006","name":"user7","amount":32.59,"desc":"收入"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10007","name":"user8","amount":-10.09,"desc":"支出"}
{"index":{}}
{"shortTime":"2019-01-06","orderId":"10008","name":"user9","amount":-11.50,"desc":"种子支出"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10009","name":"user10","amount":305.49,"desc":"其他入账"}
{"index":{}}
{"shortTime":"2019-01-04","orderId":"10010","name":"user11","amount":1112.09,"desc":"分红"}
{"index":{}}
{"shortTime":"2019-01-08","orderId":"10011","name":"user12","amount":-48.45,"desc":"支出"}
{"index":{}}
{"shortTime":"2019-01-06","orderId":"10012","name":"user13","amount":-59.20,"desc":"税收支出"}

插入完成后的数据

es入门

更新数据

  1. 按id更新数据

    1
    2
    3
    4
    5
    6
    7
    POST fwdatabase/order/1 
    {
    "orderId":"10001",
    "name":"userTest",
    "amount":122.76,
    "desc":"测试更新"
    }
  2. 批量更新

    批量更新与批量插入是同一个接口,在批量插入的时候指定_id,es会自动选择插入和更新。

    1
    2
    3
    4
    5
    POST /fwdatabase/order/_bulk
    { "index": { "_id": "RH0AvWgBNfbilfsxyX-V" }}
    {"orderId":"111111", "name":"user", "amount":12.09, "desc":"测试批量更新"}
    { "index": { "_id": "Pn0AvWgBNfbilfsxyX-V" }}
    {"orderId":"222222", "name":"user", "amount":112.09, "desc":"测试批量更新"}

    es入门

  3. 局部更新

    局部更新可以增加一个document的字段,其他数据保持不变:

    1
    2
    3
    4
    5
    6
    7
    8

    POST /fwdatabase/order/PX0AvWgBNfbilfsxyX-V/_update
    {
    "doc" : {
    "tags" : "testing"
    }
    }

    id=PX0AvWgBNfbilfsxyX-V的数据增加一个tags列。

删除数据

  1. 按id删除

    1
    DELETE fwdatabase/order/1

    删除Id为1的数据。
    或者用query的查询删除也可以:

    1
    2
    3
    4
    5
    6
    7
    8
    POST  fwdatabase/order/_delete_by_query
    {
    "query": {
    "term": {
    "_id": "O30AvWgBNfbilfsxyX-V"
    }
    }
    }

    term是精确查询,后面查询会讲到

  2. 按查询结果删除

    1
    2
    3
    4
    5
    6
    POST  fwdatabase/order/_delete_by_query
    {
    "query": {
    "match_all": {}
    }
    }

查询数据


es查询分为两种,一种是参数存在与url中,形式/index/type/_search?q=_id:1
如:

1
GET /fwdatabase/order/_search?q=_id:P30AvWgBNfbilfsxyX-V

称为: Search Lite API

一种是利用POST方法提交json数据,实现查询(上面已经演示),称为:DSL查询

dsl更具有可读性,推荐使用。

  1. Search Lite API查询

    由于这种查询方式,不太场采用,只是简单介绍下,不做深入探究。

    1.1. 按ID查询

    1
    GET /index/type/_search?q=_id:1

    1.2. 查询指定的字段

    只查询orderId,amount字段。

    1
    GET  /fwdatabase/order/_search?q=_id:P30AvWgBNfbilfsxyX-V&_source=orderId,amount   

    1.3. 排序和分页

    排序:

    1
    GET  /fwdatabase/order/_search?sort=amount:desc  

    分页:

    1
    GET  /fwdatabase/order/_search?sort=amount:desc&from=0&size=2 

  1. (DSL)查询
    2.1 精确查询

    精确查询相当于mysql中的等于(=),在es中使用的term关键字

    • 查询user用户的收入和支出情况:

      1
      2
      3
      4
      5
      6
      7
      POST  /fwdatabase/order/_search
      {
      "query": {
      "term": {"name":"user"}
      }
      }

    • 查询user和user1的收支情况:

      1
      2
      3
      4
      5
      6
      7
      POST  /fwdatabase/order/_search
      {
      "query": {
      "terms": {"name":["user","user1"]}
      }
      }

    • 多个精确条件查询

      这个是需要其他高级的表达式,此处先不着急,留个问题在这。

    2.2 模糊查询

    • match

      1
      2
      3
      4
      5
      6
      7
      POST  /fwdatabase/order/_search
      {
      "query": {
      "match": {"desc":"收入"}
      }
      }

      查询出所有desc为包含收入的数据。

      如果用match下指定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed 的字符串时,将是精确搜索. 

      1
      2
      3
      4
      5
      6
      POST  /fwdatabase/order/_search
      {
      "query": {
      "match": {"name":"user"}
      }
      }

    2.3 分页

    1
    2
    3
    4
    5
    6
    POST  /fwdatabase/order/_search
    {

    "from":0,
    "size":3
    }

    2.4 排序

  • 默认正序

    1
    2
    3
    4
    5
    6
    {
    "from":0,
    "size":10,
    "sort":"amount"

    }
  • 倒序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "from": 0,
    "size": 10,
    "sort": {
    "amount": {
    "order": "desc"
    }
    }
    }
  • 多条件排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "sort": [
    {
    "amount": {
    "order": "desc"
    }
    },
    {
    "orderId": {
    "order": "desc"
    }
    }
    ]
    }
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×