ScyllaDB “Critical disk utilization: rejected write mutation“ 报错排查与磁盘防耗尽机制深度解析
2026/9/15 18:23:51 网站建设 项目流程

ScyllaDB "Critical disk utilization: rejected write mutation" 报错排查与磁盘防耗尽机制深度解析

【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb

导读

本文聚焦 ScyllaDB 集群在磁盘空间逼近耗尽时抛出的Critical disk utilization: rejected write mutation错误,从 cqlsh 报错现象入手,结合仓库源码(replica/exceptions.hh、replica/database.cc)剖析防耗尽(out-of-space prevention)机制的触发与判定逻辑,并给出nodetool statusdf -h等标准验证手段与横向扩容的解决方案。读完本文,你将能准确解读该错误码、定位触发条件、评估critical_disk_utilization_level配置的影响,并理解 ScyllaDB 在"暂停写入但不阻断迁移"这一设计取舍背后的实现原理。

问题现象:写操作批量失败,报 WriteFailure 错误

在 ScyllaDB 集群中,当某个节点的磁盘利用率达到临界阈值后,来自 cqlsh 的SELECT/INSERT/UPDATE/DELETE操作会失败并返回类似如下的错误:

WriteFailure: Error from server: code=1500 [Replica(s) failed to execute write] message="Critical disk utilization: rejected write mutation" info={'consistency': 'QUORUM', 'required_responses': 2, 'received_responses': 1, 'failures': 2}

逐段拆解这条错误信息:

  • code=1500:CQL 协议中的WriteFailure错误码,表示协调节点(coordinator)收到副本节点(replica)写入失败的通知;
  • message="Critical disk utilization: rejected write mutation":副本节点返回的具体失败原因——写入的 mutation 因磁盘利用率达到临界值而被拒绝;
  • consistency: QUORUMrequired_responses: 2received_responses: 1failures: 2:本次写入要求的 QUORUM(2 个副本响应)未能满足,实际只收到 1 个响应,且有 2 个副本节点失败。

从源码看,这条消息并非由协调节点拼装,而是由副本侧抛出的异常序列化而来。仓库中的 replica/exceptions.hh 定义了critical_disk_utilization_exception,其构造函数将失败动作拼接为统一的错误文案:

class critical_disk_utilization_exception final: public replica_exception { critical_disk_utilization_exception(std::string_view failed_action) noexcept : _message(seastar::format("Critical disk utilization: {}", failed_action)) { } };

failed_action会随场景不同而变化,例如写路径上是"rejected write mutation"(replica/database.cc),计数器更新路径上是"rejected counter update mutation"(replica/database.cc),流式传输加载场景下还会出现"rejected streamed mutation fragment"(见 test/cluster/storage/test_out_of_space_prevention.py)。因此,日志中Critical disk utilization: ...冒号后面的部分,往往能直接告诉你被拒的是哪类数据操作。

问题根因:扩容受阻导致磁盘告急,防耗尽机制被激活

出现上述错误的根本原因通常是:集群横向扩容失败或被延迟,数据无法及时疏散到新节点,磁盘空间随之降到临界水位以下,ScyllaDB 内置的"防止节点磁盘耗尽"(prevention of running out of space)机制被自动激活。

该机制生效后的行为是:

  • 用户写入活动被暂停:正常用户写入(以及 counter 更新等)会被拒绝,从而阻止磁盘继续增长;
  • 节点仍保留迁移能力:节点依然可以向外迁移数据(例如参与流式传输、tablet 迁移等),这样集群才有机会通过数据疏散把磁盘利用率降下来。

也就是说,这是一个"宁可拒绝新写入、也要保住节点不因磁盘写满而崩溃"的防御性降级策略。相关实现位于 replica/database.cc:

future<> database::set_in_critical_disk_utilization_mode(sharded<database>& sharded_db, bool enabled) { return sharded_db.invoke_on_all([enabled] (replica::database& db) { db._critical_disk_utilization_mode_count += enabled ? 1 : -1; ... }); } bool database::is_in_critical_disk_utilization_mode() const { if (_critical_disk_utilization_mode_count) [[unlikely]] { return true; } return false; }

这里使用了一个引用计数_critical_disk_utilization_mode_count(定义于 replica/database.hh):多个订阅源都可以请求进入/退出临界模式,只有计数归零时才真正退出,避免某个订阅源提前解除保护导致状态抖动。

如何验证:确认节点存活与磁盘利用率超阈值

按以下两步确认问题是否确由磁盘利用率触达临界阈值导致:

  1. 确认节点在线
nodetool status

该命令返回集群节点列表及其状态(UN表示 Up/Normal)。Critical disk utilization拒绝写入通常发生在节点仍然存活(否则你会看到其他类型的不可用错误),这一步用于排除节点宕机导致的失败。

  1. 检查磁盘利用率是否超过阈值
df -h /var/lib/scylla/

将输出中的已用百分比与critical_disk_utilization_level配置(默认0.98,即 98%)比较。若利用率大于等于该阈值,说明防耗尽机制已激活。

该阈值在源码中的定义位于 db/config.cc:

critical_disk_utilization_level(this, "critical_disk_utilization_level", liveness::LiveUpdate, value_status::Used, 0.98, "Disk utilization level above which mechanisms preventing a node getting out of space are activated")

要点:

  • 默认值 0.98:磁盘利用率达到 98% 时激活防耗尽机制;
  • liveness::LiveUpdate:该参数属于热更新配置,可以在运行中通过scylla.yamlscylla --update-config或配置 API 调整,无需重启节点;
  • 配置项同时作用于数据库的订阅逻辑:节点启动时,replica/database.cc 通过磁盘空间监视器订阅该阈值,一旦跨越阈值立即将全部分片切换到临界模式:
if (dsm && (this_shard_id() == 0)) { _out_of_space_subscription = dsm->subscribe(_cfg.critical_disk_utilization_level, [this] (auto threshold_reached) { return set_in_critical_disk_utilization_mode(container(), bool(threshold_reached)); }); }

此外,磁盘空间监视器还有一组配套的轮询参数(db/config.cc),用于控制检测频率:

  • disk_space_monitor_polling_interval_threshold:默认0.9,磁盘利用率达到该值后进入高频轮询;
  • disk_space_monitor_normal_polling_interval_in_seconds:默认10秒,低水位时的普通轮询间隔;
  • disk_space_monitor_high_polling_interval_in_seconds:默认1秒,达到轮询阈值后的高频轮询间隔。

也就是说,磁盘利用率在 90%–98% 之间时,监视器会以 1 秒为周期高频检测,确保一旦突破 98% 能迅速激活保护,避免在检测间隙里磁盘被写满。

解决方案:扩容集群,将磁盘利用率拉回阈值以下

官方推荐的做法是横向扩容(scale out)

  1. 向集群中加入新节点;
  2. 新节点应加入与达到临界状态的节点相同的机架(rack),从而让数据迁移优先发生在这条路径上,把问题节点的磁盘利用率降下来;
  3. 当磁盘利用率回落到critical_disk_utilization_level阈值以下后,防耗尽机制自动解除,用户写入恢复。

之所以强调"相同机架",是因为 ScyllaDB 在机架感知(rack-aware)复制与拓扑迁移中,同机架节点之间的数据再平衡路径更直接;把新节点加在同机架可以更高效地分担临界节点的数据与写入负载,让保护机制尽快关闭。

扩容过程中你可以持续观察两个信号来确认恢复进度:

  • df -h /var/lib/scylla/的利用率是否持续下降;
  • 通过 Prometheus 指标scylla_storage_proxy_write_...相关计数器观察写拒绝次数是否停止增长——仓库中提供了专门的统计项total_writes_rejected_due_to_out_of_space_prevention(定义见 replica/database.hh,在写路径被拒时递增,见 replica/database.cc),可以借此量化保护机制开启期间被拒的写入总量。

源码视角:防耗尽机制如何拦截写入

理解"只拒写入、不阻迁移"的设计,需要回到写路径的判定点。在 replica/database.cc 中,普通写入(mutation)到达副本前会先做一次检查:

if (is_in_critical_disk_utilization_mode() && cf.is_eligible_to_write_rejection_on_critical_disk_utilization()) { co_await coroutine::return_exception(replica::critical_disk_utilization_exception{"rejected write mutation"}); }

其中有两个关键条件:

  1. is_in_critical_disk_utilization_mode():节点整体是否处于临界模式(由上述磁盘监视器订阅置位);
  2. cf.is_eligible_to_write_rejection_on_critical_disk_utilization()当前列族(表)是否允许在临界模式下拒绝写入。该开关由 replica/database.hh 提供读写接口,默认值为false(见 replica/database.hh),并由 replica/database.cc 按表显式设置。这一层"按表开关"的设计,使得系统表等关键内部表不会被误伤——即使在临界模式下,系统表与未标记为可拒绝的表依然可以写入,保证集群元数据与运维操作不被中断。

counter 更新路径(replica/database.cc)与普通写路径走的是同一套判定逻辑,只是拒绝文案变为"rejected counter update mutation"

测试佐证:行为在仓库中被系统验证

该机制并非纸上谈兵,仓库中的集成测试 test/cluster/storage/test_out_of_space_prevention.py 覆盖了多种临界磁盘场景:

  • test_user_writes_rejection(第 73 行):验证临界磁盘利用率下用户写入被拒绝;
  • test_critical_utilization_during_decommission(第 170 行):验证节点下线(decommission)期间磁盘达到临界状态时的行为;
  • test_autotoggle_reject_incoming_migrations(第 351 行):验证临界模式下拒绝迁移流量的自动切换;
  • test_load_and_stream_rejected_on_critical_disk(第 651 行):验证 load-and-stream 场景下流式片段被拒(错误文案为"Critical disk utilization: rejected streamed mutation fragment",第 778 行),并确认被拒后目标节点不落数据。

这些测试印证了文档中的两个关键论断:保护机制确实只拦截数据写入,而迁移/流式相关路径在临界状态下按场景分别处理,且节点始终保持在线可诊断。

小结

Critical disk utilization: rejected write mutation是 ScyllaDB 磁盘防耗尽机制在 98% 水位(默认critical_disk_utilization_level)被激活时的正常防御反应,本质是集群扩容受阻的"果"而非独立故障。排查时按nodetool statusdf -h /var/lib/scylla/两步确认节点在线且利用率超阈值即可定性;解决时向同机架扩容新节点,让数据疏散把利用率拉回阈值以下,保护机制便会自动解除、写入恢复。若在运维中频繁遇到该错误,除了及时扩容,还可结合disk_space_monitor_*轮询参数评估检测灵敏度,并通过total_writes_rejected_due_to_out_of_space_prevention指标量化影响范围。

【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询