ClickHouse 运维 SQL 速查:system 表查跑批、内存、合并与磁盘一篇通

📝 摘要 :ClickHouse 线上出问题,答案几乎都在 system 库里------但官方文档按表罗列字段、不按问题组织,真出事了根本不知道该查哪张。本文按症状 把常用运维 SQL 串成一份速查:现在在跑什么(processes / merges / mutations)、内存去哪了(metrics / query_log)、磁盘被谁吃了(parts)、合并为什么不动(parts + OPTIMIZE)、数据删了空间为什么没释放。附一条几乎人人中招的实测结论:system.*_log 自己可能就占了一半以上磁盘。基于 ClickHouse 25.4。
ClickHouse 用久了都会遇到这么一天:

磁盘告警了,可你把业务表体积加起来,怎么算都对不上;

报表跑不出来,也不知道是卡在查询、卡在合并,还是卡在某个删了一半的 ALTER;

内存报警一直响,MemoryTracking 二十多个 G,你却说不清是谁吃的。

这些问题的答案全都在 system 库里 。麻烦在于:官方文档是按表 写的------一张表一页、把字段列全,可你出事的时候脑子里是症状 ,不是表名。中间那一层「我这个现象该查哪张表」,得自己攒。

这篇就是我自己攒的那一层,按症状组织,出问题照着抄。
📚 「命令速查」系列 :PostgreSQL 常用命令速查(MySQL 用户平滑上手) · uv 常用命令速查(Python 工具链五合一) · Linux 网络排查命令速查(ss / tcpdump / mtr / dig) · Redis 排查命令速查 · Arthas 排查命令速查 · 本篇:ClickHouse 运维 SQL。
⚠️ 版本说明 :本文基于 ClickHouse 25.4 。system 表的字段在版本间有增减 (比如 system.parts 这些年就加过 visible、removal_state 等列),跑不通时先 DESCRIBE system.xxx 看一眼当前版本有哪些列,别照抄字段名。


一、先记这张表:症状 → 查哪儿

出问题时不用回忆表名,先看这一列「你观察到的现象」:

你观察到的现象 查这张表 跳到
服务卡住 / 报表跑不出来 system.processes [§三](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
ALTER DELETE / UPDATE 半天没生效 system.mutations [§三](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
数据重复、ReplacingMergeTree 没去重 system.parts + system.merges [§六](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
内存报警、MemoryTracking 居高不下 system.metrics / system.query_log [§四](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
磁盘告警,但业务表体积对不上 system.parts [§五](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) [§八](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
查询走没走索引 EXPLAIN indexes = 1 [§七](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)
加的跳数索引到底有没有生效 system.data_skipping_indices [§七](#你观察到的现象 查这张表 跳到 服务卡住 / 报表跑不出来 system.processes §三 ALTER DELETE / UPDATE 半天没生效 system.mutations §三 数据重复、ReplacingMergeTree 没去重 system.parts + system.merges §六 内存报警、MemoryTracking 居高不下 system.metrics / system.query_log §四 磁盘告警,但业务表体积对不上 system.parts §五 删了数据磁盘没变小 system.mutations + system.parts(多半是行为不是故障) §八 查询走没走索引 EXPLAIN indexes = 1 §七 加的跳数索引到底有没有生效 system.data_skipping_indices §七)

💡 一个通用前提 :system 库里带 _log 后缀的都是历史表 (query_log、part_log、text_log...),是写盘的真实表 ;不带 _log 的多是实时视图 (processes、merges、metrics...),只反映此刻 。查历史用前者,查现状用后者------这条分不清,会出现「明明刚才还在跑,怎么查不到」。


二、先连上去

sh 复制代码
# 容器里连(本机部署直接跑 clickhouse-client)
docker exec -it clickhouse-server clickhouse-client

# -m 允许多行 SQL(不加的话回车就执行,写长 SQL 很难受)
clickhouse-client -m

# 想看执行细节:打开 trace 级日志,排查「慢在哪一步」很有用
clickhouse-client --send_logs_level=trace <<< "SELECT count() FROM ods.ods_tb_message"

# 直接导出 CSV(带表头)
clickhouse-client --query "SELECT count() FROM ods.ods_tb_message" --format CSVWithNames > /tmp/rs.csv

不想进容器,走 HTTP 接口(8123)也行,排查时最省事:

sh 复制代码
curl 'http://localhost:8123/' --data-binary 'SELECT 1'
curl 'http://localhost:8123/?user=<user>&password=<password>' --data-binary "SELECT 1"

⚠️ 坑 :常用格式还有 TSV、JSONEachRow、Pretty。Pretty 在终端看着舒服,但别用它导文件,里面全是画表格的字符。


三、现在到底在跑什么

3.1 正在执行的查询

sql 复制代码
SELECT
    query_id, user,
    elapsed,              -- 已经跑了多少秒
    is_cancelled,
    memory_usage,         -- 此刻占用内存(字节)
    peak_memory_usage,    -- 到目前为止的峰值
    read_rows,
    query
FROM system.processes
ORDER BY elapsed DESC;    -- 抓「谁在吃内存」就改成 ORDER BY memory_usage DESC

3.2 杀掉一个查询

sql 复制代码
KILL QUERY WHERE query_id = 'a1b2c3d4-....';

⚠️ 坑 :KILL QUERY 是异步 的------它只是打了个「取消」标记,语句立刻返回,但查询不一定马上停 。想确认是不是真停了,回头再查一次 system.processes;也可以用 KILL QUERY ... SYNC 等它真正结束。

📌 SHOW PROCESSLIST 是 system.processes 的简写,应急时敲着快,但不能加 WHERE 和排序,排查还是查表。

3.3 数据变更任务(ALTER DELETE / UPDATE)

这是最容易被忽略的一类------ALTER TABLE ... DELETE 不是同步的,它是一个后台 mutation:

sql 复制代码
SELECT
    database, table, mutation_id, command,
    create_time,
    is_done,
    parts_to_do          -- 还剩多少个 part 没处理完
FROM system.mutations
WHERE NOT is_done;

⚠️ 坑(很常见) :删了半天数据没变化,八成不是没执行,而是 mutation 还在跑 ,或者卡住了 。看 parts_to_do 是不是在往下掉;如果一直不动,看 latest_fail_reason 字段------失败原因写在那里。

mutation 卡住可以撤 :KILL MUTATION WHERE mutation_id = '...'。

3.4 后台合并任务

sql 复制代码
SELECT
    database, table,
    elapsed,
    progress,            -- 0~1 的进度
    result_part_name,
    source_part_names
FROM system.merges;

四、内存到底被谁吃了

4.1 先看总量

sql 复制代码
SELECT metric, formatReadableSize(value) AS size
FROM system.metrics WHERE metric = 'MemoryTracking'           -- CK 自己记的账
UNION ALL
SELECT metric, formatReadableSize(value)
FROM system.asynchronous_metrics WHERE metric = 'MemoryResident';  -- 进程实际占的物理内存

两个数差很多(比如前者 20G、后者 2.4G)时,先别急着找大查询,见文末延伸阅读第二篇。

4.2 再看历史上谁吃得最多

system.processes 只有此刻 在跑的,查历史必须用 query_log:

sql 复制代码
SELECT
    query_id, query,
    formatReadableSize(memory_usage) AS peak_mem,   -- query_log 里它就是峰值
    query_duration_ms
FROM system.query_log
WHERE event_date = today()
  AND type = 'QueryFinish'
  AND memory_usage > 1e9        -- 只看超过 1GB 的
ORDER BY memory_usage DESC
LIMIT 10;

⚠️ 两个坑:

  1. query_log 一行查询会记两条 ------QueryStart 和 QueryFinish,不加 type 过滤,条数和内存都会看重。异常结束的是 ExceptionBeforeStart / ExceptionWhileProcessing,查失败原因时反而要专门捞这两类。
  2. query_log 只有 memory_usage,记的就是这条查询的峰值 。peak_memory_usage 是 system.processes 才有的列,照抄过来会报 Unknown expression identifier。

五、磁盘被谁吃了(最容易翻车的一节)

5.1 按表排序,看谁最大

sql 复制代码
SELECT
    database, table,
    sum(data_compressed_bytes) AS cc,
    formatReadableSize(sum(data_compressed_bytes)) AS total_compressed
FROM system.parts
WHERE active = 1
GROUP BY database, table
ORDER BY cc DESC
LIMIT 20;

⚠️ active = 1 这个条件不能省。 合并之后旧 part 不会立刻消失,会以 active = 0 的状态再留 8 分钟以上(见 §八)。不加过滤,算出来的体积会明显偏大,然后你就会去查一个根本不存在的「磁盘暴涨」。

5.2 ⭐ 实测:一半以上的磁盘,是 ClickHouse 自己的日志表吃掉的

有一次磁盘占到 280 GiB,而业务表按预估只有 100 G 出头,怎么算都对不上。按上面那条 SQL 排下来,第一名让人意外:

database table total_compressed
system text_log 105.06 GiB
ods (最大的业务表) 33.80 GiB
system trace_log 23.44 GiB
system part_log 20.66 GiB
system query_log 17.96 GiB
ods (第二大业务表) 13.14 GiB
... ... ...
system metric_log 3.22 GiB

system.text_log 一张表 105 GiB,是最大业务表的 3 倍多 ;再算上 trace_log、part_log、query_log、query_views_log、processors_profile_log、metric_log,系统日志表合计占掉了一半以上的磁盘。

原因不复杂:这些表默认开着、默认没有 TTL (25.4 默认配置里,表里这五张都不过期),写入量跟着集群活跃度线性涨,而没人会想到去查它们 ------大家排查磁盘时,眼睛都盯在 ods、dwd 上。

怎么处理 :这些表可以安全清空 (它们的表注释里都写着 「It is safe to truncate or drop this table at any time」 ,SELECT comment FROM system.tables WHERE database = 'system' 能看到),清完不影响任何业务数据,只是丢掉历史排查线索:

sql 复制代码
TRUNCATE TABLE system.text_log;
TRUNCATE TABLE system.trace_log;

💡 治本是给它们配 TTL 或直接关掉 (在 config.xml 里各 *_log 段配置留存策略 / 注释掉对应段落)。text_log 尤其值得关注------它记的是服务端日志正文 ,默认配置是 <level>trace</level>,连 trace 级日志都入库,所以量最大、平时最没用。

5.3 看某张表的分区分布

sql 复制代码
SELECT
    partition,
    count() AS parts,
    sum(rows) AS rows,
    formatReadableSize(sum(bytes_on_disk)) AS size,
    min(min_time), max(max_time)
FROM system.parts
WHERE database = 'ods' AND table = 'ods_tb_message' AND active
GROUP BY partition
ORDER BY partition;

六、合并没动,数据一直重复

ReplacingMergeTree 只在合并时去重 。所以「数据重复」这个现象,本质上问的是「为什么还没合并」。

先看每个分区堆了多少个 part:

sql 复制代码
SELECT partition, count() AS parts_count
FROM system.parts
WHERE table = 'dim_user_profile' AND active
GROUP BY partition
ORDER BY parts_count DESC;

后台合并什么时候发生是不保证的:part 少、写入停了,几个 part 可能一直就这么放着。想立刻去重就手动强合:

sql 复制代码
OPTIMIZE TABLE dim_user_profile FINAL;

⚠️ 三个坑,按踩中概率排:

  1. OPTIMIZE ... FINAL 是重操作 ------不带 PARTITION 时会把每个分区 都重写一遍,大表上跑可能几十分钟并吃满 IO。只坏了一个分区就用 OPTIMIZE TABLE t PARTITION '202601' FINAL;别在业务高峰期敲,更别写进定时任务无脑跑。
  2. FINAL 关键字在两个地方含义不同 :OPTIMIZE TABLE t FINAL 是真的去合并 ;SELECT ... FROM t FINAL 只是查询时临时合并给你看,不落盘、而且很慢。查数临时去重用后者,治本用前者。
  3. 「怎么合了还是重复」,查两处 :① 去重的依据是排序键(ORDER BY),不是主键、也不是你以为的业务唯一键,ORDER BY 里没带上的字段不参与判断;② 合并只在分区内发生 ,同一个 key 落在两个分区(比如分区键用了会变的时间字段),OPTIMIZE ... FINAL 永远合不掉。更迷惑的是 SELECT ... FINAL 默认会跨分区去重,查出来是干净的,底下其实还是两行。

七、查询为什么慢:EXPLAIN 七件套

sql 复制代码
EXPLAIN SELECT ...                    -- 默认:执行计划
EXPLAIN PLAN SELECT ...               -- 同上,写全了更清楚
EXPLAIN PIPELINE SELECT ...           -- 执行管道:看并行度、看各阶段
EXPLAIN indexes = 1 SELECT ...        -- ⭐ 最常用:索引有没有生效、裁掉了多少 part
EXPLAIN ESTIMATE SELECT ...           -- 预估要扫多少行 / 多少 part
EXPLAIN SYNTAX SELECT ...             -- 优化器改写后的 SQL
EXPLAIN AST SELECT ...                -- 语法树,基本只在排 SQL 解析问题时用

日常只有两条真正高频 :EXPLAIN indexes = 1 看索引裁剪,EXPLAIN ESTIMATE 看扫描量。

查跳数索引建了没、生效没:

sql 复制代码
SELECT name, type, expr, granularity
FROM system.data_skipping_indices
WHERE database = 'ods' AND table = 'ods_tb_message';

⚠️ 坑:新加的跳数索引对存量数据默认不生效。 ALTER TABLE ... ADD INDEX 只写元数据,必须再物化一次,历史 part 才会真正建索引:

sql 复制代码
ALTER TABLE ods.ods_tb_message ADD INDEX idx_from_time (from_account, msg_time) TYPE minmax GRANULARITY 4;
ALTER TABLE ods.ods_tb_message MATERIALIZE INDEX idx_from_time;   -- 别漏这步

物化本身是个 mutation,会跑一阵------回 §3.3 看进度。


八、删数据:三种方式区别很大

写法 磁盘空间 说明
ALTER TABLE ... DELETE WHERE ... 8 分钟以后才释放 异步 mutation,把命中的 part 逐个重写一遍,旧 part 8 分钟以后才删
ALTER TABLE ... DROP PARTITION ... 当场释放 不重写数据,整个分区的文件直接删,最快,能按分区删就别用 DELETE
TRUNCATE TABLE ... 当场释放 清空整表,和 DROP PARTITION 是一类
sql 复制代码
-- 慢:异步、按条件、要重写 part
ALTER TABLE ods.ods_tb_message DELETE WHERE msg_time < toDate('2026-01-01', 'UTC');

-- 快:直接干掉整个分区
ALTER TABLE ods.ods_tb_message DROP PARTITION '202601';

-- 一次删多个
ALTER TABLE ods.ods_event_log
  DROP PARTITION '20260201',
  DROP PARTITION '20260202';

为什么同样是「删」,磁盘表现完全不同 ------差别在于要不要重写数据:
#mermaid-svg-80Y3eRa5eWzaLx8t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-80Y3eRa5eWzaLx8t .error-icon{fill:#552222;}#mermaid-svg-80Y3eRa5eWzaLx8t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-80Y3eRa5eWzaLx8t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-80Y3eRa5eWzaLx8t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-80Y3eRa5eWzaLx8t .marker.cross{stroke:#333333;}#mermaid-svg-80Y3eRa5eWzaLx8t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-80Y3eRa5eWzaLx8t p{margin:0;}#mermaid-svg-80Y3eRa5eWzaLx8t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster-label text{fill:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster-label span{color:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster-label span p{background-color:transparent;}#mermaid-svg-80Y3eRa5eWzaLx8t .label text,#mermaid-svg-80Y3eRa5eWzaLx8t span{fill:#333;color:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t .node rect,#mermaid-svg-80Y3eRa5eWzaLx8t .node circle,#mermaid-svg-80Y3eRa5eWzaLx8t .node ellipse,#mermaid-svg-80Y3eRa5eWzaLx8t .node polygon,#mermaid-svg-80Y3eRa5eWzaLx8t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-80Y3eRa5eWzaLx8t .rough-node .label text,#mermaid-svg-80Y3eRa5eWzaLx8t .node .label text,#mermaid-svg-80Y3eRa5eWzaLx8t .image-shape .label,#mermaid-svg-80Y3eRa5eWzaLx8t .icon-shape .label{text-anchor:middle;}#mermaid-svg-80Y3eRa5eWzaLx8t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-80Y3eRa5eWzaLx8t .rough-node .label,#mermaid-svg-80Y3eRa5eWzaLx8t .node .label,#mermaid-svg-80Y3eRa5eWzaLx8t .image-shape .label,#mermaid-svg-80Y3eRa5eWzaLx8t .icon-shape .label{text-align:center;}#mermaid-svg-80Y3eRa5eWzaLx8t .node.clickable{cursor:pointer;}#mermaid-svg-80Y3eRa5eWzaLx8t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-80Y3eRa5eWzaLx8t .arrowheadPath{fill:#333333;}#mermaid-svg-80Y3eRa5eWzaLx8t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-80Y3eRa5eWzaLx8t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-80Y3eRa5eWzaLx8t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-80Y3eRa5eWzaLx8t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-80Y3eRa5eWzaLx8t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-80Y3eRa5eWzaLx8t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster text{fill:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t .cluster span{color:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-80Y3eRa5eWzaLx8t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-80Y3eRa5eWzaLx8t rect.text{fill:none;stroke-width:0;}#mermaid-svg-80Y3eRa5eWzaLx8t .icon-shape,#mermaid-svg-80Y3eRa5eWzaLx8t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-80Y3eRa5eWzaLx8t .icon-shape p,#mermaid-svg-80Y3eRa5eWzaLx8t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-80Y3eRa5eWzaLx8t .icon-shape .label rect,#mermaid-svg-80Y3eRa5eWzaLx8t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-80Y3eRa5eWzaLx8t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-80Y3eRa5eWzaLx8t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-80Y3eRa5eWzaLx8t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DROP PARTITION / TRUNCATE:不重写,当场回收
提交 DROP / TRUNCATE
直接删除

对应的磁盘文件
空间当场释放
ALTER DELETE:要重写,空间 8 分钟以后才回收
提交 DELETE
生成 mutation

进后台队列
逐个 part 重写

parts_to_do 递减
旧 part 变成

active = 0
至少等 old_parts_lifetime

默认 480 秒
删文件,空间释放

💡 看懂这张图,就知道该去哪儿看进度 :卡在 A3 就查 system.mutations 的 parts_to_do;过了 A3、磁盘还没变小,查 system.parts WHERE NOT active 看旧 part 还剩多少。能按分区删就别用 DELETE。
⚠️ 三个坑:

  1. 磁盘快满时别用 DELETE 腾空间 ------新 part 先写出来、旧 part 后删,这期间磁盘反而先涨一截 :在 25.4 上对一张 92 MB 的表删掉约四分之一的行,表目录先涨到 118 MB,8.5~10 分钟后才降到 70 MB。急着回收空间,用 DROP PARTITION。

  2. 先 DELETE 过、再 DROP 同一个分区 ,DROP 掉的部分当场释放,但 DELETE 留下的旧版本 part 还得等满它自己的 8 分钟。看到「DROP 了还有一截没回来」,system.parts WHERE NOT active 里就是它们。

  3. 删大表 / 大分区可能被安全阈值拦下 (默认 50 GB),报 Table or Partition in ... was not dropped,下一行是 Size (...) is greater than max_[table/partition]_size_to_drop。这是 CK 防手滑的保护。确认无误后在当前会话 放开即可:

    sql 复制代码
    SET max_table_size_to_drop = 0;      -- 0 = 不限制,仅当前会话生效
    SET max_partition_size_to_drop = 0;

    别去改配置文件把它永久关掉------这道保护拦住过很多次误删。


九、速查卡

sql 复制代码
-- 现在在跑什么
SELECT query_id, user, elapsed, memory_usage, query FROM system.processes ORDER BY elapsed DESC;
KILL QUERY WHERE query_id = '...';

-- 变更任务卡住没
SELECT database, table, mutation_id, is_done, parts_to_do, latest_fail_reason
FROM system.mutations WHERE NOT is_done;

-- 合并在不在动
SELECT database, table, elapsed, progress FROM system.merges;

-- 内存历史 Top(注意 type 过滤;query_log 的 memory_usage 就是峰值)
SELECT query_id, formatReadableSize(memory_usage), query_duration_ms, query FROM system.query_log
WHERE event_date = today() AND type = 'QueryFinish' ORDER BY memory_usage DESC LIMIT 10;

-- 磁盘 Top(注意 active = 1)
SELECT database, table, formatReadableSize(sum(data_compressed_bytes)) FROM system.parts
WHERE active = 1 GROUP BY database, table ORDER BY sum(data_compressed_bytes) DESC LIMIT 20;

-- 分区里堆了多少 part
SELECT partition, count() FROM system.parts WHERE table = 'xxx' AND active GROUP BY partition;

-- 索引有没有生效
EXPLAIN indexes = 1 SELECT ...;
SELECT name, type, expr, granularity FROM system.data_skipping_indices WHERE table = 'xxx';

四条最容易踩的:

  1. 算磁盘必须 WHERE active = 1,否则把已合并的旧 part 也算进去了。
  2. 查 query_log 必须过滤 type ,否则一条查询看成两条;它没有 peak_memory_usage 列 ,memory_usage 本身就是峰值。
  3. ALTER ... DELETE 是异步的 ,"没生效"先去 system.mutations 看 parts_to_do。
  4. 先查一次 system.*_log 占了多少磁盘------它很可能是你集群里最大的那张表。

延伸阅读

本篇负责「出问题该查哪张表 」,下面几篇负责「查出来之后怎么治」:

相关推荐
Databend1 小时前
以云原生湖仓支撑 AI 应用落地丨Databend 亮相 2026 人工智能开放计算大会
大数据·数据库·数据分析
geovindu1 小时前
sql:Managing Complex Joins and Subqueries in SQL using mysql 9.0
大数据·数据库·数据仓库·sql·mysql·数据库开发·数据库架构
国科安芯1 小时前
星载微控制器MCU选型与系统设计的证据化方法
大数据·数据库·单片机·嵌入式硬件·低轨卫星·商业航天·抗辐射
todoitbo2 小时前
报表总在扫大表,列存储应该放在哪一层
大数据·数据库·报表·国产数据库
深海里的鱼(・ω<)★2 小时前
SMB备份导致的 Gitea 分支推送成功但界面不可见故障复盘
大数据·elasticsearch·gitea
zhangzeyuaaa2 小时前
StarRocks 的技术版图:在分析、检索与流处理之间找到位置
大数据·数据仓库
漫步人生走在路上2 小时前
外贸SEO中的品牌词与产品词策略:大卖家和小卖家的不同打法
大数据·javascript·网络·数据库·人工智能·搜索引擎
2503_931712482 小时前
数据服务公司怎么帮助企业更高效找到行业数据与商业情报
大数据·人工智能·microsoft
AI Workforce3 小时前
数据质量:4 层去重 + 黑名单机制——宁可漏杀,不可错杀
大数据·人工智能·ai·架构·prompt