参考:https://www.phpmianshi.com/?id=242

背景

最近发现es占用的磁盘存储大小一直在增加,本来1mb的东西,隔了1-2天达到100mb多,如下

分析   

elasticsearch是建立在Apache Lucene 基础上的实时分布式搜索引擎,Lucene为了提高搜索的实时性,采用不可再修改(immutable)方式将文档存储在一个个segment中。也就是说,一个segment在写入到存储系统之后,将不可以再修改。那么Lucene是如何从一个segment中删除一个被索引的文档呢?简单的讲,当用户发出命令删除一个被索引的文档#ABC时,该文档并不会被马上从相应的存储它的segment中删除掉,而是通过一个特殊的文件来标记该文档已被删除。当用户再次搜索到#ABC时,Elasticsearch在segment中仍能找到#ABC,但由于#ABC文档已经被标记为删除,所以Lucene会从发回给用户的搜索结果中剔除#ABC,所以给用户感觉的是#ABC已经被删除了。

 

我们已经知道在elasticsearch中每个shard每隔1秒都会refresh一次,每次refresh都会生成一个新的segment,按照这个速度过不了多久segment的数量就会爆炸,所以存在太多的segment是一个大问题,因为每一个segment都会占用文件句柄,内存资源,cpu资源,更加重要的是每一个搜索请求都必须访问每一个segment,这就意味着存在的segment越多,搜索请求就会变的更慢。

那么elaticsearch是如何解决这个问题呢?

 

根据ES的写入原理分析,默认每秒从memory buffer里面搬运数据到filesystem  cache,生产一个segments段,由后台程序定期分梯队进行合并,后台线程根据Lucene的合并规则定期进行segment merging合并操作,一般不需要用户担心或者采取任何行动。被删除的文档在segment合并时,才会被真正删除掉。在此之前,它仍然会占用着JVM heap和操作系统的文件cache等资源。在某些情况下,我们需要强制Elasticsearch进行segment merging,已释放其占用的大量系统资源。如果不加控制,合并一个大的segment会消耗比较多的io和cpu资源,同时也会搜索性能造成影响,所以默认情况下es已经对合并线程做了资源限额以便于它不会搜索性能造成太大影响。

限制如下:(不限制可以设置为 none)

 

PUT /_cluster/settings
{
   "persistent" : {
       "indices.store.throttle.max_bytes_per_sec" : "100mb"
   }
}

 

结合具体的业务,我们每次全量同步就会产生大量的segments,并标识为deleted,所以磁盘占用越来越大。因为ES还有定期合并功能,所以过一段时间还会再减少下来。

查看索引segments状态

 

GET _cat/segments/live_v4?v

图片.png

合并参数详解

参数名称

说明

max_num_segments

设置需要合并的段数。如果需要整个索引完全合并,则将该值设置为1。默认情况下会检查合并操作是否需要执行,如果需要才执行,否则就不执行。

only_expunge_deletes

合并过程是否仅操作哪些包含了被删除文档的段,将这些段中未标识为删除的内容放到一个新创建的段中,然后将这些包含了被删除文档的段全部删除。

注:这不会覆盖index.merge.policy.expunge_deletes_allowed阈值。

flush

是否应在强制合并后执行刷新,默认为true。

 

POST /live_v4/_forcemerge?max_num_segments=1&only_expunge_deletes=true

_forcemerge 命令可强制进行segment合并,将要合并的segment读取出来,再写入到新的segment,然后删除所有标记为删除的文档。Segment merging要消耗CPU,以及大量的I/O资源,所以一定要在你的ElasticSearch集群处于维护窗口期间,并且有足够的I/O空间的(如:SSD)的条件下进行;否则很可能造成集群崩溃和数据丢失。

 

为了保证合并顺利进行,在此期间暂停了所有对其进行的写操作,仅有少量的读操作。加大磁盘,或者限制索引合并的线程数量,减小每次合并的segment数量。

这里需要注意: expunge操作是一种不得已而为之的操作,即在Elasticsearch不能有效自动清除删除文件的情况下才执行该操作。同时建议在此操作期间,最好停止对集群的所有读/写操作,并暂停止shard的自动分配 (cluster.routing.allocation.enable= none),以防有节点被踢出后shard自动分配造成的数据丢失。

 

下面两个设置可以用于控制清除时的处理速度,其中给出值是默认值,可以根据需求进行调整,具体请参见Merge。此外, 还可以临时将所有索引的replica设置为0,这样只用针对Primary进行expunge,以减小I/O压力。

 

PUT /{index}/_settings
{
    "settings": {
        "index.merge.policy.expunge_deletes_allowed": "10",
        "index.merge.policy.max_merge_at_once_explicit" : "30"
    }
}

 

通过/_cat/indices/ api查看所有index的段情况和当前正在进行merge的文档数。

 

GET /_cat/indices/?s=segmentsCount:desc&v&h=index,segmentsCount,segmentsMemory,memoryTotal,mergesCurrent,mergesCurrentDocs,storeSize,p,r

查看各个节点forceMerge的线程数

 

GET _cat/thread_pool/force_merge?v&s=name

查看forceMerge任务详情

 

GET _tasks?detailed=true&actions=*forcemerge

查看某个index的forceMerge情况

 

GET /_cat/segments/test-000032?v&s=prirep,shard

 

参考

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/indices-forcemerge.html

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐