首页 > 基础资料 博客日记

MySQL 大批量 LONGTEXT 数据迁移导致临时表空间溢出的解决方案

2026-06-11 15:30:02基础资料围观2

文章MySQL 大批量 LONGTEXT 数据迁移导致临时表空间溢出的解决方案分享给大家,欢迎收藏极客资料网,专注分享技术知识

事情是这样的,我有一个 MySQL 表,大概 100 个 G,但数据只有 10000 条。

看起来行数不多,奈何每条数据里塞了好几个 LONGTEXT 字段,单条数据体量巨大。

最近需要对这张表做一个备份操作:根据批次 ID(batchid)把某批数据备份到另一个库,然后删掉原表里对应的数据。

目标库有同名表,用来归档历史数据。思路很直接,两条 SQL 搞定:

-- 先把数据迁过去
INSERT INTO history_db.target_table
SELECT * FROM target_table WHERE batchid = 381;

-- 再把原表数据删掉
DELETE FROM target_table WHERE batchid = 381;

结果一执行直接报错,要么是:

SQL 错误 [3] [HY000]: Error writing file 'C:\Windows\TEMP\MLa4gsg6dpk9s2z8ry' (OS errno 28 - No space left on device)

要么是:

SQL 错误 [1114] [HY000]: The table 'C:\Windows\TEMP#sql6f88_bd086f_2' is full

MySQL 是装在Windows服务器的,不过明明装在 D 盘,为什么 C 盘会爆?

实际上,MySQL 在执行大事务或者大查询时,会在系统临时目录(C:\Windows\TEMP)生成临时文件。当一次操作的数据量太大,临时文件直接把 C 盘写满了,SQL 自然就挂了。INSERT ... SELECT 一次性搬运大量 LONGTEXT 数据,临时表膨胀得飞快(现象就是服务器剩余50G的C盘迅速飙红,待执行失败后又恢复正常)。

既然一步到位不行,那就化整为零——用存储过程分批处理:

CREATE DEFINER=`root`@`%` PROCEDURE `business_db`.`batch_migrate_and_delete`(
    IN p_table_name VARCHAR(128),
    IN p_batchid      BIGINT
)
batch_migrate_and_delete: BEGIN
    DECLARE v_min_id    BIGINT;
    DECLARE v_max_id    BIGINT;
    DECLARE v_batch_size INT DEFAULT 500;

    -- 获取 id 范围
    SET @sql_range = CONCAT(
        'SELECT MIN(id), MAX(id) INTO @v_min, @v_max FROM ',
        p_table_name, ' WHERE batchid = ', p_batchid
    );
    PREPARE stmt FROM @sql_range;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    SET v_min_id = @v_min;
    SET v_max_id = @v_max;

    IF v_min_id IS NULL THEN
        LEAVE batch_migrate_and_delete;
    END IF;

    -- 每批先迁移到历史库,再删除原表数据
    WHILE v_min_id <= v_max_id DO
        SET @sql_insert = CONCAT(
            'INSERT INTO history_db.', p_table_name,
            ' SELECT * FROM ', p_table_name,
            ' WHERE batchid = ', p_batchid,
            ' AND id BETWEEN ', v_min_id, ' AND ', v_min_id + v_batch_size - 1
        );
        PREPARE stmt FROM @sql_insert;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        SET @sql_delete = CONCAT(
            'DELETE FROM ', p_table_name,
            ' WHERE batchid = ', p_batchid,
            ' AND id BETWEEN ', v_min_id, ' AND ', v_min_id + v_batch_size - 1
        );
        PREPARE stmt FROM @sql_delete;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        COMMIT;
        SET v_min_id = v_min_id + v_batch_size;
    END WHILE;
END batch_migrate_and_delete

创建存储过程后,执行call即可

CALL batch_migrate_and_delete('target_table', 381)

 

存储过程的核心思路很简单:先用 MIN(id)MAX(id) 圈出要处理的数据范围,然后每次取 500 条(v_batch_size),做完 insert 和 delete 立刻 COMMIT,再继续下一批。这样每批事务体量都很小,临时文件来不及撑爆 C 盘就已经释放了。

⚠️ 需要注意的是:

  • v_batch_size 要结合实际调。我这里的表单条数据就很大,设 500 差不多。如果你的表行数多但单条很小,可以适当调大;反过来单条更大就调小一些,核心是控制每批事务的数据总量。
  • id BETWEEN 分批的前提是 id 连续或大致连续。如果 id 有大量空洞,可以换成 LIMIT 配合游标的方式分批。
  • 迁移和删除放在同一个事务批次里。先 insert 成功再 delete,万一 insert 失败,当前批次不会被删掉,数据不会丢。
  • Windows 下装 MySQL 要留意临时目录位置。如果 C 盘实在太小,可以在 MySQL 配置文件里把 tmpdir 指向其他盘,从根源上避免这个问题。

文章来源:https://www.cnblogs.com/yellow3gold/p/20448811
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云