1. Index(写)调优
1.1 副本数置0
如果是集群首次灌入数据,可以将副本数设置为0,写入完毕再调整回去,这样副本分片只需要拷 贝,节省了索引过程。
PUT /my_temp_index/_settings
{
"number_of_replicas": 0
}
1.2 自动生成doc ID
通过Elasticsearch写入流程可以看出,如果写入doc时如果外部指定了id,则Elasticsearch会先尝试 读取原来doc的版本号,以判断是否需要更新。这会涉及一次读取磁盘的操作,通过自动生成doc ID可 以避免这个环节。
1.3 合理设置mappings
- 将不需要建立索引的字段index属性设置为not_analyzed或no。对字段不分词,或者不索引,可以 减少很多运算操作,降低CPU占用。 尤其是binary类型,默认情况下占用CPU非常高,而这种类 型进行分词通常没有什么意义。
- 减少字段内容长度,如果原始数据的大段内容无须全部建立 索引,则可以尽量减少不必要的内容。
- 使用不同的分析器(analyzer),不同的分析器在索引过程中 运算复杂度也有较大的差异。
1.4 调整_source字段
source字段用于存储doc原始数据,对于部分不需要存储的字段,可以通过includes excludes 过滤,或者将source禁用,一般用于索引和数据分离,这样可以降低 I/O 的压力,不过实际场景中大多不会禁用_source。
1.5 对analyzed的字段禁用norms
Norms用于在搜索时计算doc的评分,如果不需要评分,则可以将其禁用:
"title": {
"type": "string",
"norms": {
"enabled": false
}
1.6 调整索引的刷新间隔
该参数缺省是1s,强制ES每秒创建一个新segment,从而保证新写入的数据近实时的可见、可被搜索 到。比如该参数被调整为30s,降低了刷新的次数,把刷新操作消耗的系统资源释放出来给index操作使 用。这种方案以牺牲可见性的方式,提高了index操作的性能。
PUT /my_index/_settings
{
"index" : {
"refresh_interval": "30s" }
}
1.7 批处理
批处理把多个index操作请求合并到一个batch中去处理,和mysql的jdbc的bacth有类似之处。如图:
比如每批1000个documents是一个性能比较好的size。每批中多少document条数合适,受很多因素 影响而不同,如单个document的大小等。ES官网建议通过在单个node、单个shard做性能基准测试来 确定这个参数的最优值。
1.8 Document的路由处理
当对一批中的documents进行index操作时,该批index操作所需的线程的个数由要写入的目的shard的个数决定。看下图:
上图中,有2批documents写入ES, 每批都需要写入4个shard,所以总共需要8个线程。如果能减少 shard的个数,那么耗费的线程个数也会减少。例如下图,两批中每批的shard个数都只有2个,总共线 程消耗个数4个,减少一半。
默认的routing就是id,也可以在发送请求的时候,手动指定一个routing value,比如: put /index/doc/id?routing=user_id
值得注意的是线程数虽然降低了,但是单批的处理耗时可能增加了。和提高刷新间隔方法类似,这有可 能会延长数据不见的时间。
2. Search(读)调优
2.1 数据分组
很多人拿ES用来存储日志,日志的索引管理方式一般基于日期的,基于天、周、月、年建索引。如下 图,基于天建索引:
当搜索单天的数据,只需要查询一个索引的shards就可以。当需要查询多天的数据时,需要查询多个索 引的shards。这种方案其实和数据库的分表、分库、分区查询方案相比,思路类似,小数据范围查询而 不是大海捞针。
开始的方案是建一个index,当数据量增大的时候,就扩容增加index的shard的个数。当shards增大 时,要搜索的shards个数也随之显著上升。基于数据分组的思路,可以基于client进行数据分组,每一 个client只需依赖自己的index的数据shards进行搜索,而不是所有的数据shards,大大提高了搜索的性 能,如下图:
2.使用Filter替代Query
在搜索时候使用Query,需要为Document的相关度打分。使用Filter,没有打分环节处理,做的事情更少,而且filter理论上更快一些。
如果搜索不需要打分,可以直接使用filter查询。如果部分搜索需要打分,建议使用’bool’查询。这种方式可以把打分的查询和不打分的查询组合在一起使用,如:
GET /_search
{
"query": {
"bool": {
"must": {
"term": {
"user": "kimchy"
}
},
"filter": {
"term": {
"tag": "tech"
}
}
}
}
}
3.ID字段定义为keyword
一般情况,如果ID字段不会被用作Range 类型搜索字段,都可以定义成keyword类型。这是因为 keyword会被优化,以便进行terms查询。Integers等数字类的mapping类型,会被优化来进行range类 型搜索。
将integers改成keyword类型之后,搜索性能大约能提升30%。
4. 别让用户的无约束的输入拖累了ES集群的性能
通过监控发现所有node的CPU 使用及其负载突然异常飙高。通过对Slow Logs分析发现, 用户查询输入的条件中夹带了很多’OR’语句以及通配符“*”开头的字符串,如下图
为了不让用户无约束的查询语句拖累ES集群的查询性能,可以限制用户用来查询的keywords。对于可以用来查询的keyworkds,也可以写成文档来帮助用户更正确的使用。