说起 ElasticSearch,往往大家想到的都是 ELK 的一套,但是作为 NoSQL,ES 有极快的响应速度,强大的聚合功能,支持复杂的查询条件,应对高并发的复杂查询的业务场景其实也是非常强力的。

You Know, for Search

我们团队就一直使用 ES 作为主力数据库, 从一开始做全文检索,到现在承担全部的商品列表页查询。近几个月将查询系统的 qps 从 1k 优化到了 10k+,其中 ES 的优化占了很重要一部分,准确的来说,应该是对 ES 特性的扬长避短起到了非常大的作用。

数组 & 嵌套结构

ES 没有 join,很多人直接就会认为 ES 无法处理一对多的情况,其实还有数组嵌套结构可以应付常见的业务场景。

比如一个商品拥有多种属性,都存放在一个数组字段中,使用 must 和 must_not 就可以灵活地进行查询筛选。

比如同款不同色的几件T恤,使用嵌套结构保存,搜索时只需要其中一件满足筛选条件,便可以全部带出来,在页面上以多个小色块展示,而无需占用多个展示位。并且还可以拿满足筛选条件的商品中的某属性最大值/最小值等进行排序,如官网给出的示例:

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
{
"query": {
"nested": {
"path": "parent",
"query": {
"bool": {
"must": {"range": {"parent.age": {"gte": 21}}},
"filter": {
"nested": {
"path": "parent.child",
"query": {"match": {"parent.child.name": "matt"}}
}
}
}
}
}
},
"sort" : [
{
"parent.child.age" : {
"mode" : "min",
"order" : "asc",
"nested": {
"path": "parent",
"filter": {
"range": {"parent.age": {"gte": 21}}
},
"nested": {
"path": "parent.child",
"filter": {
"match": {"parent.child.name": "matt"}
}
}
}
}
}
]
}

聚合

商品列表页面能用到聚合的场景非常多,比如聚合出分类下(可能多达数万个商品)的各子分类,各属性的数量,并且需要支持复杂的筛选条件,比如库存,价格范围等等,并且这种查询速度远比 RDS 的 join + group by + count 快。

又比如需要查出最近10天内有新商品的日期列表,那就可以用到 date_histogram 聚合函数。

动态字段

动态字段的设计也为我们的业务提供了很大便利,由于与具体业务关联性太强,就不详细展开了。

ES能支持的动态字段数量非常的多,不过这里要留意的就是动态字段一个比较容易出问题的地方,就是瞬时写入大量的动态字段会导致集群索引的元数据大量变动,master 节点负载暴涨甚至挂掉。

缺陷

  1. 没有 join。ES 的查询速度非常的快,但是不能 join 毕竟还是有一些业务场景无法使用。当然话又说回来,在高并发量下,多表 join 能不能抗得住也是个问题。对于查询,我们一贯的原则还是:把数据离线准备成便于查询的结构,线上实时查询尽可能的简单,一步到位
  2. 由于要把数据离线准备好,这便带来了数据同步更新的问题,数据的时效性、准确性都需要保证,数组与嵌套结构的数据更新也不够方便高效,这些都会增加很多的工作量。

评论和共享

集群管理

节点分配

原集群 master*3 data*12 client*0

新集群 master*3 data*12 injest*0 coordinating*0

ingest 节点用于支持 pipeline 操作 对bulk和index文档进行预处理

coordinating 功能主要是分发请求,聚合各节点的处理结果,负载均衡,大规模集群可以设置一个给读,一个给写。但coordinating 数量也不宜过多,会拖慢选举主节点的时间,并且data节点其实也可以处理这些请求.

节点设置

search.remote.connect: false
node.ingest: false

数据迁移

数据源

由于有数据源及同步方案,所以只需数据全量导入6.3版本的集群即可.

索引管理

目前生产环境有300个索引需要同步,要检查同步脚本的创建索引,切别名等步骤.

mapping设置

  1. type 只支持1种,自 ES7.0 起将不再支持 type—官方说明
  2. 对可以使用自增 id 的索引使用自增 id
  3. 对大多数字符串字段使用 keyword 类型
  4. 对不用于数值范围查找的数值类型改为keyword类型
  5. 分词插件可能需要改动
  6. index: no 改为 index:false
  7. index: not_analyzed 删掉

提高迁移速度

  1. sudo swapoff -a
  2. 副本设置为0
  3. refresh_interval 设置为 -1 (对线上生产集群上索引批量导入时,设置-1后,重新打开时可能会导致集群压力暴增)
  4. 导入数据
  5. refresh_interval 设置为30
  6. 确认数据正确性
  7. POST /_forcemerge max_num_segments=1(对于大索引可能非常耗时)
  8. 副本设置为1

scala项目升级

  1. scala & play 升级, 尤其是play的升级会导致大量代码改动
  2. elastic4s 依赖升级,注意除了core包还需要http包 。
  3. 原本的获取client, 构建dsl,excute,解析response的大量代码要修改,尤其是构建dsl涉及大量业务,需要逐一比对修改。

监控

Prometheus + Grafana 主要是获取ES信息的api随之升级,改动通常不大

另外推荐 xpack 的 monitor,收集了 segment 的数据,收集了每个索引的请求量,响应时间等信息,信息集成进了 kibana

评论和共享

  • 第 1 页 共 1 页
作者的图片

heeexy

世上是不是就没有你不认识的字了?


JAVA


南京