【生产环境】老板让我清理ES数据,我竟然把它清理爆了,大坑啊

5,544 阅读5分钟

  大家好,我是Coder哥, 最近线上客户反馈,有部分数据没产生,作为一个打工人那我必须是快速响应排查了一下,后来定位到是ES磁盘达到阈值,索引变为read only模式了,就是只能读不能写了,我说等我几分钟,我把ES磁盘清理一下(不扩容的原因是,历史数据没啥用,主要是节约成本),然后就快速的制定如下方案:

  1. 找到只读索引。
  2. 清除索引半年之前的数据。
  3. 通知用户。
  4. 完美。

如上,第一次跟领导有一样的想法,君子所见略同啊。

ES清理.png

经过上面分析后我就开工了,最后经历了各种坑,总结如下问题:

  1. ES索引为啥突然变成了read only状态了?
  2. 清理数据清理到一半的时候磁盘爆了ES又变成只读了?
  3. ES集群磁盘分布不均是为什么?

ES索引为啥突然变成了read only状态了?

ES正在使用,突然个别索引变为只读了,插入时候报错如下:

报错:Elasticsearch exception [type=cluster_block_exception, reason=blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];]

这个是因为ES的默认磁盘使用机制

原因为:es 的默认三个阈值是 85% 和 90 %,85%

当为85%时:Elasticsearch不会将碎片分配给磁盘使用率超过85%的节点( cluster.routing.allocation.disk.watermark.low: "85%")

当为90%时:Elasticsearch尝试重新分配给磁盘低于使用率90%的节点(cluster.routing.allocation.disk.watermark.high: "90%")

当为85%时:Elasticsearch执行只读模块(cluster.routing.allocation.disk.watermark.flood_stage: "85%")

基于以上机制,来看一下我们线上的数据:

todocoer:~# curl -X GET -u todocoder:todocoder http://127.0.0.1:9200/_cat/allocation?v
shards disk.indices disk.used disk.avail disk.total disk.percent host      ip        node
    73        757gb   811.8gb    172.2gb    984.1gb           82 xxx1 		xxx1 			node1
    73      748.6gb   803.4gb    180.7gb    984.1gb           81 xxx2 		xxx2			node2
     6      879.9gb   932.3gb     51.7gb    984.1gb           94 xxx3 		xxx3			node3
参数解释
shards分片个数
disk.used已用磁盘大小
disk.indices索引所占磁盘大小
disk.avail可以使用磁盘大小
disk.total磁盘总容量
disk.percent磁盘使用百分比
node节点名称

从磁盘情况可以看出节点node3 使用率已经达到94%了,也就是说已经超过85%了,所以这个上面的索引应该都会变成只读的。

我们看一下都哪个索引分配到这个节点上了

todocoer:~# curl -X GET -u todocoder:todocoder http://127.0.0.1:9200/_cat/shards?v | grep "node3"
index    shard prirep state        docs   	store 	ip        node
style    1 			p 		STARTED 		113431552 219.2gb 10.0.0.3 	node3
style    2 			r 		STARTED 		112811799 219.3gb 10.0.0.3 	node3
style    3 			r 		STARTED 		112789097 219.1gb 10.0.0.3 	node3
style    4 			p 		STARTED 		113420875 219.2gb 10.0.0.3 	node3

可以看到索引style已经占了94%了,所以ES把这个索引置为 read_only_allow_delete: true 如下:

todocoer:~# curl -X GET -u todocoder:todocoder http://127.0.0.1:9200/style/_settings?pretty
{
  "style" : {
    "settings" : {
      "index" : {
        "search" : {
          ...
        },
        "refresh_interval" : "5s",
        "indexing" : {
          ...
        },
        "number_of_shards" : "5",
        "blocks" : {
          "read_only_allow_delete" : "true"
        },
        ...
        "number_of_replicas" : "1",
      }
    }
  }
}

至此我们知道了索引不能写入了的原因了,那么接下来我们怎么处理呢?由于领导不让扩容,所以只能在当前容量下清除部分数据,那么执行删除操作之前先把 read_only_allow_delete置为false

todocoer:~# curl -X PUT -u todocoder:todocoder http://127.0.0.1:9200/style/_settings -H "Content-Type:application/json" -d '{"index.blocks.read_only_allow_delete":false}'

然后再执行删除操作,删除字段 insert_time <= 2022-06-10 23:59:59的数据,如下:

todocoer:~# curl -X POST -u todocoder:todocoder http://127.0.0.1:9200/style/_delete_by_query -H "accept:application/json"  -H "Content-Type:application/json" -d '{"query":{"bool":{"must":[{"range":{"insert_time":{"lte":"2022-06-10 23:59:59"}}}]}}}'
...
// 过了5分钟左右突然又出现下面的错误了
报错:Elasticsearch exception [type=cluster_block_exception, reason=blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];]

删除了5分钟,又只读了???那么就引出了上面的问题2: 清理数据清理到一半的时候ES又变成只读了

清理数据清理到一半的时候ES又变成只读了

  这个时候看了一下索引,占用的磁盘变大了,于是网上恶补了一下ES删除逻辑:

ES删除操作是软删除,原理如下:

  ES每个segment中维护了一个.del文件。ES删除文档的时候,仅在.del文件中将文档标记为已删除,并未将磁盘上的数据进行清理。ES执行segment merge 时,挑选segment进行合并生成新的segment,此时标记为删除的文档并不进行处理,因此磁盘空间进行了释放。默认的情况下ES选择部分segment进行合并,因而只有所有的segment都进行了处理之后磁盘空间完全释放,中间会有冗余数据的存在,以保证正常切换。

shard 和 segment的区别

Shard 是数据分片,比如一个索引有10G的内容,分成5shard, 放在不同的节点,这个数量是number_of_shards 在创建索引的时候指定的,后面无法更改。

Segment : 每个shard包含多个segment, 每个segment都是一个倒排索引,查询时会汇总所有segment的数据返回。

  那这样不行啊,得想个办法处理一下这个问题,既然他会自动的变为只读,那我把阈值调高点是不是就不会自动触发了,介于当前用了94%了,那我把阈值调flood_stage到97%,同时调了下indices.recovery.max_bytes_per_sec这个参数(但是不要调太高,比较吃资源),增加合并时候的吞吐量如下:

todocoer:~# curl -X PUT -u todocoder:todocoder http://127.0.0.1:9200/_cluster/settings -H "Content-Type:application/json" -d '{"transient":{"cluster.routing.allocation.disk.watermark.flood_stage":"97%","indices.recovery.max_bytes_per_sec":"100mb"}}'

执行完后,继续上面的步骤,修改read only, 清除索引,最后手动触发合并操作:

todocoer:~# curl -X POST -u todocoder:todocoder http://127.0.0.1:9200/style/_forcemerge?only_expunge_deletes=true&max_num_segments=1

only_expunge_deletes 该优化操作是否只清空打有删除标签的索引记录。在Lucence中,在执行删除操作时,不会直接删除segment中的记录,而是对该记录打上delete标签。当多个segment进行合并操作时,就会生成一个新的segment,而该新的segment中不再包含删除的记录。这个参数允许只对哪些包含删除记录的segment进行优化操作。

max_num_segments

指定segment的数量

执行完上面步骤,就等待系统合并完成。最后记得把flood_stage还原。

执行完后,发现整个过程比较慢还可能会影响用户查询速度,由于我们数据的特殊性,即使丢失也不太影响系统,所以我这边直接进行副本缩容:

todocoer:~# curl -X PUT -u todocoder:todocoder http://127.0.0.1:9200/style/_settings -H "Content-Type:application/json" -d '{"index":{"number_of_replicas":0}}'

number_of_replicas: 0, 意思就一份数据,不要副本。这个命令生效很快,几分钟就生效了,容量一下就多了1T。。

ES集群磁盘分布不均是为什么?

  以上是ES的清除过程,从这次清除ES发现了一些问题,某些索引的shard如果比较小的话,会造成数据分布不均,比如我们的索引 style,一年 1T多点的数据,number_of_shards值是5, 加上副本的话一共是2T数据分了10份,一份219G,那么一个节点大小为1000G的话,分4份就超过85%了,导致索引变成只读,但是这个时候其他的节点还有空间,造成了数据不均匀的情况,所以在创建索引的时候需要预估一下索引容量,分配合理的number_of_shards值。

最后

如果公司有条件,建议先扩容再清除,不然投入的时间成本也是挺高的,其实正常的清除过程是:

  1. 先扩容。
  2. 找到只读索引。
  3. 清除索引的数据。
  4. 通知用户。
  5. 完美。