Elasticsearch 性能优化实践

概要

性能调优是系统架构里所有组件必不可少的话题,Elasticsearch也不例外,虽说Elasticsearch内的默认配置已经非常优秀,但这不表示它就是完美的,必要的一些实践我们还是需要了解一下。

开启慢查询日志

慢查询日志是性能诊断的重要利器,常规操作是设置慢查询的阀值,然后运维童鞋每天对慢日志进行例行巡查,有特别慢的查询,立即报备事件处理,其余的定期将慢日志的top n取出来进行优化。

慢日志的配置在elasticsearch 6.3.1版本下是通过命令配置的,读操作和写操作可以单独设置,阀值的定义可根据实际的需求和性能指标,有人觉得5秒慢,有人觉得3秒就不可接受,我们以3秒为例:

PUT /_all/_settings
{
"index.search.slowlog.threshold.query.warn":"3s",
"index.search.slowlog.threshold.query.info":"2s",
"index.search.slowlog.threshold.query.debug":"1s",
"index.search.slowlog.threshold.query.trace":"500ms",

"index.search.slowlog.threshold.fetch.warn":"1s",
"index.search.slowlog.threshold.fetch.info":"800ms",
"index.search.slowlog.threshold.fetch.debug":"500ms",
"index.search.slowlog.threshold.fetch.trace":"200ms",

"index.indexing.slowlog.threshold.index.warn":"3s",
"index.indexing.slowlog.threshold.index.info":"2s",
"index.indexing.slowlog.threshold.index.debug":"1s",
"index.indexing.slowlog.threshold.index.trace":"500ms",
"index.indexing.slowlog.level":"info",
"index.indexing.slowlog.source":"1000"
}

这三段分别表示query查询、fetch查询和index写入三类操作的慢日志输出阀值,_all表示对所有索引生效,也可以针对具体的索引。

同时在log4j2.properties配置文件中增加如下配置:

# 查询操作慢日志输出
appender.index_search_slowlog_rolling.type = RollingFile
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_search_slowlog.log
appender.index_search_slowlog_rolling.layout.type = PatternLayout
appender.index_search_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %.10000m%n
appender.index_search_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_search_slowlog-%d{yyyy-MM-dd}.log
appender.index_search_slowlog_rolling.policies.type = Policies
appender.index_search_slowlog_rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.index_search_slowlog_rolling.policies.time.interval = 1
appender.index_search_slowlog_rolling.policies.time.modulate = true

logger.index_search_slowlog_rolling.name = index.search.slowlog
logger.index_search_slowlog_rolling.level = trace
logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
logger.index_search_slowlog_rolling.additivity = false

# 索引操作慢日志输出
appender.index_indexing_slowlog_rolling.type = RollingFile
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_indexing_slowlog.log
appender.index_indexing_slowlog_rolling.layout.type = PatternLayout
appender.index_indexing_slowlog_rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %marker%.10000m%n
appender.index_indexing_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_index_indexing_slowlog-%d{yyyy-MM-dd}.log
appender.index_indexing_slowlog_rolling.policies.type = Policies
appender.index_indexing_slowlog_rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.index_indexing_slowlog_rolling.policies.time.interval = 1
appender.index_indexing_slowlog_rolling.policies.time.modulate = true

logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = trace
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
logger.index_indexing_slowlog.additivity = false

重启elasticsearch实例后,就能在/home/esuser/esdata/log目录中看到生成的两个日志文件了。

优化实践建议

基本使用规范

  1. 搜索结果不要返回过大的结果集

过大的结果集会占用大量的IO资源和带宽,速度肯定快不了,Elasticsearch是一个搜索引擎,最理想的搜索是精准查询或次精准查询,最关心的是排在前面的少数结果,而不是所有结果,优化搜索条件,控制搜索结果数量是高性能的前提。

如果真有大批量的数据查询,建议使用scroll api。

  1. 避免超大的document

http.max_context_length的默认值是100mb,如果你一次document写入时,document的内容不能超过100mb,否则es就会拒绝写入。虽然你可以修改此配置,但不建议这么做,es底层的lucene引擎还是有一个2gb的最大限制。

过大的document会占用非常多的资源,从任何方面考虑都不建议,如果业务需求真有非常大的内容,如对书的内容搜索,建议按章节、按段落进行拆分存储。

  1. 避免稀疏的数据

document的设计会从根本上影响索引的性能,稀疏数据是一个典型的不良设计,浪费存储空间,影响读写性能。

下面有一些document结构设计的建议:

  • 避免将没有任何关联性的数据写入同一个索引

没有关联性的数据,意味着数据结构也不相同,硬生生放在同一个索引,会导致index数据非常稀疏,建议是将这些数据放在不同的索引中。

  • 对document的结构进行统一规范化

document的结构、命名尽可能统一规范处理,同样是创建时间字段,避免有的叫timestamp,有的叫create_time,尽可能统一。

  • 对某些field禁用norms和doc_values

如果一个field不需要考虑其相关度分数,那么可以禁用norms,如果不需要对一个field进行排序或者聚合,那么可以禁用doc_values字段。

服务器层级

硬件资源是性能最硬核的部分,硬件好,起点就高。

  1. 用更快的硬件资源

在预算范围内,能用SSD固态硬盘就不要选用机械硬盘;

CPU主频、核数当然是强大到预算上限;

内存单机上限64GB,加机器加到没钱为止;

尽量使用本地存储系统,不要用NFS等网络存储,毕竟硬盘便宜。

  1. 给filesystem cache更多的内存

Elasticsearch的搜索严重依赖于底层的filesystem cache,如果所有的数据都能够存放在filesystem cache中,那么搜索基本上是秒级。

由于实际情况的限制,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。

要达到最佳情况有两个办法:一个是砸钱,买更多机器,加更大内存;另一种是精简document数据,只把需要搜索的field放进es内,filesystem cache就能存下更多的document,可以提高内存的利用率。剩余的其他字段,可以放在redis/mysql/hbase/hapdoop做二级加载。

  1. 禁止swapping交换内存

将swapping禁止掉,如果es jvm内存交换到磁盘,再交换回内存,会造成大量磁盘IO,性能很差。

Elasticsearch层级

  1. index buffer

在高并发写入场景,我们可以将index buffer调大一些,indices.memory.index_buffer_size,这个可以调节大一些,这个值默认是jvm heap的10%,这个index buffer大小,是所有的shard公用的,这个值除以shard数量,算出来平均每个shard可以使用的内存大小,一般建议对于每个shard最多给512mb。

  1. 禁止_all field

_all field会将document中所有field的值都合并在一起进行索引,很占用磁盘空空间,实际上用处却不大,生产环境最好禁用_all field。

  1. 使用best_compression

_source field和其他field很占用磁盘空间,建议对其使用best_compression进行压缩。

  1. 用最小的最合适的数字类型

es支持4种数字类型:byte,short,integer,long。如果最小的类型就合适,那么就用最小的类型,节省磁盘空间。

  1. 禁用不需要的功能
展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java