bigywater.tech
Elasticsearch · 119

Disk Watermark 与只读索引

本机磁盘用量长期维持94%左右,真实验证disk watermark三个水位必须满足low≤high≤flood_stage的顺序约束,全新分片撞上high水位会直接unassigned,而已经started的分片水位下调后不会被驱逐只会加上read_only_allow_delete这个block,并真实验证这个block允许的delete指的是删除整个索引而非文档,跟名字直觉完全相反。

全新索引撞上high水位
真实直接UNASSIGNED,不是变只读
已STARTED的分片水位下调
真实不被驱逐,但自动加read_only_allow_delete
block真实效果
写入和文档删除都被拒,真正允许的是删整个索引

做的事:本机真实磁盘用量长期在94%左右,先直接把三个水位全部设到比94%更低,对一个全新索引测试

代码本机 df -h / 真实显示: 24Gi可用 / 460Gi总量 ≈ 94%已用 PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.disk.watermark.low":"88%", "cluster.routing.allocation.disk.watermark.high":"89%", "cluster.routing.allocation.disk.watermark.flood_stage":"90%" }} PUT /ro_test {"settings":{"number_of_shards":1,"number_of_replicas":0}}

验证:全新索引的主分片真实直接变成UNASSIGNED,比预想的"只读"更严重

实测 · 本机 Elasticsearch 8.15.0 执行输出
ro_test 0 p UNASSIGNED
集群健康: status=red

这个真实结果说明超过high水位的后果,对一个还没有任何副本存在于任何地方的全新分片来说,真实不是"变成只读"这么温和,而是真实直接拒绝分配——因为超过high水位的节点真实被ES判定为"不适合接收新分片",这跟本系列上一篇验证allocation.exclude时发现的"新分片没有既成事实保护"是同一类真实行为。

真实尝试只单独调高flood_stage来隔离测试,看是否能绕开high水位的限制

代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.disk.watermark.high":"98%", "cluster.routing.allocation.disk.watermark.flood_stage":"90%" }}

验证:真实直接被拒绝,ES强制要求low≤high≤flood_stage,三个水位真实不能互相独立设置

实测 · 本机执行输出
→ 400 illegal_argument_exception:
  "high disk watermark [98%] more than flood stage disk watermark [90%]"
这条真实报错揭示了一个容易被忽略的约束:flood_stage这个"最严重"的水位,真实永远不可能比high更低——ES在校验层面真实强制要求三个水位满足low ≤ high ≤ flood_stage这个顺序关系,想单独触发flood_stage而不碰到high,在真实配置层面根本做不到。这意味着要真实隔离观测"flood_stage导致只读"这个行为,不能像上一步那样对一个全新的分片测试(会先撞上更严重的high拒绝),必须换一个思路:让分片先在水位安全时正常启动,再把水位整体下调,才能真实只观测到flood_stage单独造成的效果。

真实换个思路:先在安全水位下让索引正常启动,再把三个水位一起下调到94%以下

代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.disk.watermark.low":"95%", "cluster.routing.allocation.disk.watermark.high":"96%", "cluster.routing.allocation.disk.watermark.flood_stage":"97%" }} 删除重建 ro_test,确认分片STARTED,真实写入1条文档成功(result:"created") PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.disk.watermark.low":"88%", "cluster.routing.allocation.disk.watermark.high":"89%", "cluster.routing.allocation.disk.watermark.flood_stage":"90%", "cluster.routing.allocation.disk.watermark.flood_stage.frozen":"90%" }} # 等待35秒,让磁盘监控周期性重新评估

验证:35秒后,已经STARTED的分片真实完全没被驱逐,但索引真实被自动加上了read_only_allow_delete这个block

实测 · 本机执行输出
ro_test 0 p STARTED   ← 真实分片位置完全没变

GET /ro_test/_settings
→ settings.index.blocks: {"read_only_allow_delete":"true"}   ← 真实自动加上的

这组真实结果跟本系列上一篇allocation.exclude的发现一脉相承:ES真实不会主动驱逐已经安置好的分片去制造数据不可用,水位下调后它选择的真实应对方式是给索引加上一个保护性的只读block,而不是把分片踢走——这跟全新分片撞上high水位时"直接拒绝分配"是完全不同的两种真实应对策略,取决于这个分片是否已经有"已经安全存在于某处"这个既成事实。

真实验证这个block的实际效果:先测写入,再测删除文档,最后测删除整个索引

代码POST /ro_test/_doc {"n":2} DELETE /ro_test/_doc/<已有文档id> DELETE /ro_test # 删除整个索引

验证:写入真实被拒绝;文档级删除真实也被同样拒绝,跟名字给人的直觉完全相反;真实能做的是删除整个索引

实测 · 本机执行输出
写入 → 429 cluster_block_exception:
  "index [ro_test] blocked by: [TOO_MANY_REQUESTS/12/disk usage exceeded
   flood-stage watermark, index has read-only-allow-delete block]"

删除文档 → 429 cluster_block_exception   ← 真实跟写入收到完全一样的拒绝

删除整个索引 DELETE /ro_test → {"acknowledged":true}   ← 真实这个才会成功
这是本文最真实也最反直觉的一个发现:read_only_allow_delete这个block名字容易让人以为"允许删除文档",但真实测试下来,无论是写入新文档还是删除一条已有文档,收到的真实都是同样的cluster_block_exception拒绝——这个block在文档级操作上真实跟纯粹的read_only没有区别。它真正允许的"delete",真实指的是删除整个索引这个动作本身,也就是DELETE /ro_test这种index级别的操作真实能正常执行。这个设计背后的真实逻辑是:磁盘已经严重不足的情况下,ES需要给运维留一条真实能够释放磁盘空间的退路——删掉整个不再需要的索引,而不是指望靠"删几条文档"来腾出空间,因为文档级删除在Lucene层面真实只是打一个删除标记,并不会立刻真实释放磁盘空间,只有真实删除整个索引的目录才能立刻拿回磁盘空间,所以这个block真实只放行了后者。

收尾:真实把水位设置恢复回项目里原本的安全值,清理测试索引

代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.disk.watermark.low":"98%", "cluster.routing.allocation.disk.watermark.high":"99%", "cluster.routing.allocation.disk.watermark.flood_stage":"99.5%", "cluster.routing.allocation.disk.watermark.flood_stage.frozen":null }} DELETE /ro_test

验证:集群真实恢复到green,4个主分片全部active,水位配置真实回到安全值

实测 · 本机执行输出
status: "green"
active_primary_shards: 4
active_shards: 4
unassigned_shards: 0

分步演示:Disk Watermark从UNASSIGNED到read_only_allow_delete的完整真实行为链

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

disk watermark从拒绝全新分片到给已有分片加只读block,read_only_allow_delete真正允许的是删索引不是删文档
这一步新验证的水位场景

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 cluster.routing.allocation.disk.watermark.* 设置
    • 本机真实的 df -h 磁盘用量(约94%)
    京ICP备2026067502号-1京公网安备11010502063487号