目录
本文对es优化提供一些思路,具体操作还是需要结合es集群的机器性能、网络、es版本、索引数据量、doc大小、读多还是写多等,来进行实际的测试和调整来进行选择最优的方式。读性能和写性能是不可能达到最优的,这个时候就需要根据具体的业务去权衡。
本文对正在使用的索引进行优化,当然对打算新建索引参考也是很有参考意思,这意味着丛一开始使用索引就尽可能达到最优状态,另外有一些业务数据随着时间的推移已经不是一开始设想的读写频率、读写比例、已经每天数据增加减少。那就需要定期审视目前业务需求以及索引这些变化,定期进行优化。
一、查优化
可以充分利用es提供的丰富的api对es的index增删改查进行优化。
-
数据预热。
我们可以想办法隔一段时间触发热点的数据搜索,让它始终在 ES 内存(JVM HEAP)中存在。
可能经常有热点关键词被搜索,当搜索的时候相关的 segment 索引还没有在内存上,这样就需要一个从 磁盘 >>> 缓存 这样的一个过程,这个过程时间是比直接查询内存要慢得多得多的,然而过了一段时间 内存可能又被其他索引的 segment 替换了,所以下次搜索又需要这样一个漫长的过程:因为 ElasticSearch 的内存是有限的,我们可以想象它内存中的数据就像是一个被 LRU 算法管理的固定大小的空间,我们可以想办法隔一段时间触发热点的数据搜索,让它始终在 ES 内存(JVM HEAP)中存在。
-
冷热分离。
第一,纵向拆分,把不用来搜索的字段拆分到另一个索引、后者存到其他的库,建议用Hbase(es存热数据+Hbase存冷数据);第二,横向拆分,把冷的数据写入到一个索引,把热的数据写在一个索引。热数据的索引可以指定与冷索引在不同的机器。这样,热数据就会在 filesystem os chche里,不会像数据在一起那么容易冲刷掉。
-
优先使用filter
如果查询的文档与排序无关,则一定用filter,既不用参与分数计算,还能缓存数据,加快下次查询。
query和filter的区别:
比如说要查询类型为Ford,黄色的,名字包含dev的汽车,一般的查询语句应该如下:
GET /my_index/my_type/_search
{
"bool":{
"must":[
{
"term":{
"type":"ford"
}
},
{
"term":{
"color":"yellow"
}
},
{
"term":{
"name":"dev"
}
}
]
}
}
上述查询中类型和颜色同样参与了文档排名得分的计算,但是由于类型和颜色仅作为过滤条件,计算得分至于name的匹配相关。因此上述的查询是不合理且效率不高的。
GET /my_index/my_type/_search
{
"bool":{
"must":{
"term":{
"name":"dev"
}
},
"filter":[
{
"term":{
"type":"ford"
}
},
{
"term":{
"color":"yellow"
}
}
]
}
}
-
分页性能优化
这一条算不上优化,主要是了解es两种分页方式,根据需要进行选择。
from + size 分页,分的越深,速度越慢。例如每页10条数据,查询第100页,es会把每个shard上符合查询条件的1000条数据都查出来到协调节点,然后协调节点合并处理,在获取第100页的10条数据。
srocll,只能一页一页的翻,且不能后退。例如不能丛第10页翻到第15页,也不能丛第10页回到第8页。
- 能用term就不用match_phrase
term查询比match_phrase性能要快10倍,比带slop的match_phrase快20倍。
GET /my_index/my_type/_search
{
"query":{
"match_phrase":{
"title":"quick"
}
}
}
变为
GET /my_index/my_type/_search
{
"query":{
"term":{
"title":"quick"
}
}
}
二、index优化
在设计index时或者优化时,都可以考虑如何优化,提高使用es的效率。
-
优化范围查询(Range aggregation)
es的范围的聚合查询比较耗时,根据具体的业务,如果能够确定经常使用的范围,那么可以把对应的范围固话下来,这样range查询就转化为了term或者terms查询,查询速度会提升不少。
{
"range":{
"price":{
"ge":20,
"lte":40
}
}
}
例如上面这个range查询 price (20,40],如果经过分析,业务经常会查价格为(20,40]、(40,60]等,那么我们可以把对应的doc的price字段进行修改,或者再增加一个类似的字段,假设为price_r(keyword类型),那么我们再查询price在(20,40]的时候就可以如下查询语句:
{
"term":{
"price_r":"20-40"
}
}
上面添加一个price_r会有另个好处,就是把经常查询的范围的doc进行改造,其他的price可以继续使用range等方式查询。
-
多条件查询优化
query_string 或 multi_match的查询字段越多, 查询越慢。可以在mapping阶段,利用copy_to(es6.8以上版本)属性将多字段的值索引到一个新字段,对于es版本低于6.8,我们可以自己手动拼接对个字段到一个新的字段,multi_match时,用新的字段查询。
-
尽量用keyword
字段设置keyword(低版本的es叫 not_analyze),就不会被analyze,查询的时候用filter查询就不需要计算这快的分数,效率会比较高。keyword最大32766字节.
-
调整refresh间隔
在提到refresh 和下面的translog之前,先了解一下es的一个写入过程原理。入下图:
写入数据到内存buffer中,每个1秒钟buffer的数据会被写入新的segment中到文件系统的cache,(写入到文件系统的cache就可以被搜索到,这就是es近实时的原因,默认写入1秒之后才能被索引到)同时清除内存buffer;另外写入数据的操作会记录到translog,每个5秒钟,将translog中的操作数据fsync到磁盘中。另外正常情况下,每隔30分钟或者tranlog文件比较大的时候回自动flush操作,会把buffer数据存入文件系统cache,文件系统cache的segment通过fsync持久化到磁盘,同时生成commit point文件,情况内存buffer和文件系统cache。
refresh操作会生成一个新的segment,默认1秒产生一个,导致大量的segment产生,相比较操作内存,还是比较耗时,影响性能。所以当对es实时性要求不高的时候,可以通过调整refresh间隔来提高写入性能。
可以通过es提供的api进行设置对应的index,如下:
PUT /my_logs/_settings
{ "refresh_interval": "30s" }
另外,每隔5秒钟就同步执行fsync把translog持久化到磁盘,比较耗时,可以设置为异步
PUT /my_index/_settings
{
“index.translog.durability”: “async”,
“index.translog.sync_interval”: “5s”
}
三、分片数和副本数优化
1.对于数据量较小(100GB以下)的index:一般设置3~5个shard
2.对于数据量较大(100GB以上)的index:一般把单个shard的数据量控制在20GB~40GB
3.对于30G内存的节点,shard数量最好控制在600个(即每1GB内存的shard数在20以内)
4.副本数:一般副本数为1,对数据可用性有高要求的,可以设置为2
四、集群硬件优化
五、参考文献
https://www.elastic.co/guide/en/elasticsearch/guide/master/translog.html
https://elastic.blog.csdn.net/article/details/97695931
https://elastic.blog.csdn.net/article/details/84727820
https://blog.csdn.net/laoyang360/article/details/82080012
https://blog.csdn.net/qq_41864967/article/details/90454647
https://xie.infoq.cn/article/2f8732411ea2f8d10cba85b78
https://blog.csdn.net/wang7075202/article/details/111308905
https://www.jianshu.com/p/9ce30e154d2f?utm_campaign=haruki&utm_content=note&utm_medium=reader_share&utm_source=weixin