13条优化带你的ES飞起来
1、bool 查询的 filter 筛选
众所周知,ES中filter是不参与相关性评分的,所以查询子句可以被系统进行缓存,性能要高于普通的query查询。
bool查询中支持4种子句,分别是filter、must、must_not、should,其中filter和must_not属于过滤器,过滤器查询先于其它查询执行。
另外在function_score、constant_score中也可以使用filter子句进行查询缓存。
2、批量bulk
批量bulk操作也可以极大提升ES的写入性能,批量写入不易太大,否则会频繁触发gc操作,导致写入线程阻塞。
3、segment merge
- 增加 Index Refresh 间隔,每次refresh操作都会生成新的Lucene段,段越多merge次数就越多,查询性能就越受影响,建议设置为60s-120s
- index buffer 数据写入缓存区,当缓冲区写满时会生成一个segment;某个节点上所有的shard共享indices.memory.index_buffer_size指定的缓存区大小,默认是10%堆内存大小,可适当增加到20%。
- 定期将只读或写少的索引进行segment合并,降低segment数量对搜索性能提升帮组很大,合并过程中将only_expunge_deletes设置为true意思是将标记为删除的数据清除掉。
curl -XPOST "http://host/visitor/_forcemerge?only_expunge_deletes=true&max_num_segments=1&flush=true"
4、SearchType
SearchType默认是QUERY_THEN_FETCH,可在查询时指定为query_and_fetch,这样协调节点只需要请求一次数据节点就可以完成请求处理了。
QUERY_THEN_FETCH:ES 默认的搜索方式。第一步,先向所有的分片发请求,各分片只返回文档的相似度得分和文档的 ID,然后协调节点按照各分片返回的分数进行重新排序和排名,再取出需要返回给客户端的 Size 个文档 ID。
第 2 步,在相关的分片中取出文档的详细信息并返回给用户。
QUERY_AND_FETCH:协调节点向所有分片发送查询请求,各分片将文档的相似度得分和文档的详细信息一起返回。
然后,协调节点进行重新排序,再取出需要返回给客户端的数据,将其返回给客户端。由于只需要在分片中查询一次,所以性能是最好的。
DFS_QUERY_THEN_FETCH:与 QUERY_THEN_FETCH 类似,但它包含一个额外的阶段:在初始查询中执行全局的词频计算,以使得更精确地打分,从而让查询结果更相关。
QUERY_THEN_FETCH 使用的是分片内部的词频信息,而 DFS_QUERY_THEN_FETCH 使用访问公共的词频信息,所以相比 QUERY_THEN_FETCH 性能更低。
DFS_QUERY_AND_FETCH:与 QUERY_AND_FETCH 类似,不过使用的是全局的词频。
/_search?preference=abcd&search_type=query_and_fetch
#preference 可以确保字符串标识相同时,每次请求会发送到相同的分片执行
5、预排序 IndexSorting
ES 6.x之后新增预排序功能,即索引在创建之前可以指定数据写入后的排序方式,当query时指定的排序方式和预排序逻辑一致时将能够很快获得排序结果,需要注意的是 查询时不能开启total值
{
"settings": {
"index": {
"sort.field": [ "username", "date" ],
"sort.order": [ "asc", "desc" ]
}
},
"mappings": {
"properties": {
"username": {
"type": "keyword",
"doc_values": true
},
"date": {
"type": "date"
}
}
}
}
{
"size": 10,
"sort": [
{ "username": "asc" },
{ "date": "desc" }
],
"track_total_hits": false
}
6、磁盘
有条件的尽量使用SSD盘,或者挂载多块盘提升io性能,以下是不同磁盘在并发场景下的性能表现:
7、设置独立的聚合节点
在有聚合分析场景的业务需要单独指定高配服务器用于聚合查询,与现有点查服务进行隔离; 一般要求CPU在16核以上
# 查询聚合节点配置(conf/elasticsearch.yml):
node.master:false
node.data:false
node.ingest:false
8、查询中断
如果不需要精确统计查询命中记录条数,可以配 teminate_after 指定每个 shard 最多匹配 N 条记录后返回,以及设置查询超时时间 timeout 使得请求提前结束
{
"timeout": "10s",
"terminate_after": 1000,
"query": {
}
}
9、调整聚合方式
默认深度优先聚合改为广度优先聚合。
"collect_mode" : "breadth_first"
depth_first 直接进行子聚合的计算
breadth_first 先计算出当前聚合的结果,针对这个结果在对子聚合进行计算。
10、索引冷热分离
一般应用在操作时序日志的场景, 通过定义模板动态生成索引,如将超出3个月的索引通过定时任务归档到cold组 ,具体操作如下:
1、将data node划分为冷热数据节点
# cat elasticsearch.yml // 配置文件设置tag区分
node.attr.tag: cold/hot
# bin/elasticsearch -d -Enode.attr.box_type=hot //启动设置
2、根据模板按月生成索引,格式如 visitor_2019-01
#构建索引模板,滚动新增的索引将按照模板进行创建
curl -X PUT "localhost:9200/_template/visitor" -H 'Content-Type: application/json' -d'
{
"index_patterns": ["visitor_*"],
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.routing.allocation.include.tag" : "hot"
},
"mappings": {}
}
'
3、归档历史数据到cold节点
curl -X PUT "${es}/${i}/_settings" -H 'Content-Type: application/json' -d'{
"number_of_replicas": 0,
"index.routing.allocation.include.tag": "cold"
}'
11、异步搜索
ES最近发布的几个版本无论是检索过滤,还是聚合分析,在性能上都有明显的提升和改进,亿级数据分组聚合秒出,对于再大的数据还可以使用_async_search查询,这是7.7版本引入的异步交互查询。
如果执行语句超过1s会返回一个执行Id,客户端根据执行Id可获取剩下的执行结果:
POST uplog_view_202008*/_async_search?wait_for_completion_timeout=1000ms
{
"size":0,
"aggs": {
"sale_date": {
"terms": {
"field": "u_i" ,
"size": 300
}
}
}
}
GET _async_search/FjRmemhPOFl1U1BDRzN6TjVnZkdsMWcgdmZmbUY1c2hRck8xWXNUYmF4UEppUToxMDMxNTk5NDA=
12、translog flush
ES数据写入线程默认会执行flush操作,该操作在高并发情况下会阻塞写入请求;如果对数据写入时效性要求不是特别高的话可设置为异步模式
# 调整为异步模式可极大提升写入性能
index.translog.durability: async
# sync_interval 和 flush_threshold_size 用来指定异步写入周期
index.translog.sync_interval: 120s
index.translog.flush_threshold_size: 1024mb
13、keyword类型
keyword是ES中性能最好的字段,所以你懂的
其它配置说明
cluster.name:elasticsearch:配置 ES 的集群名称,默认值是 ES,建议改成与所存数据相关的名称,ES 会自动发现在同一网段下的集群名称相同的节点。
node.nam:"node1":集群中的节点名,在同一个集群中不能重复。节点的名称一旦设置,就不能再改变了。当然,也可以设置成服务器的主机名称,例如 node.name:${HOSTNAME}。
noed.master:true:指定该节点是否有资格被选举成为 Master 节点,默认是 True,如果被设置为 True,则只是有资格成为 Master 节点,具体能否成为 Master 节点,需要通过选举产生。
node.data:true:指定该节点是否存储索引数据,默认为 True。数据的增、删、改、查都是在 Data 节点完成的。
index.number_of_shards:5:设置都索引分片个数,默认是 5 片。也可以在创建索引时设置该值,具体设置为多大都值要根据数据量的大小来定。如果数据量不大,则设置成 1 时效率最高。
index.number_of_replicas:1:设置默认的索引副本个数,默认为 1 个。副本数越多,集群的可用性越好,但是写索引时需要同步的数据越多。
path.conf:/path/to/conf:设置配置文件的存储路径,默认是 ES 目录下的 Conf 文件夹。建议使用默认值。
path.data:/path/to/data1,/path/to/data2:设置索引数据多存储路径,默认是 ES 根目录下的 Data 文件夹。切记不要使用默认值,因为若 ES 进行了升级,则有可能数据全部丢失。
可以用半角逗号隔开设置的多个存储路径,在多硬盘的服务器上设置多个存储路径是很有必要的。
path.logs:/path/to/logs:设置日志文件的存储路径,默认是 ES 根目录下的 Logs,建议修改到其他地方。
path.plugins:/path/to/plugins:设置第三方插件的存放路径,默认是 ES 根目录下的 Plugins 文件夹。
bootstrap.mlockall:true:设置为 True 时可锁住内存。因为当 JVM 开始 Swap 时,ES 的效率会降低,所以要保证它不 Swap。
network.bind_host:192.168.0.1:设置本节点绑定的 IP 地址,IP 地址类型是 IPv4 或 IPv6,默认为 0.0.0.0。
network.publish_host:192.168.0.1:设置其他节点和该节点交互的 IP 地址,如果不设置,则会进行自我判断。
network.host:192.168.0.1:用于同时设置 bind_host 和 publish_host 这两个参数。
http.port:9200:设置对外服务的 HTTP 端口,默认为 9200。ES 的节点需要配置两个端口号,一个对外提供服务的端口号,一个是集群内部使用的端口号。
http.port 设置的是对外提供服务的端口号。注意,如果在一个服务器上配置多个节点,则切记对端口号进行区分。
transport.tcp.port:9300:设置集群内部的节点间交互的 TCP 端口,默认是 9300。注意,如果在一个服务器配置多个节点,则切记对端口号进行区分。
transport.tcp.compress:true:设置在节点间传输数据时是否压缩,默认为 False,不压缩。
index.merge.scheduler.max_thread_count:1 #索引merge最大线程数
index.translog.durability:async #这个可以异步写硬盘,增大写的速度
index.translog.sync_interval:120s #translog间隔时间
thread_pool.bulk.size:20 #写入线程个数 由于我们查询线程都是在代码里设定好的,我这里只调节了写入的线程数
thread_pool.bulk.queue_size:1000 #写入线程队列大小
index.refresh_interval:300s #index刷新间隔
discovery.zen.minimum_master_nodes:1:设置在选举 Master 节点时需要参与的最少的候选主节点数,默认为 1。如果使用默认值,则当网络不稳定时有可能会出现脑裂。
合理的数值为(master_eligible_nodes/2)+1,其中 master_eligible_nodes 表示集群中的候选主节点数。
discovery.zen.ping.timeout:3s:设置在集群中自动发现其他节点时 Ping 连接的超时时间,默认为 3 秒。
在较差的网络环境下需要设置得大一点,防止因误判该节点的存活状态而导致分片的转移。
几种ES性能分析方式
- hot_threads 显示ES中占用CPU的线程
GET _nodes/hot_threads
::: {store-es3}{xNc5Noz8TauZa-ejkUVftQ}{EgDVIheuSvCB4yI6Gqumng}{store-es3}{10.0.20.109:9300}{dilmrt}{ml.machine_memory=33678888960, ml.max_open_jobs=20, xpack.installed=true, transform.node=true}
Hot threads at 2020-08-25T13:18:16.454Z, interval=500ms, busiestThreads=3, ignoreIdleThreads=true:
6.0% (30.2ms out of 500ms) cpu usage by thread 'elasticsearch[store-es3][search][T#7]'
2/10 snapshots sharing following 30 elements
- profile 慢查询在线分析
{
"profile": "true",
"aggs": {
"aa": {
"terms": {
"field": "create_user",
"size": 10
}
}
}
}
- 慢查询日志
ES也可以通过日志的形式分析慢查询
index.search.slowlog.threshold.query.warn
index.search.slowlog.threshold.query.info
index.search.slowlog.threshold.query.debug
index.search.slowlog.threshold.query.trace
index.search.slowlog.threshold.fetch.warn
index.search.slowlog.threshold.fetch.info
index.search.slowlog.threshold.fetch.debug
index.search.slowlog.threshold.fetch.trace
index.search.slowlog.level
写在最后
坚信方法总比问题多。一个系统性问题往往是多种因素造成的,在处理服务的性能问题上,先将问题分解,在单台上进行压测,观察哪种系统资源达到极限,例如,CPU或磁盘利用率、I/Oblock、线程切换、堆栈状态等。然后分析并调整参数,优化单台上的能力至极致,局部问题得到了解决 全局问题自然也就解决了。