前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >非常哇塞的 ES读场景、写场景 性能优化指南!你值得拥有!

非常哇塞的 ES读场景、写场景 性能优化指南!你值得拥有!

作者头像
xjjdog
发布2022-01-05 10:43:47
6300
发布2022-01-05 10:43:47
举报
文章被收录于专栏:架构专题架构专题

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。

ES作为NoSQL数据库里非常重要的一员,使用越来越广泛。虽然它因为索引延迟的原因,数据在时效性上有一些缺陷,但其大容量、分布式的优秀设计,使得它在时效性要求并不是特别高的类实时搜索领域,能够大展身手。

根据使用场景和用途,ES可以分为写入和读取两种典型的应用方式。比如ELKB,我们就需要额外关注它的写优化;再比如从MySQL中同步数据到ES的宽表,我们就需要额外关注它的读优化

废话不多说,我们直接show一下优化方法。如果你对ES的一些概念还不是很清楚,建议收藏本文慢慢看。

1.写入优化

日志属于写多读少的业务场景,对写入速度要求很高。拿我们其中一个集群来说,单集群日志量达到百TB,每秒钟日志写入量达到10W条。

数据写入,主要有三个动作:flush、refresh和merge。通过调整它们的行为,即可在性能和数据可靠性之间进行权衡。

1.1 translog异步化

首先,ES需要写一份translog,它类似于MySQL中的redolog,为的是避免在断电的时候数据丢失。ES默认每次请求都进行一次flush,但对于日志来说,这没有必要,可以将这个过程改为异步的,刷盘间隔为60秒。参数如下:

代码语言:javascript
复制
curl-H"Content-Type: application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{
  "index.translog.durability" : "async",
  "index.translog.flush_threshold_size" : "512mb",
  "index.translog.sync_interval" : "60s"
}'

这可以说是最重要的一步优化了,对性能的影响最大,但在极端情况下会有丢失部分数据的可能。对于日志系统来说,是可以忍受的。

1.2 增加refresh间隔

除了写translog,ES还会将数据写入到一个缓冲区中。但是注意了!此时,缓冲区的内容是无法被搜索到的,它还需要写入到segment里面才可以,也就是刷新到lucence索引里面。这就是refresh动作,默认1秒。也就是你写入的数据,大概率1秒之后才会被搜索到。

这也是为什么ES不是实时搜索系统的原因,它从数据写入到数据读出,一般是有一个合并过程的,有一定的时间差。

通过index.refresh_interval可以修改这个刷新间隔。

对于日志系统来说,当然要把它调大一点啦。xjjdog这里调整到了120s,减少了这些落到segment的频率,I/O的压力自然会小,写入速度自然会快。

代码语言:javascript
复制
curl-H"Content-Type: application/json"-XPUT'http://localhost:9200/_all/_settings?preserve_existing=true'-d'{
  "index.refresh_interval" : "120s"
}'

1.3 merge

merge其实是lucene的机制,它主要是合并小的segment块,生成更大的segment,来提高检索的速度。

原因就是refresh过程会生成一大堆小segment文件,数据删除也会产生空间碎片。所以merge,通俗来讲就像是碎片整理进程。像postgresql等,也有vaccum进程在干同样的事。

显而易见,这种整理操作,既浪费I/O,又浪费CPU。

如果你的系统merge非常频繁,那么调整merge的块大小和频率,是一个比较好的方法。

2.读取优化

2.1 指定路由

如果你向ES里写数据,那么它会为你设置一个离散的隐藏ID,落到哪个分片,是不一定的。如果你根据一个查询条件查询数据,你设置了6个shards的话,它要查询6次才行。如果能够在路由的时候就知道数据在哪个分片上,查询速度自然会上升,这就要求我们在构造数据的时候,人工指定路由规则。它的实际运行规则如下:

代码语言:javascript
复制
shard = hash(routing) % number_of_primary_shards

比如,一个查询会变成这样。

代码语言:javascript
复制
GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_routing": [ "user1" ] 
    }
  }
}

当然,如果你的查询维度较多,又对数据的查询速度有非常高的有求,根据routing存放多份数据是一个比较好的选择。

2.2 rollover冷热分离

rollover根据索引大小,文档数或使用期限自动过渡到新索引。当rollover触发后,将创建新索引,写别名将更新为指向新索引,所有后续更新都将写入新索引,比如indexname-000001.这种模式。

从rollover这个名字可以看出来,它和Java的log日志有一定的相似之处,比如Log4j的RollingFileAppender。

当索引变的非常大,通常是几十GB,那它的查询效率将变的非常的低,索引重建的成本也较大。实际上,很多索引的数据在时间维度上有较为明显的规律,一些冷数据将很少被用到。这个时候,建立滚动索引将是一个比较好的办法。

滚动索引一般可以与索引模板结合使用,实现按一定条件自动创建索引,ES的官方文档有具体的_rollover建立方法。

2.3 使用BoolQuery替代TermQuery

Bool查询现在包括四种子句,must、filter、should和must_not。Bool查询是true、false对比,而TermQuery是精确的字符串比对,所以如果需求相似,BoolQuery自然会快于TermQuery。

2.4 将大查询拆成分段查询

有些业务的查询比较复杂,我们不得不拼接一张非常大的宽表放在ES中,这有两个比较明显的问题。

  1. 宽表的数据往往需要从其他数据源中回查拼接而成,数据更新时对源库或者ES本身都有较大的压力
  2. 业务的查询JSON需要书写的非常复杂,查询效率未知,一次查询锁定的内存过高,无法进行深入优化

其实,宽表不论在RDBMS中还是ES中,都会与复杂的查询语句有关,其锁定时间都较长,业务也不够灵活。

应对这种场景的策略,通常将复杂的数据查询,转移到业务代码的拼接上来。比如,将一段非常冗长的单条查询,拆分成循环遍历的100条小查询。所有的数据库都对较小的查询请求有较好的响应,其整体性能整体上将优于复杂的单条查询。

这对我们的ES索引建模能力和编码能力提出了挑战。毕竟,在ES层面,互不相关的几个索引,将作为整体为其他服务提供所谓的数据中台接口。

2.5 增加第一次索引的速度

很多业务的索引数据往往来自于MySQL等传统数据库,第一次索引往往是全量索引,后面才是增量索引。必要的时候,也会进行索引的重建,大量的数据灌入造成了ES的索引速度建立缓慢。

为了缓解这种情况,建议在创建索引的时候,把副本数量设置成1,即没有从副本。等所有数据索引完毕,再将副本数量增加到正常水平。

这样,数据能够快速索引,副本会在后台慢慢复制。

3.通用优化

当然,我们还可以针对ES做一些通用的优化。比如,使用监控接口或者trace工具,发现线程池有明显的瓶颈,则需要调整线程池的大小。

具体的优化项如下。

3.1 线程池优化

新版本对线程池的配置进行了优化,不需要配置复杂的search、bulk、index线程池。有需要配置下面几个就行了:thread_pool.get.size, thread_pool.write.size, thread_pool.listener.size, thread_pool.analyze.size。具体可观测_cat/thread_pool接口暴露的数据进行调整。

3.2 物理冷热分离

上面的rollover接口,我们可以实现索引滚动。但是如何将冷数据存放在比较慢但是便宜的节点上?如何将某些索引移动过去?

ES支持给节点打标签,具体方式是在elasticsearch.yml文件中增加一些属性。比如:

代码语言:javascript
复制
//热节点
node.attr.temperature: hot 
//冷节点
node.attr.temperature: cold 

节点有了冷热属性后,接下来就是指定数据的冷热属性,来设置和调整数据分布。ES提供了index shard filtering功能来实现索引的迁移。

首先,可以对索引也设置冷热属性。

代码语言:javascript
复制
PUT hot_data_index/_settings
{
    "index.routing.allocation.require.temperature": "hot"
}

这些索引将自动转移到冷设备上。我们可以写一些定时任务,通过_cat接口的数据,自动的完成这个转移过程。

3.2 多磁盘分散I/O

其实,可以通过配置多块磁盘的方式,来分散I/O的压力,但容易会造成数据热点集中在单块磁盘上。

ES支持在一台机器上配置多块磁盘,所以在存储规模上有更大的伸缩性。在配置文件中,配置path.data属性,即可挂载多块磁盘。

代码语言:javascript
复制
path.data : /data1, /data2, /data3

值得注意的是,如果你是在扩容,那么就需要配合reroute接口进行索引的重新分配。

3.3 减少单条记录的大小

Lucene的索引建立过程,非常耗费CPU,可以减少倒排索引的数量来减少CPU的损耗。第一个优化就是减少字段的数量;第二个优化就是减少索引字段的数量。具体的操作,是将不需要搜索的字段,index属性设置为not_analyzed或者no。至于_source和_all,在实际调试中效果不大,不再赘述。

End

ES的使用越来越广泛,从ELKB到APM,从NoSQL到搜索引擎,ES在企业中的地位也越来越重要。本文通过分析ES写入和读取场景的优化,力求从原理到实践层面,助你为ES加速。希望你在使用ES时能够更加得心应手。

通常,一个ES集群对配置的要求是较高的,尤其是APM等场景,甚至会占到PaaS平台的1/3资源甚至更多。ES提供了较多的配置选项,我们可以根据应用场景,调整ES的表现,使其更好的为我们服务。

作者简介:小姐姐味道 (xjjdog)。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小姐姐味道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.写入优化
    • 1.1 translog异步化
      • 1.2 增加refresh间隔
        • 1.3 merge
        • 2.读取优化
          • 2.1 指定路由
            • 2.2 rollover冷热分离
              • 2.3 使用BoolQuery替代TermQuery
                • 2.4 将大查询拆成分段查询
                  • 2.5 增加第一次索引的速度
                  • 3.通用优化
                    • 3.1 线程池优化
                      • 3.2 物理冷热分离
                        • 3.2 多磁盘分散I/O
                          • 3.3 减少单条记录的大小
                          • End
                          相关产品与服务
                          云数据库 SQL Server
                          腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档