1.ES基本概念
为了更好的理解内存,我们先看一下ES的基本概念。
1.1 cluster 集群
多个节点组合在一起就形成了一个集群,在每个ES节点中,我们可以通过配置集群的名称来使各个节点组合在一起,成为一个集群。当某些节点的集群名称一样,ES会自动根据配置文件中的地址找到这些节点,然后就自动组成一个集群了,这一切都是ES自动来完成的,所以说ES的分布式扩展性很强,在这些节点中可以随时增加和删除节点不用费一点精力。
1.2.node 节点
ES的时候是在一台服务器上安装ES,其实这就是一个节点(Node),由ES是分布式的,所以我们可以在多台服务器上安装ES,这样我们就有了多个节点。
1.3.index 索引、type类型、Document文档
(逻辑命名空间,它映射到一个或多个主分片,并且可以具有零个或多个副本分片)类似mysql中的数据库
type 文档的逻辑容器,类似表,在 Elasticsearch 6.0 以后,一个 Index 只能含有一个 type。在默认的情况下是 _doc。在 8.0 的版本中,type 将被彻底删除
Document 文档 就是 mysql里的一条记录,ES的最小单元。
查看索引信息:
curl -XGET http://localhost:9200/twitter/_settings?pretty
1.4 share 分片
share,Elasticsearch 提供了将索引划分成多份的能力,这些份就叫做分片(shard)。
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
有两种类型的分片:Primary shard 和 Replica shard
Primary shard: 每个文档都存储在一个 Primary shard。 索引文档时,它首先在Primary shard 上编制索引,然后在此分片的所有副本上(replica)编制索引。索引可以包含一个或多个主分片。
Replica shard: 每个主分片可以具有零个或多个副本。 副本是主分片的副本,有两个目的:
增加故障转移:如果主要故障,可以将副本分片提升为主分片。
提高性能:get 和 search 请求可以由主 shard
永远不会在与其主分片相同的节点上启动副本分片
默认情况下,Elasticsearch 为每个索引创建一个主分片和一个副本。
下面的图表示的是一个 index 有 5 个 shard 及 1 个 replica
分配多个分片和副本是分布式搜索功能设计的本质,提供高可用性和快速访问索引中的文档。
主副本和副本分片之间的主要区别在于,只有主分片可以接受索引请求。副本和主分片都可以提供查询请求
如果一个 index 显示的是红色,表面这个 index 至少有一个 primary shard 没有被正确分配,并且有的 shard 及其相应的 replica 已经不能正常访问。
如果是绿色,表明 index 的每一个 shard 都有备份 (replica),并且其备份也成功复制在相应的replica shard 之中。
如果其中的一个 node 坏了,相应的另外一个 node 的 replica将起作用,从而不会造成数据的丢失。
获得index健康情况
http://localhost:9200/_cat/indices/twitter
yellow 标识RS PS在同一个node中,无法高可用
http://localhost:9200/_cat/shards
2.ES内存基础知识
2.1 内存架构
Elasticsearch能控制的是On Heap内存部分,这部分由JVM管理;Off Heap由Lucene管理,负责缓存倒排索引数据空间(Segment Memory)。
同时堆内内存又分为可以 GC 和不可以 GC,不可 GC 的部分采用 LRU 方式进行缓存更新。
2.2 内存设置
配置原则
作为一个Java应用程序,Elasticsearch需要从系统的物理内存中分配一些逻辑内存(堆)。这应该最多是物理RAM的一半,上限为32GB。设置较高的堆使用率通常是为了应对开销较大的查询和更大的数据存储。父级熔断器默认值为95%,但我们建议在持续达到85%时就扩展资源。
设置Elasticsearch内存的方式:
1、设置变量的方式: export ES_HEAP_SIZE=32G 该方式比较好
2、启动es时添加启动差数: -Xmx 32G -Xms 32G ,Xmx和Xms的大小最好一样,防止程序在运行时改变大小。
将最小堆大小(Xms)和最大堆大小(Xmx)设置为彼此相等
防止内存抖动
给多大的堆内存?
给ES的内存配置不是越大越好,建议不能超过32GB,不同jdk版本最大边界值是不同的,对于32位小于32G JVM才采用内存对象指针压缩技术,不然对象指针需要占用很大的内存; 一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,意味着就算给ES100G的内存,ES仍然最多使用30-32G的内存;
预留一半的内存给lucene
假如设备的内存是64G , 那就要预留出32G的内存给Lucene,Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。
建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存
Elasticsearch最大分配32G内存的原因:
1、内存对于Elasticsearch来说绝对是重要的,用于更多的内存数据提供更快的操作。而且还有一个内存消耗大户-Lucene
Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。
Lucene的性能取决于和OS的交互,如果你把所有的内存都分配给Elasticsearch,不留一点给Lucene,那你的全文检索性能会很差的。
最后标准的建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存。不要超过32G
2、不分配大内存给Elasticsearch,事实上jvm在内存小于32G的时候会采用一个内存对象指针压缩技术。
在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,不是32bit就是64bit,这取决于你的处理器,指针指向了你的值的精确位置。
对于32位系统,内存最大可使用4G。64系统可以使用更大的内存。但是64位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器之间移动数据的时候,会占用更多的带宽。
java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。最终,也就是说堆内存长到32G的物理内存,也可以用32bit的指针表示。
一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的CPU内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达40-50GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32G内存。
这段描述的意思就是说:即便你有足够的内存,也尽量不要超过32G,因为它浪费了内存,降低了CPU的性能,还要让GC应对大内存。
*swapping**是性能的坟墓*
内存交换到磁盘对服务器性能来说是致命的。想想看一个内存的操作必须是快速的。 如果内存交换到磁盘上,一个100微秒的操作可能变成10毫秒,再想想那么多10微秒的操作时延累加起来。不难看出swapping对于性能是多么可怕。 最好的办法就是在你的操作系统中完全禁用swapping
暂时禁用:sudo swapoff -a
永久禁用:
修改参数的方法是修改/etc/sysctl.conf文件,加入vm.swappiness=xxx,并重起系统。这个操作相当于是修改虚拟系统中的/proc/sys/vm/swappiness文件,将值改为XXX数值。
如果不想重起,可以通过sysctl -p动态加载/etc/sysctl.conf文件,但建议这样做之前先清空swap。
以上系统参数配置完成,还要修改elasticsearch.yml配置:
bootstrap.memory_lock : true 锁定内存,防止进行内存的交换使用swapping
elasticsearch配置文件
直接在本地 Elasticsearch 文件的 config > jvm.options 文件中配置。
## JVM configuration ################################################################ ## IMPORTANT: JVM heap size ################################################################ … # Xms represents the initial size of total heap space # Xmx represents the maximum size of total heap space -Xms4g -Xmx4g
docker-compose配置文件
作为 docker-compose 中的 Elasticsearch 环境变量。
version: '2.2' services: es01: image: docker.elastic.co/elasticsearch/elasticsearch:7.12.0 environment: - node.name=es01 - cluster.name=es - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - discovery.type=single-node ulimits: memlock: soft: -1 hard: -1 ports: - 9200:9200
熔断器配置
Elasticsearch 有一系列的断路器,它们都能保证内存不会超出限制:
- indices.breaker.fielddata.limit fielddata 断路器默认设置堆的 60% 作为 fielddata 大小的上限。
- indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 60%。它实际上是node level的一个统计值,统计的是这个结点上,各类查询聚合操作,需要申请的Bigarray的空间大小总和。 所以如果有一个聚合需要很大的空间,同时在执行的聚合可能也会被break掉。
- indices.breaker.total.limit 父熔断,inflight、request(agg)和fielddata不会使用超过堆内存的 70%。
- network.breaker.inflight requests.limit 限制当前通过HTTP等进来的请求使用内存不能超过Node内存的指定值。这个内存主要是限制请求内容的长度。 默认100%。
- script.max_compilations_per_minute
- 限制script并发执行数,默认值为15。
2.3 Segments Memory
一个segment是一个完备的lucene倒排索引,而倒排索引是通过词典 (Term Dictionary)到文档列表(Postings List)的映射关系,快速做查询的。 由于词典的size会很大,全部装载到heap里不现实,因此Lucene为词典做了一层前缀索引(Term Index),这个索引在Lucene4.0以后采用的数据结构是FST (Finite State Transducer)。 这种数据结构占用空间很小,Lucene打开索引的时候将其全量装载到内存中,加快磁盘上词典查询速度的同时减少随机磁盘访问次数。
segments FST数据的缓存,为了加速查询,FST 永驻堆内内存,无法被 GC 回收。该部分内存无法设置大小,长期占用 50% ~ 70% 的堆内存,只能通过delete index,close index以及force-merge index释放内存。
解释下FST:
ES 底层存储采用 Lucene(搜索引擎),写入时会根据原始数据的内容,分词,然后生成倒排索引。查询时,先通过 查询倒排索引找到数据地址(DocID)),再读取原始数据(行存数据、列存数据)。但由于 Lucene 会为原始数据中的每个词都生成倒排索引,数据量较大。所以倒排索引对应的倒排表被存放在磁盘上。这样如果每次查询都直接读取磁盘上的倒排表,再查询目标关键词,会有很多次磁盘 IO,严重影响查询性能。为了解磁盘 IO 问题,Lucene 引入排索引的二级索引 FST [Finite State Transducer] 。原理上可以理解为前缀树,加速查询。
ES的data node存储数据并非只是耗费磁盘空间的,为了加速数据的访问,每个segment都有会一些索引数据驻留在heap里。因此segment越多,瓜分掉的heap也越多,并且这部分heap是无法被GC掉的! 理解这点对于监控和管理集群容量很重要,当一个node的segment memory占用过多的时候,就需要考虑删除、归档数据,或者扩容了。
查看一个索引所有segment的memory占用情况:
curl -X GET "http://127.0.0.1:9200/_cat/segments/indexv&h=shard,segment,size,size.menory"
查看一个node上所有segment占用的memory总和:
curl -X GET "http://127.0.0.1:9200/_cat/nodes?h=name,hp,hm,rp,rm,qcm,rcm,fm,sm&v"
segment合并
我们已经知道在elasticsearch中每个shard每隔1秒都会refresh一次,每次refresh都会生成一个新的segment,按照这个速度过不了多久segment的数量就会爆炸,所以存在太多的segment是一个大问题,因为每一个segment都会占用文件句柄,内存资源,cpu资源,更加重要的是每一个搜索请求都必须访问每一个segment,这就意味着存在的segment越多,搜索请求就会变的更慢。
那么elaticsearch是如何解决这个问题呢? 实际上elasticsearch有一个后台进程专门负责segment的合并,它会把小segments合并成更大的segments,然后反复这样。在合并segments的时候标记删除的document不会被合并到新的更大的segment里面,所有的过程都不需要我们干涉,es会自动在索引和搜索的过程中完成,合并的segment可以是磁盘上已经commit过的索引,也可以在内存中还未commit的segment:
(1)在索引时refresh进程每秒会创建一个新的segment并且打开它使得搜索可见
(2)merge进程会在后台选择一些小体积的segments,然后将其合并成一个更大的segment,这个过程不会打断当前的索引和搜索功能。
(3)一旦merge完成,旧的segments就会被删除,流程如下:
3.1 新的segment会被flush到磁盘
3.2 然后会生成新的commit point文件,包含新的segment名称,并排除掉旧的segment和那些被合并过的小的segment
3.3 接着新的segment会被打开用于搜索
3.4 最后旧的segment会被删除掉
至此原来标记伪删除的document都会被清理掉,如果不加控制,合并一个大的segment会消耗比较多的io和cpu资源,同时也会搜索性能造成影响,所以默认情况下es已经对合并线程做了资源限额以便于它不会搜索性能造成太大影响。
api如下:
PUT /_cluster/settings
{
"persistent" : {
"indices.store.throttle.max_bytes_per_sec" : "100mb"
}
}
或者不限制:
PUT /_cluster/settings
{
"transient" : {
"indices.store.throttle.type" : "none"
}
}
es的api也提供了我们外部发送命令来强制合并segment,这个命令就是optimize,它可以强制一个shard合并成指定数量的segment,这个参数是:max_num_segments ,一个索引它的segment数量越少,它的搜索性能就越高,通常会optimize成一个segment。需要注意的是optimize命令不要用在一个频繁更新的索引上面,针对频繁更新的索引es默认的合并进程就是最优的策略,optimize命令通常用在一个静态索引上,也就是说这份索引没有写入操作只有查询操作的时候是非常适合用optimize来优化的,比如说我们的一些日志索引,基本都是按天,周,或者月来索引的,只要过了今天,这周或这个月就基本没有写入操作了,这个时候我们就可以通过optimize命令,来强制合并每个shard上索引只有一个segment,这样查询性能就能大大提升,api如下:
POST /logstash-2014-10/_optimize?max_num_segments=1
注意,由外部发送的optimize命令是没有限制资源的,也就是你系统有多少IO资源就会使用多少IO资源,这样可能导致某一段时间内搜索没有任何响应,所以如果你计划要optimize一个超大的索引,你应该使用shard allocation功能将这份索引给移动到一个指定的node机器上,以确保合并操作不会影响其他的业务或者es本身的性能。
减少segments内存的办法
- 删除不用的索引。delete index
- 关闭索引(文件仍然存在于磁盘,只是释放掉内存),需要的时候可重新打开。close index
- 定期对不再更新的索引做force merge
2.4 Indexing buffer(索引写入缓冲区)
索引写入缓冲区,用于存储新写入的文档,当其被填满时,缓冲区中的文档被写入磁盘中的 segments 中。节点上所有 shard 共享。这部分空间是可以通过GC被反复利用的。
索引写入缓冲区用于存储新建索引的文档。 填满时,缓冲区中的文档将写入磁盘上的segments。 它在节点上的所有分片之间划分。
indexing buffer配置
以下设置是静态的,必须在群集中的每个数据节点上进行配置:
indices.memory.index_buffer_size 接受百分比或字节大小的值。 它默认为10%,这意味着分配给一个节点的总堆栈的10%将用作所有分片共享的索引缓冲区大小。
indices.memory.min_index_buffer_size 如果将index_buffer_size指定为百分比,则可以使用此设置指定绝对最小值。 默认值为48mb。
ndices.memory.max_index_buffer_size 如果index_buffer_size被指定为百分比,则可以使用此设置来指定绝对最大值。 默认为限制
indexing buffer注意事项
由于可以GC,有flush操作,不需要特殊的关注
Indexing Buffer是用来缓存新数据,当其满了或者refresh/flush interval到了,就会以segment file的形式写入到磁盘。 这个参数的默认值是10% heap size。根据经验,这个默认值也能够很好的工作,应对很大的索引吞吐量。 但有些用户认为这个buffer越大吞吐量越高,因此见过有用户将其设置为40%的。到了极端的情况,写入速度很高的时候,40%都被占用,导致OOM。
2.5 Node Query Cache
node级别的filter过滤器结果缓存
Elasticsearch 集群中的每个节点包含一个 Node Query Cache,作用域是Node实例,由该节点的所有 shard 共享,Cache 采用 LRU 算法,Node Query Cache 只缓存 filter 部分耗时高的查询类型。
注:LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
什么情况下会产生NodeQueryCache
1)只有Filter下的子Query才能参与Cache。
2)不能参与Cache的Query有TermQuery/MatchAllDocsQuery/MatchNoDocsQuery/BooleanQuery/DisjunnctionMaxQuery。
3)MultiTermQuery/MultiTermQueryConstantScoreWrapper/TermInSetQuery/Point*Query的Query查询超过2次会被Cache,其它Query要5次。
4)默认每个段大于10000个doc或每个段的doc数大于总doc数的30%时才允许参与cache。
5)结果集比较大的Query在Cache时尽量增加使用周期以免频繁Cache构建DocIdset。
6)Segment被合并或者删除,那么也会清理掉对应的缓存。
7) 内存无法被GC。
Cache相关配置
1)indices.queries.cache.size:为每个节点配置缓存的内存大小,默认是10%,支持两种格式,一种是百分数,占节点heap的百分比,另一种是精确的值,如512mb,这个参数是静态的配置后需要重启节点生效。
2)indices.queries.cache.count: 配置缓存的总数量。
3)indices.queries.cache.all_segments: 用于是否在所有 Segment上启用缓存,默认是false,对文档数小于10000或者小于整个索引Doc的30%的Segment进行缓存。
4)index.queries.cache.enabled:属于index级别的配置,用来控制是否启用缓存,默认是开启的。
查看当前queryCache
curl -X GET "http://127.0.0.1:9200/_stats/query_cache?pretty&human"
2.6 Shard request cache
Shard Request Cache 简称 Request Cache,他是分片级别的查询缓存,每个分片有自己的缓存。该缓存采用 LRU 机制,缓存的 key 是整个客户端请求,缓存内容为单个分片的查询结果。如果一个客户端请求被数据节点缓存了,下次查询的时候可以直接从缓存获取,无需对分片进行查询。
结构:
final Key key = new Key(cacheEntity, reader.getReaderCacheHelper().getKey(), cacheKey);
cacheEntity, 主要是 shard信息,代表该缓存是哪个 shard上的查询结果。
readerCacheKey, 主要用于区分不同的 IndexReader。
cacheKey, 主要是整个客户端请求的请求体(source)和请求参数(preference、indexRoutings、requestCache等)。由于客户端请求信息直接序列化为二进制作为缓存 key 的一部分,所以客户端请求的 json 顺序,聚合名称等变化都会导致 cache 无法命中。
缓存的 value比较简单,就是将查询结果序列化之后的二进制数据。
Request Cache作用
Request Cache 的主要作用是对聚合的缓存,聚合过程是实时计算,通常会消耗很多资源,缓存对聚合来说意义重大。
只有客户端查询请求中 size=0的情况下才会被缓存,其他不被缓存的条件还包括 scroll、设置了 profile属性,查询类型不是 QUERY_THEN_FETCH,以及设置了 requestCache=false等。另外一些存在不确定性的查询例如:范围查询带有now,由于它是毫秒级别的,缓存下来没有意义,类似的还有在脚本查询中使用了 Math.random() 等函数的查询也不会进行缓存。
该缓存使用LRU淘汰策略,内存无法被GC。
1)默认被开启,ES6.71版本。
2)RequestCache作用域为Node,在Node中的Shard共享这个Cache空间。。
3)RequestCache 是以查询的DSL整个串为key的,修改一个字符和顺序都需要重新生成Cache。
4)缓存失效是索引的refresh操作,也可以设置失效时间。
5)缓存的默认大小是JVM堆内存的1%,可以通过手动设置。
首先,我们按照 汽车的颜色color来划分桶
size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
aggs:声明这是一个聚合查询,是aggregations的缩写
popular_colors:给这次聚合起一个名字,任意。
terms:划分桶的方式,这里是根据词条划分
field:划分桶的字段
Request Cache设置
ES 默认情况下最多使用堆内存的 1% 用作 Request Cache,这是一个节点级别的配置:
indices.requests.cache.size
Request Cache 默认是开启的。你可以为某个索引动态启用或禁用缓存:
PUT /my-index/_settings { "index.requests.cache.enable": true }
或者在查询请求级别通过 request_cache 参数来控制本次查询是否使用 cache,他会覆盖索引级别的设置。
GET /my-index/_search?request_cache=true
2.7 Fielddata Cache
FielddataCache是什么,开始做搜索就用ES5.0以后的朋友对这个Cache应该没什么概念了,因为早期版本,Lucene没有doc values这样的数据结构。 做数据聚合和排序的时候,需要将倒排索引的数据读取出来,重新组织成一个数组缓存,也就是从倒排索引中生成出来的要自己维护这段Cache, 之后才能够高效的做排序和聚合计算。后来ES中有了FielddataCache,但转换工作很耗资源,转换好的列表就会被缓存到fielddata cache,提升速度。 但是因为这个cache是在heap内部的,海量数据聚合的时候,生成的这些fielddata可能heap都放不下,很容易引起性能问题,甚至JVM OOM(还没有熔断保护的时候)。
FielddataCache作用
默认Elasticsearch2.0 开始,在非 text 字段开启 doc_values,基于 doc_values 做排序和聚合,可以减少对FielddataCache的依赖,减少内存消耗,减少节点 OOM 的概率,由于doc_values的特性性能上也不会有多少损失,doc_value是一种正向索引结构以顺序预读的方式进行获取,所以随机获取就很慢了。
Elasticsearch(后面简称ES)除了强大的搜索功能外,还可以支持排序,聚合之类的操作。搜索需要用到倒排索引,而排序和聚合则需要使用 “正排索引”。说白了就是一句话,倒排索引的优势在于查找包含某个项的文档,而反过来确定哪些项在单个文档里并不高效。
doc_values和fielddata就是用来给文档建立正排索引的。他俩一个很显著的区别是,前者的工作地盘主要在磁盘,而后者的工作地盘在内存。
5.0 开始,text 字段默认关闭了 Fielddata 功能, Fielddata Cache 应当只用于 global ordinals。
FielddataCache失效和Node QueryCache 失效机制相同,当 segment 被合并后,才会失效。
Fielddata Cache大家做了解吧,使用它的也非常的少了,基本可以用doc_value代替了,doc_value使用不需要全部载入内存
着ES版本的升级,对于doc_values的优化越来越好,索引的速度已经很接近fielddata了,而且我们知道硬盘的访问速度也是越来越快(比如SSD)。所以 doc_values 现在可以满足大部分场景,也是ES官方重点维护的对象。
所以我想说的是,doc values相比field data还是有很多优势的。所以 ES2.x 之后,支持聚合的字段属性默认都使用doc_values,而不是fielddata。
field data配置
indices.breaker.fielddata.limit: 60% (默认heap的60%) ,如果设置了indices.fielddata.cache.size,当达到size时,cache会剔除旧的fielddata。注意,indices.breaker.fielddata.limit 必须大于 indices.fielddata.cache.size,否则只会触发fielddata circuit breaker,而不会剔除旧的fielddata。
排查:
curl -X GET "http://127.0.0.1:9200/_stats/fielddata?pretty&human"
3.ES内存排查
如果您的集群目前遇到了性能问题,这很可能是因为一些常见的原因:
配置问题:分片过多,无 ILM 策略
量引起的:请求速度高、负载高,重叠昂贵的查询/写入重叠
3.1.分片过多问题
数据索引存储在sub-shards中,sub-shards利用堆进行维护,并搜索/写入请求。shard大小上限应为50GB,数量上限由以下的公式确定:
shards <= sum(nodes.max_heap) * 20
在上述的Elasticsearch Service示例中,两个区域之间有8GB的物理内存 (总共分配两个节点)。
# node.max_heap 8GB of physical memory / 2 = 4GB of heap # sum(nodes.max_heap) 4GB of heap * 2 nodes = 8GB # max shards 8GB * 20 160
然后将其与_cat/allocation进行交叉比较
GET /_cat/allocation?v=true&h=shards,node shards node 41 instance-0000000001 41 instance-0000000000
或与cluster/health进行交叉比较
GET /_cluster/health?filter_path=status,*_shards { "status": "green", "unassigned_shards": 0, "initializing_shards": 0, "active_primary_shards": 41, "relocating_shards": 0, "active_shards": 82, "delayed_unassigned_shards": 0 }
所以这个部署有82个分片, 最大推荐值为160。 如果计数高于建议值,您可能会在接下来的两部分中遇到如下情况(见下文)。
如果任何分片在active_shards或active_primary_shards之外的报告>0,则表明您已经找到了引发性能问题的主要配置原因。
最常见的情况是,如果报告了一个问题,则unassignd_shards >0。如果这些 shard 是主要分片,您的集群将报告“状态:红色”,如果只是副本,则报告为“状态:黄色”。(所以在索引上设置副本很重要,这样,在集群遇到问题时可以恢复,不会丢失数据。)
假设我们有一个“状态:黄色”和一个未指派的shard。为了进行调查,我们将利用_cat/shards来查看是哪个索引分片出现了问题。
GET _cat/shards?v=true&s=state index shard prirep state docs store ip node logs 0 p STARTED 2 10.1kb 10.42.255.40 instance-0000000001 logs 0 r UNASSIGNED kibana_sample_data_logs 0 p STARTED 14074 10.6mb 10.42.255.40 instance-0000000001 .kibana_1 0 p STARTED 2261 3.8mb 10.42.255.40 instance-0000000001
这将用于我们的非系统索引日志,它有一个未指派的副本分片。让我们运行 _cluster/allocation/explain来看看问题出在哪儿(专业提示:当您升级到支持时,这正是我们所做的事情)。
GET _cluster/allocation/explain?pretty&filter_path=index,node_allocation_decisions.node_name,node_allocation_decisions.deciders.* { "index": "logs", "node_allocation_decisions": [{ "node_name": "instance-0000000005", "deciders": [{ "decider": "data_tier", "decision": "NO", "explanation": "node does not match any index setting [index.routing.allocation.include._tier] tier filters [data_hot]" }]}]}
此错误消息指向data_hot,它是索引生命周期管理( ILM)策略的一部分,说明我们的ILM策略与当前的索引设置不一致。在本例中,此错误的原因是在没有指定hot-warm的情况下设置了hot-warm ILM策略。(我必须让一些东西出错,因为这是我在给你们演示错误示例。看看你们对我做了什么 😂)。
仅供参考,如果没有unsigned shards情况下就运行此命令,将会出现一个400 错误:当前已没有未指派到节点的分片,因为没有任何错误报告。
如果您遇到非逻辑原因(例如,在分配期间节点离开集群之类的临时网络错误),您可以随时使用Elastic的 _cluster/reroute。
POST /_cluster/reroute
这一无需定制的请求将启动一个异步后台进程,尝试分配所有当前状态:NASSIGNED shards。(别像我一样等到它完成后才联系开发人员,我当时以为这是瞬间的,而且可以很巧地为他们及时完成升级,然后告诉他们一切正常,因为什么都没有了。)
3.2.超过资源限制熔断器报错
最大化堆分配可能会引起对集群的请求超时或错误,而且,您的集群还会经常遇到熔断器异常这一问题。熔断会导致elasticsearch.log事件,例如:
Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]
要进行调查,请查看您的heap.percent,方法是查看 _cat/nodes。
GET /_cat/nodes?v=true&h=name,node*,heap* # heap = JVM (logical memory reserved for heap) # ram = physical memory name node.role heap.current heap.percent heap.max tiebreaker-0000000002 mv 119.8mb 23 508mb instance-0000000001 himrst 1.8gb 48 3.9gb instance-0000000000 himrst 2.8gb 73 3.9gb
或者,如果您之前已启用过它,请导航到 Kibana > Stack Monitoring。
如果您确定遇到了内存熔断问题,您应考虑暂时增加堆容量,以便给自己喘息的空间,并进行调查。在调查根本原因时,请查看您的集群代理日志或elasticsearch.log, 以此查找之前发生的连续事件。您需要查找:
● 高桶聚合
我发现,搜索在根据搜索大小或存储桶尺寸运行查询之前,临时分配了堆的某个端口,我觉得这太傻了,就设置 了10,000,000。我的运维团队为此很挠头。
● 非优化映射
感到愚蠢的第二个原因是,我认为搜索时如果利用分层报告会比扁平化数据更好(事实并非如此)。
● 请求量/速度:通常是批处理或异步查询
3.3.资源不够,扩容
如果您已不止一次启动熔断机制,或者您觉得这个问题将一直存在(例如,始终达到85%,则应该考虑扩展资源了),您就需要仔细查看JVM内存压力,将其作为您的长期堆指标。 您可以在Elasticsearch Service > Deployment中进行检查。
或者您可以根据 _nodes/stats进行计算
GET /_nodes/stats?filter_path=nodes.*.jvm.mem.pools.old {"nodes": { "node_id": { "jvm": { "mem": { "pools": { "old": { "max_in_bytes": 532676608, "peak_max_in_bytes": 532676608, "peak_used_in_bytes": 104465408, "used_in_bytes": 104465408 }}}}}}}
这里
JVM Memory Pressure = used_in_bytes / max_in_bytes
可能会出现的是情况是:elasticsearch.log中的垃圾收集器(gc)事件发生的频率很高,而且持续时间长
[timestamp_short_interval_from_last][INFO ][o.e.m.j.JvmGcMonitorService] [node_id] [gc][number] overhead, spent [21s] collecting in the last [40s]
如果您确认了这一情况,那么您需要考虑一下如何扩展您的集群,或者如何减少对集群的需求。你应该调查并考虑的方面包括:
● 增加堆资源(堆/节点、节点数量)
● 减少shards(删除不必要的或旧的数据,利用ILM将数据放入热/冷存储中,之后您就可以缩减数据,关闭那些即使丢失了您也不在乎的数据副本)