百度ElasticsearchBES

    Elasticsearch系统常见问题

    本文档主要目的:

    1. 用来积累用户常见的问题。
    2. 文档中的操作都是使用sense来完成,需要在chrome浏览器中安装sense插件。

    如何查看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
    }'
    上一篇
    常见问题总览
    下一篇
    Spark访问Es常见问题