华为云DWS磁盘使用率超90%后集群只读

一、故障现象

凌晨作业调度时段,ETL任务连续报错:

二、根因分析

DWS集群规则:单数据盘使用率 ≥ 90% 时,集群自动进入只读模式,default_transaction_read_only 被强制打开,所有 INSERT/UPDATE/DELETE/DDL 全部 cancel

实际排查发现 :大量中间临时表(dm_tmp_*mid_*dws_tmp_*)未清理,累计占用 2TB+,单表最大超过 180GB。凌晨ETL集中写入时,磁盘使用率突破90%阈值,触发集群只读。

三、排错过程

Step 1:确认集群状态

SQL 复制代码
SHOW transaction_read_only;

Step 2:扫描大表,定位"大块头"

单表大小查询:

SQL 复制代码
SELECT pg_size_pretty(pg_total_relation_size('tmp.tmp_test'));

全库大表扫描(>10GB):

SQL 复制代码
WITH all_table_size AS (
    SELECT
        n.nspname AS schema_name,
        c.relname AS table_name,
        c.reltuples::BIGINT AS estimate_rows,
        pg_total_relation_size(c.oid)/1024/1024/1024 AS total_bytes,
        pg_table_size(c.oid)/1024/1024/1024 AS table_only_bytes,
        pg_indexes_size(c.oid)/1024/1024/1024 AS index_bytes,
        pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size_pretty,
        pg_size_pretty(pg_table_size(c.oid)) AS table_size_pretty,
        pg_size_pretty(pg_indexes_size(c.oid)) AS index_size_pretty
    FROM pg_class c
    LEFT JOIN pg_namespace n ON c.relnamespace = n.oid
    WHERE c.relkind = 'r'
      AND n.nspname NOT IN ('pg_catalog','information_schema','pg_toast','cstore','pmk')
      AND n.nspname !~ '^pg_temp_'
)
SELECT *
FROM all_table_size
WHERE total_bytes > 10
ORDER BY total_bytes DESC;

Step 3:解除只读 + 清理

必须在事务内 执行,否则 SET transaction_read_only = off 会被集群级只读参数拦截。

方式一:显示事务关闭只读

SQL 复制代码
START TRANSACTION READWRITE;
DROP TABLE IF EXISTS dm.dm_tmp_xxx;
TRUNCATE TABLE dm.dm_tmp_yyy;
COMMIT;

方式二:设置事务只读关闭

SQL 复制代码
START TRANSACTION;
SET transaction_read_only = off;
DROP/TRUNCATE 目标表;
COMMIT;

Step 4:清理优先级

优先级清理对象动作风险P0临时表(tmp_、mid_、dm_tmp_)DROP TABLE无P1业务已废弃的备份表(_bak、_v1)DROP TABLE低P2大表历史分区DROP PARTITION中

清理目标:磁盘使用率降至 80% 以下,集群自动退出只读模式。

四、踩坑记录

坑1:临时表不会自动释放

CREATE TEMP TABLE(会话级临时表)才会随连接断开释放;

CREATE TABLE dm.dm_tmp_xxx AS ...物理表,永远不会自动删除。

命名带 tmp 不等于临时表。

坑2:TRUNCATE不立即释放空间

DWS列存下 TRUNCATE 释放空间依赖底层存储回收,存在延迟;

大量数据要彻底释放,必须用 DROP TABLE

坑3:VACUUM FULL会临时占更多空间

VACUUM FULL 执行期间需要 额外空间 ≈ 表大小 × (1 - 脏页率) ,最高磁盘使用率 > 70% 时必须串行清理(一张一张来),否则会再次打满磁盘。

坑4:单SQL可写满整盘

未设置 sql_use_spacelimit 时,单条SQL可无限制写入磁盘,是定时炸弹。

五、调优方案

作业结尾强制清理(最关键)

所有ETL作业模板末尾追加:

SQL 复制代码
-- 显式删除已知临时表
DROP TABLE IF EXISTS dm.dm_tmp_step1_{biz_date};
DROP TABLE IF EXISTS dm.dm_tmp_step2_{biz_date};

-- 模糊匹配清理(兜底)
DO $$
DECLARE r RECORD;
BEGIN
    FOR r IN
        SELECT n.nspname, c.relname
        FROM pg_class c
        JOIN pg_namespace n ON c.relnamespace = n.oid
        WHERE n.nspname = 'dm'
          AND c.relname ~ '^dm_tmp_'
          AND c.relname LIKE '%{biz_date}%'
    LOOP
        EXECUTE format('DROP TABLE IF EXISTS %I.%I', r.nspname, r.relname);
    END LOOP;
END $$;

使用真正的TEMP TABLE

中间结果只在当前会话使用时:

SQL 复制代码
-- 事务级临时表(COMMIT后自动释放,最严格)
CREATE TEMP TABLE tmp_step1 ON COMMIT DROP AS
SELECT ...;

兜底清理作业(每日巡检)

清理>7天未访问的疑似临时表:

SQL 复制代码
SELECT
    n.nspname || '.' || c.relname AS full_name,
    pg_size_pretty(pg_total_relation_size(c.oid)) AS size
FROM pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
JOIN pg_stat_user_tables s ON s.schemaname = n.nspname AND s.relname = c.relname
WHERE n.nspname NOT IN ('pg_catalog','information_schema','pg_toast','cstore','pmk')
  AND (c.relname ~ '^(tmp_|temp_|mid_|dm_tmp_|dws_tmp_)' OR c.relname ~ '(_bak|_old|_v1)$')
  AND GREATEST(s.last_analyze, s.last_autoanalyze,
               s.last_vacuum, s.last_autovacuum) < (CURRENT_TIMESTAMP - INTERVAL '7 days')
ORDER BY pg_total_relation_size(c.oid) DESC;

SQL空间管控参数

SQL 复制代码
-- 单SQL最大可用空间(建议=磁盘总容量×10%)
ALTER DATABASE dm SET sql_use_spacelimit TO '500GB';

-- 会话级临时文件限制
ALTER DATABASE dm SET temp_file_limit TO '100GB';

-- 紧急一次性写入可临时关闭
SET sql_use_spacelimit = 0;

VACUUM回收脏页

8.1.3+ 版本用智能运维(控制台 → 智能运维 → 添加 Vacuum 周期任务)。

低版本手动:

SQL 复制代码
VACUUM FULL ANALYZE dm.dm_pur_pe_unclear_amount;

注意 :会锁表,需低峰执行,且最高磁盘使用率 > 70% 时必须串行

六、SQL空间管控参数

重复一下,这块最重要

参数作用建议值sql_use_spacelimit单SQL最大可用空间磁盘总容量 × 10%temp_file_limit会话临时文件上限100GB

设置:

SQL 复制代码
ALTER DATABASE dm SET sql_use_spacelimit TO '500GB';
ALTER DATABASE dm SET temp_file_limit TO '100GB';

七、总结

核心问题:临时表生命周期失控,凌晨ETL集中写入打爆磁盘。

三板斧

  1. 排错 :显示事务 START TRANSACTION READWRITE + 删临时表,腾出空间。
  2. 踩坑 :临时表要 DROP 不用 TRUNCATE;命名带 tmp 不等于临时表;KILL 任务时清理语句不执行。
  3. 调优 :作业末尾强制清理 + 优先用 TEMP TABLE + 每日兜底作业 + 设置 sql_use_spacelimit

参考DWS磁盘空间满处理方案

相关推荐
zzzll11112 小时前
如何学习AI Agent?
java·人工智能·学习·程序员·大模型
码农胖大海5 小时前
AI 辅助学习的正确姿势和方法
人工智能·程序员·ai编程
AskHarries5 小时前
用 OpenClaw 辅助做短视频:素材整理、字幕、剪辑清单和发布文案
程序员
SimonKing6 小时前
FreeLLMAPI:24 家厂商、128 个模型、1 个 Key智能切换所有可用的模型
java·后端·程序员
爱勇宝18 小时前
办公资料反复修改、补传、交接混乱,我做了个桌面工具来解决这件事
前端·后端·程序员
庄周de蝴蝶19 小时前
梅开二度,这次拿下系统分析师
后端·程序员
Hilaku1 天前
为什么你的大文件断点续传,一遇到弱网就崩溃?
前端·javascript·程序员
开发者如是说1 天前
Vibe Code 了产品却无人下载?我写了个工具来帮你
人工智能·程序员·产品