Elasticsearch系统常见问题
本文档主要目的:
- 用来积累用户常见的问题。
- 文档中的操作可通过 Kibana 或在chrome浏览器中安装sense插件完成。用户也可以通过curl的方式自行实现。
如何查看Es安装了哪些插件
可以使用下面这个API,会列出每个节点安装的插件列表。
GET /_cat/plugins
线程池队列满导致错误
在这种场景下ES抛出的异常是
rejected execution of org.elasticsearch.transport.TransportService$4@c8998f4 on EsThreadPoolExecutor[bulk, queue capacity = 50, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@553aee29 [Running, pool size = 4, active threads = 4, queued tasks = 50, completed tasks = 0]]
ES内部有很多线程池,比如index,search,bulk是我们能够看到的3个典型的线程池,如果系统的压力特别大,后台线程处理不过来的时候,用户发起的任务会在线程池的队列里堆积,如果达到队列的上限就会抛出对应的异常,遇到这种错误需要做以下两步:
- 检查系统的CPU和IO的利用情况,如果系统的IO和CPU的利用率比较高,这说明系统遇到资源瓶颈了,已经不能通过优化系统的参数来避免这种错误发生了,云上的用户可以在ES的控制台查看ES的CPU利用率,也可以通过ES自带的命令来查看,通过下面这个命令可以看到ES各个节点的CPU利用率以及负载: GET /_cat/nodes?v
-
如果资源没有问题,那么检查当先线程池的配置,比如上面这个错误就需要检查bulk的线程池的配置,在sense里执行以下命令: GET /_cluster/settings
结果如下
"thread_pool": { "bulk": { "type": "fixed", "size": "4", "queue_size": "50" } }
这个结果表示处理bulk任务的线程池有4个执行线程,队列数为50. 根据我们的经验看,这个值还是比较小的,所以可以直接用以下操作处理:
ES 5.5.0 版本:
PUT /_cluster/settings
{
"persistent": {
"thread_pool.bulk.size": 32,
"thread_pool.bulk.queue_size": 300
}
}
ES 6.5.3+ 集群:
PUT /_cluster/settings
{
"persistent": {
"thread_pool.write.size": 32,
"thread_pool.write.queue_size": 300
}
}
Too Many Open Files的错误
在es的日志中如果出现这个错误,一般都是打开的文件太多了,ES 建议文件句柄的限制至少为65536个,用户可以通过修改 /etc/security/limits.conf来修改,或者用ulimit这个命令来修改。 es里每个shard都是一个单独的lucene index writer,每个shard由多个segment组成,每个segment有多个文件,所以打开的文件的数目= shard数目 segment数目 每个segment包含的文件数量,所以我们建议一个物理机节点上shard的数目在1000个左右,不建议有太多的shard。另外lucene使用compound file格式也能有效的减少每个segment里的文件的数量。
Es 中一个分片一般设置多大
ES 的每个分片(shard)都是lucene的一个index,而lucene的一个index只能存储20亿个文档,所以一个分片也只能最多存储20亿个文档。 另外,我们也建议一个分片的大小在10G-50G之间,太大的话查询时会比较慢,另外在做副本修复的时,耗时比较多;分片太小的话,会导致一个索引的分片数目很多,查询时带来的 fanout 太大。
当集群为red或者yellow的时候怎么办
集群为RED表示集群中有primary shard没有分配,yellow表示有replica没有分配,我们建议你用下面这个API来看shard为什么没有被分配到某个节点上。 GET _cluster/allocation/explain
根据我们的使用经验,有以下几种情况导致shard没有被分配:
- 没有节点上有存储空间能够放下这个shard。
- 如果shard是replica,那么可能是primary shard未分配或者处于initializing状态。
分片长时间处于未分配状态 ES内部会对一个unassigned 分片尝试5次进行分配,失败后不再尝试进行分配,这时候需要调用进行手动控制集群处理 unassigned 分片:
POST /_cluster/reroute?retry_failed=true
如何cancel掉慢查询
用户发送一个查询可能导致一个集群非常慢,CPU利用率非常高,所以用户有的时候想把占用资源非常多的查询cancel 掉。在ES 5.0之后es提供了cancel查询的命令。 es内部把所有的执行任务都封装成了task,可以通过task api来查看一个节点在执行的task任务列表,也可以使用task api来取消task。比如我们要查询所有在执行search类型的task,可以使用如下API: GET /_tasks?actions=*search
取消所有在执行的search任务:
POST _tasks/_cancel?actions=*search
更多的使用方式可以参考官方网站的介绍。
PageCache 在查询中的作用很大
我们建议如果条件可以的话应该给ES留尽量多的pagecache,这能极大的优化我们的查询速度,如果pagecache不足够多,那么ES每次查询【fetch 文档,拿posting list】都会读取磁盘,此时系统就会变慢。 用户可以使用iostat 来查看一下系统的IO 信息,也可在 GET _nodes/state 返回的信息里搜索 "io_stats"查看。如果iops比较高的话,说明系统的io比较高了,可能就是pagecache小的原因。
禁用权限验证
有的时候业务系统原有的ES服务没有权限验证,但是云上的ES服务是有权限验证的,当业务系统迁移的时候不希望改代码,那么可以先把权限验证关掉,这样就能平滑迁移了。操作的方式是:
PUT /_cluster/settings
{
"persistent": {
"simpleauth.enable":false
}
}
支持的Client的类型
目前我们云上的产品只支持基于http 的restful api,不支持基于tcp的transport client这种api。 这个设置主要原因是transport client跟集群运行的版本深度绑定,当集群升级的时候需要前端业务也跟着升级才可以。
Es是否支持Spark和Hadoop来写入或者读取数据
支持,需要到es官方网站下载es-hadoop包放到spark或者hadoop中就可以用spark或者hadoop读写es了。
JVM FULL GC 的几种情形
Scroll 导致FullGC
一些用户使用scroll做分页查询或者用scroll导出数据的时候,经常把scroll的超时时间设置的比较长,比如设置为1天,在这种情况下es后端会为这个scroll一直保存对应的search context,每个search context都对应了lucene的searcher,此时searcher一直不释放导致lucene merge完的文件也不删除,一些leafreader, fst等都长期在JVM 里导致最终随着search context越来越多导致了FullGC。用户可以使用以下2个API 来查看和清除这些Context。
GET /_nodes/stats/indices/search
DELETE /_search/scroll/_all
查询导致FullGC
用户在查询时将结果集的from+size设置的太大,比如size=Integer.MAX_VALUE导致的,目前ES会根据设置的这个from+size开辟一个priority queue,当并发量大时,内存会分配不过来这么多非常大的queue,导致FULL GC,甚至OOM。
aggregation导致FullGC
用户在执行类似terms agg时,如果不同的值非常多,最终会导致产生很多bucket,比如几千万个bucket,这些bucket也会在内存里,最终导致fullgc。
如何提升导入性能
减少副本数,增加refresh间隔
PUT /index_name/_settings
{
"index.number_of_replicas": 0,
"index.refresh_interval": "10s"
}
ES 的多副本机制在写入时会向多个副本都发送原始的json文档,然后在多个副本上分别进行分词,建立索引等操作。由于导入是CPU密集型操作,所以把replica数目改成0,可以减少CPU使用率,当导入完毕后,把replica数目改回,这样就是直接拷贝物理文件了,速度会比较快。
refresh interal是用来控制多久把内存里的数据刷出segment的,es会对刷出的segment进行merge,如果merge不过来es会阻止写入。所以把refresh interval调大,也可以把刷出的segment变大,降低merge的频率,提升导入性能。 增大index的导入速度限制
PUT /_cluster/settings
{
"persistent" : {
"indices.store.throttle.max_bytes_per_sec" : "200mb"
}
}
ES 在写入数据的时候会有速度的限制,防止占用过多的磁盘IO,如果集群的导入比较大而查询比较少,那么可以把这个速度限制调大。
集群配置问题
- 需要使用 oracle JDK 1.8 以上版本。
-
设置最大文件数:
修改 /etc/security/limits.conf : * soft nofile 65536 * hard nofile 65536
-
增加 mmap counts : 修改 /etc/sysctl.conf :
vm.max_map_count=262144 然后执行: sysctl -p
集群重启问题
在一些情况下(比如修改配置文件),我们希望重启集群,重启集群可以是一台台的重启,也可以是整个集群重启,重启Es的时候,可能会引起数据的重分布,下面就这两种情况分别介绍如何重启服务。
整个集群重启
-
把整个集群设置为只读状态
PUT /_cluster/settings { "persistent": { "cluster.blocks.read_only":true } } ```
-
把节点内存的数据全部flush到硬盘上
POST /_flush/synced
- 把所有的es节点重启
-
当集群green之后,把集群修改为可写入状态
PUT /_cluster/settings { "persistent": { "cluster.blocks.read_only":false } }
一台台重启
这种方式重启服务不会中断,适用于线上服务。
- 禁止分片分配,这样我们关闭一台Es服务的时候,分片不会重分布。
PUT /_cluster/settings { "transient" : { "cluster.routing.allocation.enable" : "none" } }
- 关闭单个节点,修改配置或者替换jar包,启动节点
-
开启分片重分布
PUT /_cluster/settings { "transient" : { "cluster.routing.allocation.enable" : "all" } } ```
- 等待集群green后,重复执行1-3 步,直到所有的节点都修改完配置为止。
禁用_field_names
_field_names
字段是Elasticsearch的内部的一个元数据字段。该字段会索引文档内的每个字段的名字(除了字段值为null的字段名字);这个字段存在的意义主要是执行Elasticsarech exists
query,Elasticsearch对该字段只做了索引处理,没有存储该字段,6.3版本以后该字段只会索引没有禁掉doc_value和norms的字段,建议业务不使用exists
查询的情况下disable该字段,能够少量的减少倒排表的占用的存储空间,可能会适当增强pagecache的利用
PUT index
{
"mappings": {
"_doc": {
"_field_names": {
"enabled": false
}
}
}
}
导入数据发现越来越慢的几种情形
导入数据中包含update
Es的update实际上是先读取数据然后更改后,再写入的,当写入的数据越来越多的时候,读取数据就会比较慢,写入也就逐渐变慢了。
控制index在节点上的数量
默认情况下,ES集群会尽可能将所有节点上的index和shard的数量进行balance,但是一些特殊情况下,可能会造成某一个index的shard过多的集中在少量的节点上,这时候可以通过设置集群中每个节点存放index shard的个数:
PUT {index名字}/_settings
{
"index.routing.allocation.total_shards_per_node": 10
}
控制索引的分片数量和副本数量
不修改参数的情况下,一个index 一共有5个分片,2个副本(包括主分片),可以通过修改index的参数来控制:
PUT /{index名字}
{
"settings": {
"number_of_shards": 20,
"number_of_replicas": 2
}
}
number_of_shards: 分片个数,创建完index后不可修改,需在创建的时候指定
number_of_replicas: 副本个数,不包括主分片
当集群处于恢复状态的时候,恢复速度可能会比较慢
当前正在恢复的索引分片可以通过
GET /_recovery?active_only=true
查看,默认情况一个节点同时恢复的个数为4,包括2个作为source节点,2个作为target节点,当分片个数非常多的时候可能会恢复的很慢,恢复的时候默认是有限速的最大40mb,这时候可以通过设置集群参数:
curl -XPUT "host:port/_cluster/settings" -d'
{
"transient": {
"cluster.routing.allocation.node_concurrent_recoveries": 8,
"indices.recovery.max_bytes_per_sec": "120mb"
}
}'
indices.recovery.max_bytes_per_sec: 节点恢复的最大带宽,这个设置应该小于当前网络带宽,避免影响其他网络服务
cluster.routing.allocation.node_concurrent_recoveries: 节点作为source node或target node同时恢复的最大个数
磁盘满了之后如何恢复
当Es的DataNode的磁盘使用率达到一定的阈值(95%)之后,Es会阻止继续写入,Es会在所有的Index上加一个block,当用户继续写入的时候会收到以下错误
cluster_block_exception [FORBIDDEN/12/index read-only / allow delete (api)];
此时用户必须释放磁盘空间才能解决问题,释放磁盘空间有2种办法:
- 删除不用的Index
- 降低Index的副本的数目,比如把replica数目从2降到1.
当释放完毕磁盘空间之后,Es并不会自动把block去掉,此时用户仍然无法写入数据,需要执行以下命令:
curl -XPUT "host:port/_all/_settings" -d '
{
"index.blocks.read_only_allow_delete": null
}'