ES写入优化
在Elasticsearch的默认设置下,是综合考虑数据可靠性、搜索实时性、写入速度等因素的。当离开默认设置、追求极致的写入速度时,很多是以牺牲可靠性和搜索实时性为代价的。有时候,业务上对数据可靠性和搜索实时性要求并不高,反而对写入速度要求很高,此时可以调整一些策略,最大化写入速度。
接下来的优化基于正常运行的前提下,如果是集群首次批量导入数据,则可以将副本数设置为0,导入完毕再将副本数调整回去,这样副本分片只需要复制,节省了构建索引过程。
综合来说,提升写入速度从以下几个方面入手:
- 加大translog flush间隔,目的是降低iops、writeblock;
- 加大index refresh间隔,除了降低I/O,更重要的是降低了segment merge频率;
- 调整bulk请求;
- 优化磁盘间的任务均匀情况,将shard尽量均匀分布到物理主机的各个磁盘;
- 优化节点间的任务分布,将任务尽量均匀地发到各节点;
- 优化Lucene层建立索引的过程,目的是降低CPU占用率及I/O,例如,禁用_all字段。
副本设置
单个文档写入对应 Index 请求,批量写入对应 Bulk 请求,Index 和 Bulk 是相同的处理逻辑,请求统一封装到 BulkRequest中。
流程拆解如下:
第一:客户端向 Node 1 发送写数据请求。
注意,此时Node1 便充当协调节点(cooridiniate)的角色。
第二:节点Node1 使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在 Node 3 上。
使用文档的 _id 确定文档所属分片的方法是:路由算法。
路由算法计算公式:
shard = hash(routing) % number_of_primary_shards
- routing:文档 _id。
- number_of_primary_shards: 主分片个数。
- shard: 文档 _id 所属分片号。
第三:Node 3 在主分片上面执行写入操作。如果写入成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告写入成功,协调节点向客户端报告写入成功。
如上流程拆解后的注意点:
- 写操作必须在主分片执行成功后,才能复制到相关的副本分片。
- 主分片写入失败,则整个请求被认为是写失败。
- 如果有部分副本写失败(前提:主分片写入成功),则整个请求被认为是写成功。
如果设置了副本,数据会先写入主分片,主分片再同步到副本分片,同步操作会加重磁盘 IO,间接影响写入性能。
基于以上分析,既然主分片的写入起到写入成败的决定性作用。那么写入前将:副本分片写入前置为0,待写入完成后复原副本,是不是就能提升写入性能了呢?
是的!
"number_of_replicas": 0
索引过程调整和优化
自动生成doc ID
通过ES写入流程可以看出,写入doc时如果外部指定了id,则ES会先尝试读取原来doc的版本号,以判断是否需要更新。这会涉及一次读取磁盘的操作,通过自动生成doc ID可以避免这个环节。
调整字段Mappings
- 减少字段数量,对于不需要建立索引的字段,不写入ES;
- 将不需要建立索引的字段index属性设置为not_analyzed或no。对字段不分词,或者不索引,可以减少很多运算操作,降低CPU占用。尤其是binary类型,默认情况下占用CPU非常高,而这种类型进行分词通常没有什么意义;
- 减少字段内容长度,如果原始数据的大段内容无须全部建立索引,则可以尽量减少不必要的内容;
- 使用不同的分词器(analyzer),不同的分析器在索引过程中运算复杂度也有较大的差异。
调整_source字段(非必要)
_source字段用于存储doc原始数据,对于部分不需要存储的字段,可以通过includes excludes过滤,或者将_source禁用,一般用于索引和数据分离。
这样可以降低I/O的压力,不过实际场景中大多不会禁用_source,而即使过滤某些字段,对于写入速度的提升作用也不大,满负荷写入情况下,基本是CPU先跑满了,瓶颈在于CPU。
禁用_all字段
从ES 6.0开始,_all字段默认认为不启用,而在此前的版本中,_all字段默认是开启的。_all字段中包含 所有字段分词后的关键词,作用是可以在搜索的时候不指定特定字段,从所有字段中检索。
对Analyzed的字段禁用Norms
Norms用于在搜索时计算doc的评分,如果不需要评分,则可以将其禁用:
"title":{
"type": "string",
"norms": {
"enabled": false
}
}
index_options设置(非必要)
index_options用于控制在建立倒排索引过程中,哪些内容会被添加到倒排索引,例如,doc数量、词频、positions、offsets等信息,优化这些设置可以一定程度降低索引过程中的运算任务,节省CPU占用率。
不过在实际场景中,通常很难确定业务将来会不会用到这些信息,除非一开始方案就明确是这样设计的。
调整刷新频率
refresh
ES的底层存储是Lucene,包含一系列的反向索引。这样的索引就成为段
(segment)。但记录不会直接写入段,而是先写入一个缓冲区。
当缓冲区满了,或者在缓冲区呆的够久,达到了刷新时间(划重点),会一次性将缓冲区的内容写进段中。
这也是为什么refresh_interval
属性的配置会严重的影响性能。如果你不要很高的实时性,不妨将其配置的大一点。
缓冲区默认使用堆空间的10%,最小值为48mb(针对于分片的)。如果你的索引多且写入重,这部分内存的占用是可观的,可以适当加大。
除了写translog,ES还会将数据写入到一个缓冲区中。但是注意了!此时,缓冲区的内容是无法被搜索到的,它还需要写入到segment里面才可以。
这就是refresh动作,默认1秒。也就是你写入的数据,大概率1秒之后才会被搜索到。
所以ES并不是一个实时性的搜索系统,它是一个类实时系统(near-realtime)。
通过index.refresh_interval
可以修改这个刷新间隔。
对于日志系统来说,可以适当调大。这里调整到了120s,减少了这些落到segment的频率,速度自然会快。
curl -H "Content-Type: application/json" -XPUT 'http://localhost:9200/_all/_settings?preserve_existing=true' -d '{
"index.refresh_interval" : "120s"
}'
在进行全量索引时,可以将 refresh 次数临时关闭,即 index.refresh_interval: -1
,数据导入成功后再打开到正常模式,比如30s。
在加载大量数据时候可以暂时不用 refresh 和 repliccas,index.refresh_interval 设置为-1,index.number_of_replicas 设置为0。
merge
merge其实是lucene的机制,它主要是合并小的segment块,生成更大的segment,来提高检索的速度。
原因就是refresh过程会生成一大堆小segment文件,数据删除也会产生空间碎片。所以merge,通俗来讲就像是碎片整理进程。像postgresql等,也有vaccum进程在干同样的事。
显而易见,这种整理操作,既让费I/O,又浪费CPU。
要命的是,merge有三种策略。
- tiered 默认选项,它能合并大小相似的索引段,并考虑每层允许的索引段的最大个数。
- log_byte_size 以字节数的对数为计算单位,选择多个索引来合并创建新索引。
- log_doc 以索引段的文档数量为计算单位,选择多个索引来合并创建新索引。
每一种策略都有非常详细的针对性配置,在此不啰嗦。
由于日志系统并没有随机性的删除操作,所以我们保持默认就可以。
合理调整堆内存中索引缓冲区(index_buffer)大小
堆内中 index_buffer 用于存储新索引的文档,填满后,冲区中的文档将最终写入磁盘上的某个段。
index_buffer_size 默认值如下所示,为堆内存的 10%。
indices.memory.index_buffer_size: 10%
例如,如果给 JVM 31GB的内存,它将为索引缓冲区提供 3.1 GB的内存,一般情况下足以容纳大量数据的写入操作。
但,如果着实数据量非常大,建议调大该默认值。比如:调整为堆内存的 20%。
调整建议:必须在集群中的每个数据节点上进行配置。
缓存区越大,意味着能缓存数据量越大,相同时间段内,写盘频次低、磁盘 IO 小,间接提升写入性能。
bulk 批量写入而非单个文档写入
当有大量数据提交的时候,建议采用批量提交(Bulk 操作);此外使用 bulk 请求时,每个请求不超过几十M,因为太大会导致内存使用过大。
比如在做 ELK 过程中,Logstash indexer 提交数据到 Elasticsearch 中,batch size 就可以作为一个优化功能点。但是优化 size 大小需要根据文档大小和服务器性能而定。
像 Logstash 中提交文档大小超过 20MB,Logstash 会将一个批量请求切分为多个批量请求。
如果在提交过程中,遇到 EsRejectedExecutionException 异常的话,则说明集群的索引性能已经达到极限了。这种情况,要么提高服务器集群的资源,要么根据业务规则,减少数据收集速度,比如只收集 Warn、Error 级别以上的日志。
合理设置线程池和队列大小
在 logstash 同步数据到 Elasticsearch,基于spark、kafka、Flink 批量写入 Elasticsearch时,经常会出现:Bulk Rejections 的报错。
当批量请求到达集群中的某个节点时,整个请求将被放入批量队列中,并由批量线程池中的线程进行处理。批量线程池处理来自队列的请求,并将文档转发到副本分片,作为此处理的一部分。子请求完成后,将响应发送到协调节点。
Elasticsearch 具有有限大小的请求队列的原因是:为了防止集群过载,从而增加了稳定性和可靠性。
如果没有任何限制,客户端可以很容易地通过恶意攻击行为将整个集群搞宕机。
关于线程池和队列,参考:Elasticsearch 线程池和队列问题,请先看这一篇。
核心建议就是:结合 CPU 核数和 esrally 的测试结果谨慎的调整 write 线程池和队列大小。
为什么要谨慎设置?
针对批量写入拒绝(reject)的场景,官方建议:
增加队列的大小不太可能改善集群的索引性能或吞吐量。相反,这只会使集群在内存中排队更多数据,这很可能导致批量请求需要更长的时间才能完成。
队列中的批量请求越多,将消耗更多的宝贵堆空间。如果堆上的压力太大,则可能导致许多其他性能问题,甚至导致集群不稳定。
设置合理的Mapping
实战业务场景中不推荐使用默认 dynamic Mapping,一定要手动设置 Mapping。
- 举例1:默认字符串类型是:text 和 keyword 的组合类型,就不见得适用所有业务场景。要结合自己业务场景设置,正文 cont 文本内容一般不需要设置 keyword 类型(因为:不需要排序和聚合操作)。
- 举例2:互联网采集数据存储场景,正文需要全文检索,但包含 html 样式的文本一般留给前端展示用,不需要索引。这时候Mapping 设置阶段要果断将 index 设置为 false。
写入过程中做好监控
如下是 kibana 监控截图,其中:index Rate 就是写入速率。
- index rate: 每秒写入的文档数。
- search rate:每秒的查询次数(分片级别,非请求级别),也就意味着一次查询请求命中的分片数越多,值越大。
translog flush间隔调整
如果没有用fsync把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正常退出之后依然存在。为了保证Elasticsearch 的可靠性,需要确保数据变化被持久化到磁盘。在动态更新索引,我们说一次完整的提交会将段刷到磁盘,并写入一个包含所有段列表的提交点。Elasticsearch 在启动或重新打开一个索引的过程中使用这个提交点来判断哪些段隶属于当前分片。
即使通过每秒刷新(refresh)实现了近实时搜索,我们仍然需要经常进行完整提交来确保能从失败中恢复。但在两次提交之间发生变化的文档怎么办? 我们也不希望丢失掉这些数据。
Elasticsearch 增加了一个translog ,或者叫事务日志,在每一次对Elasticsearch进行操作时均进行了日志记录。
整个流程如下:
- 一个文档被索引之后,就会被添加到内存缓冲区,并且追加到了 translog
- 刷新(refresh)使分片每秒被刷新(refresh)一次:
2.1 这些在内存缓冲区的文档被写入到一个新的段中,且没有进行fsync操作。
2.2 这个段被打开,使其可被搜索。
2.3 内存缓冲区被清空。
- 这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志
- 每隔一段时间—例如translog变得越来越大,索引被刷新(flush);一个新的translog被创建,并且一个全量提交被执行。
4.1 所有在内存缓冲区的文档都被写入一个新的段。
4.2 缓冲区被清空。
4.3 一个提交点被写入硬盘。
4.4 文件系统缓存通过fsync被刷新(flush) 。
4.5 老的translog被删除。
translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当Elasticsearch启动的时候,它会从磁盘中使用最后一个提交点去恢复己知的段,并且会重放translog 中所有在最后一次提交后发生的变更操作。
translog 也被用来提供实时CRUD。当你试着通过ID查询、更新、删除一个文档,它会在尝试从相应的段中检索之前,首先检查 translog任何最近的变更。这意味着它总是能够实时地获取到文档的最新版本。
执行一个提交并且截断translog 的行为在 Elasticsearch被称作一次flush。分片每30分钟被自动刷新(flush),或者在 translog 太大的时候也会刷新。
你很少需要自己手动执行flush操作,通常情况下,自动刷新就足够了。这就是说,在重启节点或关闭索引之前执行 flush有益于你的索引。当Elasticsearch尝试恢复或重新打开一个索引,它需要重放translog中所有的操作,所以如果日志越短,恢复越快。
translog 的目的是保证操作不会丢失,在文件被fsync到磁盘前,被写入的文件在重启之后就会丢失。默认translog是每5秒被fsync刷新到硬盘,或者在每次写请求完成之后执行(e.g. index, delete, update, bulk)。这个过程在主分片和复制分片都会发生。最终,基本上,这意味着在整个请求被fsync到主分片和复制分片的translog之前,你的客户端不会得到一个200 OK响应。
在每次请求后都执行一个fsync会带来一些性能损失,尽管实践表明这种损失相对较小(特别是 bulk 导入,它在一次请求中平摊了大量文档的开销)。
但是对于一些大容量的偶尔丢失几秒数据问题也并不严重的集群,使用异步的 fsync还是比较有益的。比如,写入的数据被缓存到内存中,再每5秒执行一次 fsync 。如果你决定使用异步translog 的话,你需要保证在发生 crash 时,丢失掉 sync_interval时间段的数据也无所谓。请在决定前知晓这个特性。如果你不确定这个行为的后果,最好是使用默认的参数“index.translog.durability”: “request”
来避免数据丢失。
从ES 2.x开始,在默认设置下,translog的持久化策略为:每个请求都”flush“。对应配置项:index.translog.durability:request
index.translog.durability:async
#设置为async表示translog的刷盘策略按sync_interval配置指定的事件周期进行。
index.translog.sync_interval:120s
#加大translog刷盘间隔时间。默认为5s,不可低于100ms
index.translog.flush_threshold_size:1024mb
#超过这个大小会导致refresh操作,产生新的Lucene分段。默认值为512MB。
从上面的介绍可以看出来,translog
写入了一份全量的数据,它有点像MysSQL中的binlog,或者redis的aof,用来保证异常情况下的数据安全。
除了修改配置文件,还可以通过API的方式调整:
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" : "1024mb",
"index.translog.sync_interval" : "120s"
}'
查询方面优化
Elasticsearch 作为业务搜索的近实时查询时,查询效率的优化显得尤为重要。
路由优化
不带routing查询
在查询的时候因为不知道要查询的数据具体在哪个分片上,所以整个过程分为2个步骤:
- 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上。
- 聚合:协调节点搜集到每个分片上查询结果,再将查询的结果进行排序,之后给用户返回结果。
带routing查询
查询的时候,可以直接根据 routing 信息定位到某个分配查询,不需要查询所有的分配,经过协调节点排序。
向上面自定义的用户查询,如果 routing 设置为 userid 的话,就可以直接查询出数据来,效率提升很多。
Filter VS Query
尽可能使用过滤器上下文(Filter)替代查询上下文(Query)
- Query:此文档与此查询子句的匹配程度如何?
- Filter:此文档和查询子句匹配吗?
Elasticsearch 针对 Filter 查询只需要回答「是」或者「否」,不需要像 Query 查询一样计算相关性分数,同时Filter结果可以缓存。
深度翻页
在使用 Elasticsearch 过程中,应尽量避免大翻页的出现。
正常翻页查询都是从 from 开始 size 条数据,这样就需要在每个分片中查询打分排名在前面的 from+size 条数据。协同节点收集每个分配的前 from+size 条数据。协同节点一共会受到 N*(from+size) 条数据,然后进行排序,再将其中 from 到 from+size 条数据返回出去。如果 from 或者 size 很大的话,导致参加排序的数量会同步扩大很多,最终会导致 CPU 资源消耗增大。
可以通过使用 Elasticsearch scroll 和 scroll-scan 高效滚动的方式来解决这样的问题。
也可以结合实际业务特点,文档 id 大小如果和文档创建时间是一致有序的,可以以文档 id 作为分页的偏移量,并将其作为分页查询的一个条件。
脚本(script)合理使用
我们知道脚本使用主要有 3 种形式,内联动态编译方式、_script 索引库中存储和文件脚本存储的形式;一般脚本的使用场景是粗排,尽量用第二种方式先将脚本存储在 _script 索引库中,起到提前编译,然后通过引用脚本 id,并结合 params 参数使用,即可以达到模型(逻辑)和数据进行了分离,同时又便于脚本模块的扩展与维护。
Cache的设置及使用
QueryCache
: ES查询的时候,使用filter查询会使用query cache, 如果业务场景中的过滤查询比较多,建议将query cache设置大一些,以提高查询速度。
indices.queries.cache.size: 10%(默认),可设置成百分比,也可设置成具体值,如256mb。
当然也可以禁用查询缓存(默认是开启), 通过index.queries.cache.enabled:false设置。
-
FieldDataCache
: 在聚类或排序时,field data cache会使用频繁,因此,设置字段数据缓存的大小,在聚类或排序场景较多的情形下很有必要,可通过indices.fielddata.cache.size:30% 或具体值10GB来设置。但是如果场景或数据变更比较频繁,设置cache并不是好的做法,因为缓存加载的开销也是特别大的。 -
ShardRequestCache
: 查询请求发起后,每个分片会将结果返回给协调节点(Coordinating Node), 由协调节点将结果整合。如果有需求,可以设置开启; 通过设置
index.requests.cache.enable: true
来开启。不过,shard request cache只缓存hits.total, aggregations, suggestions类型的数据,并不会缓存hits的内容。也可以通过设置indices.requests.cache.size: 1%(默认)来控制缓存空间大小。
更多查询优化经验
- query_string 或 multi_match的查询字段越多,查询越慢。可以在mapping阶段,利用copy_to属性将多字段的值索引到一个新字段,multi_match时,用新的字段查询。
- 日期字段的查询, 尤其是用now 的查询实际上是不存在缓存的,因此, 可以从业务的角度来考虑是否一定要用now, 毕竟利用query cache 是能够大大提高查询效率的。
- 查询结果集的大小不能随意设置成大得离谱的值, 如query.setSize不能设置成 Integer.MAX_VALUE, 因为ES内部需要建立一个数据结构来放指定大小的结果集数据。
- 避免层级过深的聚合查询, 层级过深的aggregation 会导致内存、CPU消耗,建议在服务层通过程序来组装业务,也可以通过pipeline的方式来优化。
- 复用预索引数据方式来提高AGG性能:
如通过 terms aggregations 替代 range aggregations, 如要根据年龄来分组,分组目标是: 少年(14岁以下) 青年(14-28) 中年(29-50) 老年(51以上), 可以在索引的时候设置一个age_group字段,预先将数据进行分类。从而不用按age来做range aggregations, 通过age_group字段就可以了。
通过开启慢查询配置定位慢查询
不论是数据库还是搜索引擎,对于问题的排查,开启慢查询日志是十分必要的,ES 开启慢查询的方式有多种,但是最常用的是调用模板 API 进行全局设置:
PUT /_template/{TEMPLATE_NAME}
{
"template":"{INDEX_PATTERN}",
"settings" : {
...............
},
"version" : 1
}
PUT {INDEX_PAATERN}/_settings
{
"index.indexing.slowlog.level": "INFO",
"index.indexing.slowlog.threshold.index.warn": "10s",
"index.indexing.slowlog.threshold.index.info": "5s",
"index.indexing.slowlog.threshold.index.debug": "2s",
"index.indexing.slowlog.threshold.index.trace": "500ms",
"index.indexing.slowlog.source": "1000",
"index.search.slowlog.level": "INFO",
"index.search.slowlog.threshold.query.warn": "10s",
"index.search.slowlog.threshold.query.info": "5s",
"index.search.slowlog.threshold.query.debug": "2s",
"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"
}
PUT /_all/_settings
{
"index.search.slowlog.threshold.query.warn":"5s",
"index.search.slowlog.threshold.query.info":"2s",
"index.search.slowlog.threshold.query.debug":"1s",
"index.search.slowlog.threshold.query.trace":"400ms",
"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":"5s",
"index.indexing.slowlog.threshold.index.info":"2s",
"index.indexing.slowlog.threshold.index.debug":"1s",
"index.indexing.slowlog.threshold.index.trace":"400ms"
}
这样,在日志目录下的慢查询日志就会有输出记录必要的信息了。
{CLUSTER_NAME}_index_indexing_slowlog.log
{CLUSTER_NAME}_index_search_slowlog.log
数据结构优化
基于 Elasticsearch 的使用场景,文档数据结构尽量和使用场景进行结合,去掉没用及不合理的数据。
尽量减少不需要的字段
如果 Elasticsearch 用于业务搜索服务,一些不需要用于搜索的字段最好不存到 ES 中,这样即节省空间,同时在相同的数据量下,也能提高搜索性能。
避免使用动态值作字段,动态递增的 mapping,会导致集群崩溃;同样,也需要控制字段的数量,业务中不使用的字段,就不要索引。控制索引的字段数量、mapping 深度、索引字段的类型,对于 ES 的性能优化是重中之重。
以下是 ES 关于字段数、mapping 深度的一些默认设置:
index.mapping.nested_objects.limit: 10000
index.mapping.total_fields.limit: 1000
index.mapping.depth.limit: 20
Nested Object vs Parent/Child
尽量避免使用 nested 或 parent/child 的字段,能不用就不用;nested query 慢,parent/child query 更慢,比 nested query 慢上百倍;因此能在 mapping 设计阶段搞定的(大宽表设计或采用比较 smart 的数据结构),就不要用父子关系的 mapping。
如果一定要使用 nested fields,保证 nested fields 字段不能过多,目前 ES 默认限制是 50。因为针对 1 个 document,每一个 nested field,都会生成一个独立的 document,这将使 doc 数量剧增,影响查询效率,尤其是 JOIN 的效率。
index.mapping.nested_fields.limit: 50
对比 | Nested Object | Parent/Child |
---|---|---|
优点 | 文档存储在一起,因此读取性高 | 父子文档可以独立更新,互不影响 |
缺点 | 更新父文档或子文档时需要更新整个文档 | 为了维护 join 关系,需要占用部分内存,读取性能较差 |
场景 | 子文档偶尔更新,查询频繁 | 子文档更新频繁 |
选择静态映射,非必需时,禁止动态映射
尽量避免使用动态映射,这样有可能会导致集群崩溃,此外,动态映射有可能会带来不可控制的数据类型,进而有可能导致在查询端出现相关异常,影响业务。
此外,Elasticsearch 作为搜索引擎时,主要承载 query 的匹配和排序的功能,那数据的存储类型基于这两种功能的用途分为两类,一是需要匹配的字段,用来建立倒排索引对 query 匹配用,另一类字段是用做粗排用到的特征字段,如 ctr、点击数、评论数等等。
document 模型设计
对于 MySQL,我们经常有一些复杂的关联查询。在 es 里该怎么玩儿,es 里面的复杂的关联查询尽量别用,一旦用了性能一般都不太好。
最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 es 中。搜索的时候,就不需要利用 es 的搜索语法来完成 join 之类的关联搜索了。
document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es 能支持的操作就那么多,不要考虑用 es 做一些它不好操作的事情。如果真的有那种操作,尽量在 document 模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。
集群架构设计
合理的部署 Elasticsearch 有助于提高服务的整体可用性。
主节点、数据节点和协调节点分离
Elasticsearch 集群在架构拓朴时,采用主节点、数据节点和负载均衡节点分离的架构,在 5.x 版本以后,又可将数据节点再细分为“Hot-Warm”的架构模式。
Elasticsearch 的配置文件中有 2 个参数,node.master 和 node.data。这两个参数搭配使用时,能够帮助提供服务器性能。
主(master)节点
配置 node.master:true 和 node.data:false,该 node 服务器只作为一个主节点,但不存储任何索引数据。我们推荐每个集群运行3 个专用的 master 节点来提供最好的弹性。使用时,你还需要将 discovery.zen.minimum_master_nodes setting 参数设置为 2,以免出现脑裂(split-brain)的情况。用 3 个专用的 master 节点,专门负责处理集群的管理以及加强状态的整体稳定性。因为这 3 个 master 节点不包含数据也不会实际参与搜索以及索引操作,在 JVM 上它们不用做相同的事,例如繁重的索引或者耗时,资源耗费很大的搜索。因此不太可能会因为垃圾回收而导致停顿。因此,master 节点的 CPU,内存以及磁盘配置可以比 data 节点少很多的。
数据(data)节点
配置 node.master:false 和 node.data:true,该 node 服务器只作为一个数据节点,只用于存储索引数据,使该 node 服务器功能单一,只用于数据存储和数据查询,降低其资源消耗率。
在 Elasticsearch 5.x 版本之后,data 节点又可再细分为“Hot-Warm”架构,即分为热节点(hot node)和暖节点(warm node)。
hot 节点:
hot 节点主要是索引节点(写节点),同时会保存近期的一些频繁被查询的索引。由于进行索引非常耗费 CPU 和 IO,即属于 IO 和 CPU 密集型操作,建议使用 SSD 的磁盘类型,保持良好的写性能;我们推荐部署最小化的 3 个 hot 节点来保证高可用性。根据近期需要收集以及查询的数据量,可以增加服务器数量来获得想要的性能。
将节点设置为 hot 类型需要 elasticsearch.yml 如下配置:
node.attr.box_type: hot
如果是针对指定的 index 操作,可以通过 settings 设置 index.routing.allocation.require.box_type: hot 将索引写入 hot 节点。
warm 节点:
这种类型的节点是为了处理大量的,而且不经常访问的只读索引而设计的。由于这些索引是只读的,warm 节点倾向于挂载大量磁盘(普通磁盘)来替代 SSD。内存、CPU 的配置跟 hot 节点保持一致即可;节点数量一般也是大于等于 3 个。
将节点设置为 warm 类型需要 elasticsearch.yml 如下配置:
node.attr.box_type: warm
同时,也可以在 elasticsearch.yml 中设置 index.codec:best_compression 保证 warm 节点的压缩配置。
当索引不再被频繁查询时,可通过 index.routing.allocation.require.box_type:warm,将索引标记为 warm,从而保证索引不写入 hot 节点,以便将 SSD 磁盘资源用在刀刃上。一旦设置这个属性,ES 会自动将索引合并到 warm 节点。
协调(coordinating)节点
协调节点用于做分布式里的协调,将各分片或节点返回的数据整合后返回。该节点不会被选作主节点,也不会存储任何索引数据。该服务器主要用于查询负载均衡。在查询的时候,通常会涉及到从多个 node 服务器上查询数据,并将请求分发到多个指定的 node 服务器,并对各个 node 服务器返回的结果进行一个汇总处理,最终返回给客户端。在 ES 集群中,所有的节点都有可能是协调节点,但是,可以通过设置 node.master、node.data、node.ingest 都为 false 来设置专门的协调节点。需要较好的 CPU 和较高的内存。
- node.master:false和node.data:true,该node服务器只作为一个数据节点,只用于存储索引数据,使该node服务器功能单一,只用于数据存储和数据查询,降低其资源消耗率。
- node.master:true和node.data:false,该node服务器只作为一个主节点,但不存储任何索引数据,该node服务器将使用自身空闲的资源,来协调各种创建索引请求或者查询请求,并将这些请求合理分发到相关的node服务器上。
- node.master:false和node.data:false,该node服务器即不会被选作主节点,也不会存储任何索引数据。该服务器主要用于查询负载均衡。在查询的时候,通常会涉及到从多个node服务器上查询数据,并将请求分发到多个指定的node服务器,并对各个node服务器返回的结果进行一个汇总处理,最终返回给客户端。
关闭data节点服务器中的http功能
针对 Elasticsearch 集群中的所有数据节点,不用开启 http 服务。将其中的配置参数这样设置,http.enabled:false,同时也不要安装 head, bigdesk, marvel 等监控插件,这样保证 data 节点服务器只需处理创建/更新/删除/查询索引数据等操作。
http 功能可以在非数据节点服务器上开启,上述相关的监控插件也安装到这些服务器上,用于监控 Elasticsearch 集群状态等数据信息。这样做一来出于数据安全考虑,二来出于服务性能考虑。
一台机器只部署一个 node
一台物理服务器上可以启动多个 node 服务器节点(通过设置不同的启动 port),但一台服务器上的 CPU、内存、硬盘等资源毕竟有限,从服务器性能考虑,不建议一台服务器上启动多个 node 节点。
合理分配索引分片
不同版本之间的优化不太一样,具体请去官方文档里选择对应的版本查看,关键词“How to”
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/size-your-shards.html
为什么要考虑副本分片数量?
大多数ElasticSearch用户在创建索引时通用会考虑一个重要问题是: 我需要创建多少个分片?
分片分配是个很重要的概念, 很多用户对如何分片都有所疑惑, 当然是为了让分配更合理. 在生产环境中, 随着数据集的增长, 不合理的分配策略可能会给系统的扩展带来严重的问题。
谨慎分片
副本对搜索性能非常重要, 同时用户也可在任何时候添加或删除副本。额外的副本能给你带来更大的容量, 更高的呑吐能力及更强的故障恢复能力。
当在ElasticSearch集群中配置好你的索引后, 你要明白在集群运行中你无法调整分片设置。既便以后你发现需要调整分片数量, 你也只能新建创建并对数据进行重新索引(reindex虽然reindex会比较耗时, 但至少能保证你不会停机)。
主分片的配置与硬盘分区很类似, 在对一块空的硬盘空间进行分区时, 会要求用户先进行数据备份, 然后配置新的分区, 最后把数据写到新的分区上。
在分片时,主要考虑数据集的增长趋势, 一定要做到不要过度分片, 并不是分片越多越好, 从ES社区用户对这个热门主题(分片配置)的分享数据来看, 用户可能认为过度分配是个绝对安全的策略(这里讲的过度分配是指对特定数据集, 为每个索引分配了超出当前数据量(文档数)所需要的分片数)。
稍有富余是好的, 但过度分配分片却是大错特错. 具体定义多少分片很难有定论, 取决于用户的数据量和使用方式. 100个分片, 即便很少使用也可能是好的; 而2个分片, 即便使用非常频繁, 也可能是多余的.
要熟知以下几点内容:
搜索在每个分片的单个线程上运行
大多数搜索都会命中多个分片。每个分片在单个 CPU 线程上运行搜索。虽然一个分片可以运行多个并发搜索,但跨大量分片的搜索会耗尽节点的搜索线程池。这会导致吞吐量低和搜索速度慢。
每个分片都有开销
每个分片都使用内存和 CPU 资源。在大多数情况下,一小组大分片使用的资源少于许多小分片。
段在分片的资源使用中起着重要作用。大多数分片包含多个段,用于存储其索引数据。Elasticsearch 将段元数据保存在 JVM 堆内存中,以便可以快速检索以进行搜索。随着分片的增长,它的片段被合并成更少、更大的片段。这减少了段的数量,这意味着更少的元数据保留在堆内存中。
Elasticsearch 自动平衡数据层内的分片
集群的节点被分组为数据层。在每一层中,Elasticsearch 尝试将索引的分片分布到尽可能多的节点上。当您添加一个新节点或一个节点发生故障时,Elasticsearch 会自动在该层的剩余节点之间重新平衡索引的分片。
相关性
ES使用词频统计来计算相关性。当然这些统计也会分配到各个分片上。如果在大量分片上只维护了很少的数据,则将导致最终的文档相关性较差。
分片设置的可参考原则
10GB 到 50GB 之间的分片大小
较大的分片在发生故障后需要更长的时间才能恢复。当一个节点出现故障时,Elasticsearch 会在数据层的剩余节点之间重新平衡该节点的分片。此恢复过程通常涉及通过网络复制分片内容,因此 100GB 分片的恢复时间是 50GB 分片的两倍。相比之下,小分片的开销成比例地增加并且搜索效率较低。搜索 50 个 1GB 的分片将比搜索包含相同数据的单个 50GB 的分片占用更多的资源。
分片大小没有硬性限制,但经验表明,10GB 到 50GB 之间的分片通常适用于日志和时间序列数据。根据您的网络和用例,您可以使用更大的分片。较小的分片可能适用于 企业搜索和类似用例。
如果使用 ILM,请将翻转操作的 max_primary_shard_size
阈值设置为50gb
以避免分片大于 50GB。
每 GB 堆内存 20 个分片或更少
节点可以容纳的分片数量与节点的堆内存成正比。例如,具有 30GB 堆内存的节点最多应具有 600 个分片。越低于此限制,您可以保留节点越好。如果您发现你的节点每 GB 超过 20 个分片,请考虑添加另一个节点
在非高峰时段强制合并
如果你不再写入索引,则可以使用强制合并 API将较小的段合并为较大的段。这可以减少分片开销并提高搜索速度。然而,强制合并是资源密集型的。如果可能,请在非高峰时段运行强制合并。
POST /my-index-000001/_forcemerge
避免节点热点
如果分配给特定节点的分片过多,则该节点可能成为热点。例如,如果单个节点包含的索引分片过多,索引量很大,则该节点很可能会出现问题。
在开始阶段, 一个好的方案是根据你的节点数量按照1.5~3倍的原则来创建分片. 例如,如果你有3个节点, 则推荐你创建的分片数最多不超过9(3x3)个。当性能下降时,增加节点,ES会平衡分片的放置。
对于基于日期的索引需求, 并且对索引数据的搜索场景非常少. 也许这些索引量将达到成百上千, 但每个索引的数据量只有1GB甚至更小. 对于这种类似场景, 建议只需要为索引分配1个分片。如日志管理就是一个日期的索引需求,日期索引会很多,但每个索引存放的日志数据量就很少。
当索引拥有较多分片时, 为了组装查询结果, ES必须单独查询每个分片(当然并行的方式)并对结果进行合并. 所以高性能IO设备(SSDs)和多核处理器无疑对分片性能会有巨大帮助. 尽管如此, 你还是要多关心数据本身的大小,更新频率以及未来的状态. 在分片分配上并没有绝对的答案.
将现有索引缩小到更少的分片
如果您不再写入索引,则可以使用 收缩索引 API来减少其分片数。
小结
避免Over Sharing
- 一个查询需要访问每一个分片,分片过多,会导致不必要的查询开销
结合应用场景,控制单个分片的尺寸
- 搜索场景:20GB以内
- 日志场景:40GB以内
Force-merge Read-only索引
- 使用基于时间序列的索引,将只读的索引force merge,减少segment数量
分片应该设置几个副本?
说明:副本数是可以随时调整的!
思考:
副本的用途是什么? 备份数据保证高可用数据不丢失,高并发的时候参与数据查询
针对它的用途,该如何设置它的副本数? 一般一个分片有1-2个副本即可保证高可用
集群规模没变的情况下副本过多会有什么影响? 副本多浪费存储空间、占用资源、影响性能
副本设置基本原则
为保证高可用,副本数设置为2即可。要求集群至少要有3个节点,来分开存放主分片、副本。
如发现并发量大时,查询性能会下降,可增加副本数,来提升并发查询能力。
注意:新增副本时主节点会自动协调,然后拷贝数据到新增的副本节点
分片操作
正如上文中提到,创建分片,不超过3倍,这里有两个节点,所以可以设置6个分片。
#在每个节点上执行,分配1个副本6个分片
[root@elkstack01 ~]# curl -XPUT 10.0.0.51:9200/_template/my_template -d'
{ "template": "*",
"settings": {
"index": {
"number_of_shards": 6,
"number_of_replicas": 1
}
}
}'
#返回结果为true,分片成功
{"acknowledged":true}
测试提交数据
验证索引及页面详解
主节点和副本节点的区别
主节点的职责: 统计各node节点状态信息、集群状态信息统计、索引的创建和删除、索引分配的管理、关闭node节点等。
副本节点的职责: 同步数据,等待机会成为Master(当主节点宕机或者重启时)。
es自动创建的索引 默认一个副本,灰色就是说有一个副本,没有节点去存
堆内存分配建议
- 将最小堆大小(Xms)和最大堆大小(Xmx)设置为彼此相等。
- Elasticsearch可用的堆越多,可用于缓存的内存就越多。但请注意,太多的堆内存可能会使您长时间垃圾收集暂停。
- 将Xmx设置为不超过物理内存的50%,以确保有足够的物理内存留给内核文件系统缓存。
- 不要将Xmx设置为JVM超过32GB。
分片大小建议:
- 宿主机内存大小的一半和31GB,取最小值。
每个分片都有数据需要保存在内存中并使用堆空间。 这包括在分片级别保存信息的数据结构,也包括在段级别的数据结构,以便定义数据驻留在磁盘上的位置。 这些数据结构的大小不是固定的,并且将根据用例而有所不同。
然而,段相关开销的一个重要特征是它与分段的大小不成正比。 这意味着与较小的段相比,较大的段的每个数据量具有较少的开销,且这种差异很大。
【堆内存的重要性】为了能够每个节点存储尽可能多的数据,重要的是尽可能多地管理堆内存使用量并减少其开销。 节点拥有的堆空间越多,它可以处理的数据和分片越多。
因此,索引和分片从集群的角度看待不是空闲的,因为每个索引和分片都有一定程度的资源开销。
*提示1:小分片会导致小分段(segment),从而增加开销。目的是保持平均分片大小在几GB和几十GB之间。对于具有基于时间的数据的用例,通常看到大小在20GB和40GB之间的分片。
*提示2:由于每个分片的开销取决于分段数和大小,通过强制操作迫使较小的段合并成较大的段可以减少开销并提高查询性能。一旦没有更多的数据被写入索引,这应该是理想的。请注意,这是一个消耗资源的(昂贵的)操作,较为理想的处理时段应该在非高峰时段执行。
*提示3:您可以在集群节点上保存的分片数量与您可用的堆内存大小成正比,但这在Elasticsearch中没有的固定限制。 一个很好的经验法则是:确保每个节点的分片数量保持在低于每1GB堆内存对应集群的分片在20-25之间。 因此,具有30GB堆内存的节点最多可以有600-750个分片,但是进一步低于此限制,您可以保持更好。 这通常会帮助群体保持处于健康状态。
合理的使用分词器
分词器决定分词的粒度,常见的中文分词 IK 可细分为:
- 粗粒度分词:ik_smart。
- 细粒度分词:ik_max_word。
从存储角度基于 ik_max_word 分词的索引会比基于 ik_smart 分词的索引占据空间大。
而更细粒度自定义分词 ngram 会占用大量资源,并且可能减慢索引速度并显着增加索引大小。
所以要结合检索指标(召回率和精准率)、结合写入场景斟酌选型。
禁止 swap
禁止 swap,一旦允许内存与磁盘的交换,会引起致命的性能问题。可以通过在 elasticsearch.yml 中 bootstrap.memory_lock: true,以保持 JVM 锁定内存,保证 ES 的性能。
GC 设置
老的版本中官方文档 (opens new window)中推荐默认设置为:Concurrent-Mark and Sweep(CMS),给的理由是当时G1 还有很多 BUG。
原因是:已知JDK 8附带的HotSpot JVM的早期版本存在一些问题,当启用G1GC收集器时,这些问题可能导致索引损坏。受影响的版本早于JDK 8u40随附的HotSpot版本。来源于官方说明 (opens new window)
实际上如果你使用的JDK8较高版本,或者JDK9+,推荐你使用G1 GC; 因为我们目前的项目使用的就是G1 GC,运行效果良好,对Heap大对象优化尤为明显。修改jvm.options
文件,将下面几行:
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
更改为
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50
其中 -XX:MaxGCPauseMillis是控制预期的最高GC时长,默认值为200ms,如果线上业务特性对于GC停顿非常敏感,可以适当设置低一些。但是 这个值如果设置过小,可能会带来比较高的cpu消耗。
G1对于集群正常运作的情况下减轻G1停顿对服务时延的影响还是很有效的,但是如果是你描述的GC导致集群卡死,那么很有可能换G1也无法根本上解决问题。 通常都是集群的数据模型或者Query需要优化。
磁盘
硬盘对所有的集群都很重要,对大量写入的集群更是加倍重要(例如那些存储日志数据的)。硬盘是服务器上最慢的子系统,这意味着那些写入量很大的集群很容易让硬盘饱和,使得它成为集群的瓶颈。
在经济压力能承受的范围下,尽量使用固态硬盘(SSD)。固态硬盘相比于任何旋转介质(机械硬盘,磁带等),无论随机写还是顺序写,都会对 IO 有较大的提升。
- 如果你正在使用 SSDs,确保你的系统 I/O 调度程序是配置正确的。当你向硬盘写数据,I/O 调度程序决定何时把数据实际发送到硬盘。大多数默认 *nix 发行版下的调度程序都叫做 cfq(完全公平队列)。
- 调度程序分配时间片到每个进程。并且优化这些到硬盘的众多队列的传递。但它是为旋转介质优化的:机械硬盘的固有特性意味着它写入数据到基于物理布局的硬盘会更高效。
- 这对 SSD 来说是低效的,尽管这里没有涉及到机械硬盘。但是,deadline 或者 noop 应该被使用。deadline 调度程序基于写入等待时间进行优化,noop 只是一个简单的 FIFO 队列。
这个简单的更改可以带来显著的影响。仅仅是使用正确的调度程序,我们看到了 500 倍的写入能力提升。
如果你使用旋转介质(如机械硬盘),尝试获取尽可能快的硬盘(高性能服务器硬盘,15k RPM 驱动器)。
使用 RAID0 是提高硬盘速度的有效途径,对机械硬盘和 SSD 来说都是如此。没有必要使用镜像或其它 RAID 变体,因为 Elasticsearch 在自身层面通过副本,已经提供了备份的功能,所以不需要利用磁盘的备份功能,同时如果使用磁盘备份功能的话,对写入速度有较大的影响。
最后,避免使用网络附加存储(NAS)。人们常声称他们的 NAS 解决方案比本地驱动器更快更可靠。除却这些声称,我们从没看到 NAS 能配得上它的大肆宣传。NAS 常常很慢,显露出更大的延时和更宽的平均延时方差,而且它是单点故障的。
其他优化
分片延迟分配
说明:
ES会自动在可用节点间进行分片均衡,包括新节点的加入和现有节点离线都会触发这个动作。
例:
- 在集群中,有一个node因为故障导致意外关机重启
- Master立即注意到这个节点重启,他会把集群内其他节点拥有掉线节点主节点对应的副本升级为主节点
- 在副本被提升为主节点后,master节点开支执行重建确实副本的操作,集群内的节点间互相拷贝数据,导致网卡,IO性能增加
- 由于集群现在处于非平衡状态,这个过程可能会触发小规模的分片移动。其他不相关的分片将会在节点间迁移来达到一个最佳的平衡状态
- 刚刚掉线的节点重启好了,重新加入集群,被告知,当前的数据已经失效了,节点会把当前数据删除,然后集群又开始重新分配分配,引起集群内资源的消耗
- 在日常工作中,主机和服务的重启都无法避免,如果重启时间比较短的情况下,如果发生上述的过程,会消耗过多的资源,这是我们不希望看到的。
操作:
-
默认情况下,集群会等待一分钟来查看节点是否会重新加入集群,如果集群在这个时间内重新加入集群,重新加入的节点会保持现有的分配数据,不会触发新的分片分配。
-
通过delayed_timeout,默认等待时间可以全局设置,也可以在索引级别进行设置:
PUT /_all/_settings { "settings": { "index.unassigned.node_left.delayed_timeout": "5m" } }
- _all: 所有索引,也可以修改为单个索引名。
- 5m: 默认时间修改为5min,也可以设置为
0m
,立即分配
NOTE
延迟分配不会阻止副本被提拔为主分片。集群还是会进行必要的提拔来让集群回到 yellow 状态。缺失副本的重建是唯一被延迟的过程。
自动取消分片迁移
- 如果节点在超时之后回来,集群还没有完成分片的迁移操作,这个时候,Elasticsearch会检查上线的节点的数据是否和当前活跃主分片的数据是否一直,如果两者匹配,说明没有新的文档存储进来,这个时候,集群会取消分片的操作,并启用改节点上的数据,因为磁盘比网络更快,这样更合乎常理。
- 但是如果已经产生分歧,在节点离线这段时间内,离线节点的主索引的数据不一致,这个时候还是会按照正常流程来进行操作。
Rollover 自动拆分索引
当满足指定的条件后,索引就会被自动拆分
指定方法:
PUT /logs-000001
{
"aliases": {
"logs_write": {}
}
}
# Add > 1000 documents to logs-000001
POST /logs_write/_rollover
{
"conditions": {
"max_age": "7d", # 最大的时间限制
"max_docs": 1000 # 最大的条数
}
}
注意事项:
- 索引命名规则必须如同:logs-000001。
- 索引必须指定 aliases。
- Rollover Index API 调用时才去检查索引是否超出指定规则,不会自动触发,需要手动调用,可以通过 Curator 实现自动化。
- 如果符合条件会创建新的索引,老索引的数据不会发生变化,如果你已经插入 2000 条,拆分后还是 2000 条。
- 插入数据时一定要用别名,否则你可能一直在往一个索引里追加数据。
示例:
假如生成的索引名为 logs-2017.04.13-1,如果你在当天执行 Rollover 会生成 logs-2017.04.13-000001,次日的话是 logs-2017.04.14-000001。
Store(存储)
文件存储类型
不同的文件系统有不同的存储类型。默认情况下,Elasticsearch将根据操作环境选择最佳实现。
可选的存储类型有:
- fs :默认实现,取决于操作系统
- simplefs :对应Lucene SimpleFsDirectory
- niofs :对应Lucene NIOFSDirectory
- mmapfs :对应Lucene MMapDirectory
可以改变这种设置,通过在config/elasticsearch.yml中添加如下配置,例如:
index.store.type: niofs
上面的设置对所有的索引都生效。你也可以在索引创建的时候针对某一个特定的索引进行设置,例如:
curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
"settings": {
"index.store.type": "niofs"
}
}
'
预加载数据到文件系统缓存
默认情况下,Elasticsearch完全依赖于操作系统的文件系统缓存来缓存I/O操作。可以设置index.store.preload来告诉操作系统在打开时将热点索引文件的内容加载到内存中。这个选项接受一个逗号分隔的文件扩展列表:扩展名在列表中的所有文件将在打开时预加载。这对于提高索引的搜索性能非常有用,特别是在主机操作系统重启时,因为这会导致文件系统缓存被丢弃。但是请注意,这可能会减慢索引的打开速度,因为只有在将数据加载到物理内存之后,索引才会可用。
静态设置的话可以这样设置:
index.store.preload: ["nvd", "dvd"]
或者在索引创建的时候设置:
curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d'
{
"settings": {
"index.store.preload": ["nvd", "dvd"]
}
}
'
默认值是一个空数组,意味着文件系统不会预加载任何数据。对于可搜索的索引,你可能想要把它们设置为["nvd", "dvd"],这将会使得norms和doc数据被预先加载到物理内存。不推荐把所有的文件都预加载到内存,通常可能更好的选择是设置为["nvd", "dvd", "tim", "doc", "dim"],这样的话将会预加载norms,doc values,terms dictionaries, postings lists 和 points
参考
标签:index,写入,查询,索引,Elasticsearch,分片,优化,节点 From: https://www.cnblogs.com/Insa/p/17011910.htmlhttps://zhuanlan.zhihu.com/p/366785695 Elasticsearch:从写入原理谈写入优化
https://cloud.tencent.com/developer/article/1737402 极限压榨elasticsearch写入速度
https://www.cnblogs.com/deepSleeping/p/14566024.html Elasticsearch性能优化汇总——写入&搜索
https://www.modb.pro/db/381929 ES系列8-ElasticSearch搜索及持久化变更
https://pdai.tech/md/db/nosql-es/elasticsearch-y-peformance.html ES详解 - 优化:ElasticSearch性能优化详解
https://mp.weixin.qq.com/s/mKL2PJuNUJTl71Axv4-Rcw Elasticsearch究竟要设置多少分片数?
https://www.elastic.co/guide/en/elasticsearch/reference/current/size-your-shards.html size-your-shards