如何用分区表 TRUNCATE PARTITION 实现按月清空历史数据

14次阅读

TRUNCATE PARTITION 比 DELETE 更快,因其是 DDL 操作,直接释放数据页和元数据,不写事务日志、不触发触发器或外键检查;千万级分区清空仅需毫秒级,而 DELETE 易卡顿并撑爆 undo 表空间。

如何用分区表 TRUNCATE PARTITION 实现按月清空历史数据

TRUNCATE PARTITION 为什么 比 DELETE 更快

因为 TRUNCATE PARTITION 是 DDL 操作,直接释放数据页和元数据,不走事务日志逐行删除,也不触发触发器或外键检查。在千万级以上分区表中,清空一个分区通常在毫秒级完成;而 DELETE 可能卡住几秒到几分钟,还容易撑爆 undo 表空间。

但注意:MySQL 8.0+ 和 PostgreSQL 12+ 才原生支持该语法,旧版本只能用 DROP PARTITION + REORGANIZE 替代(会重建分区结构,风险更高)。

MySQL 中 TRUNCATE PARTITION 的实际写法

必须确保表是按 RANGELIST 分区,且目标分区存在。常见错误是传入不存在的分区名,报错:ERROR 1735 (HY000): Unknown partition 'p202301'

  • 先查分区名:SELECT PARTITION_NAME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'orders' AND TABLE_SCHEMA = 'prod_db';
  • 再执行清空:ALTER TABLE orders TRUNCATE PARTITION p202301;
  • 支持批量:ALTER TABLE orders TRUNCATE PARTITION p202301, p202302, p202303;
  • 不能跨引擎混用——InnoDB 支持,MyISAM 不支持 TRUNCATE PARTITION(只支持全表 TRUNCATE

PostgreSQL 中没有 TRUNCATE PARTITION,得用 TRUNCATE …… ONLY

PG 没有 TRUNCATE PARTITION 语法,但可通过继承关系把分区当作独立子表处理。前提是使用原生声明式分区(PARTITION BY RANGE),且子表名由系统生成(如 logs_202301)。

  • 确认子表名:d+ logs 查看分区列表,或查 pg_inherits
  • 清空指定月:TRUNCATE TABLE ONLY logs_202301;ONLY 关键字不能省,否则会清空父表)
  • 若用 TRUNCATE TABLE logs_202301(无 ONLY),会误删所有继承链上的数据
  • PG 14+ 支持 TRUNCATE TABLE logs PARTITION (FOR VALUES FROM ('2023-01-01') TO ('2023-02-01')),但需明确时间范围,不如子表名直觉

自动化脚本里怎么安全地按月清理

别硬 编码 分区名。真实场景中,分区名常按 pYYYYMM 命名,需动态生成并校验存在性,否则脚本一跑就崩。

  • MySQL 示例(存储过程片段):
    SET @partition_name = CONCAT('p', DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 12 MONTH), '%Y%m')); SET @sql = CONCAT('ALTER TABLE orders TRUNCATE PARTITION', @partition_name); SELECT COUNT(*) INTO @exists FROM INFORMATION_SCHEMA.PARTITIONS    WHERE TABLE_NAME='orders' AND PARTITION_NAME=@partition_name; IF @exists > 0 THEN PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF;
  • 关键点:必须先查 INFORMATION_SCHEMA.PARTITIONS 确认分区存在,再拼 SQL;否则 PREPARE 会直接报错退出
  • PG 脚本建议用 psql -c "SELECT ……" 先取子表名,再调 TRUNCATE TABLE ONLY,避免因命名规则变更导致误删

最易被忽略的是权限——MySQL 需要 ALTER 权限,PG 需要子表上的 TRUNCATE 权限(不是父表权限)。运维配权时常常只给父表,结果脚本静默失败。

text=ZqhQzanResources