MySQL分表后原分区表处理方案

来源:这里教程网 时间:2026-03-01 15:35:08 作者:

主题:原分区表,由于数据量上涨,需要进行分表,分表后原分区表的数据删掉,将表修改为非分区表。

大数据量数据删除问题(原则)

1、批量删除,这样往往可以工作得更快,你可能需要在每次批量删除前sleep一段时间,控制删除的频率, 这样的目的是减少对生产系统的IO冲击,把符合平均分布,避免从库滞后太多; 2、可以考虑分区表技术,删除一个分区,比删除大量数据简单方便的多,这也是分区表清理/归档数据的优势所在; 删除分区会把分区内的数据删掉,本次实践就是采用这样的方式。 3、按照主键的序列分批分批,或者基于时间分批分批,你总可以找到一种方式批量删除, 如果实在没有批量删除的方式,可能你的表结构设计得不好; 4、基于硬件的性能,每批删除的记录数,可以选择几百到几千到几万的数据量, 但不要太大,MySQL很难同时处理好大事务和随机小事务; 5、如果要删除大部分数据,那么可以考虑的方式是, 创建一个新表,insert要保留的数据,然后切换表; 6、对于大表(InnoDB)删除大量数据,如果是一个很大的事务,中止删除数据的操作, 可能需要几倍的时间用于回滚,导致严重的IO瓶颈,而批量删除可以让我们的回滚恢复得快得多。 7、需要留意空间的释放,选择独立表空间会更有利于释放空间。

实践步骤

MySQL分区表的创建

CREATE TABLE tmp_User
(
   UserNum              BIGINT NOT NULL AUTO_INCREMENT,
   UserName             VARCHAR(30),
   CreateDate              DATE NOT NULL,
   Remark              VARCHAR(64),
   PRIMARY KEY (UserNum)
)
ENGINE = INNODB
 PARTITION BY RANGE
 (TO_DAYS(CreateDate))
    (
        PARTITION p_first
 VALUES LESS THAN (TO_DAYS('2021-01-01')),
        PARTITION p202101
 VALUES LESS THAN (TO_DAYS('2021-02-01')),
		PARTITION p202102
 VALUES LESS THAN (TO_DAYS('2021-03-01')),
 		PARTITION p202103
 VALUES LESS THAN (TO_DAYS('2021-04-01')),
        PARTITION p202104
 VALUES LESS THAN (TO_DAYS('2021-05-01')),
		PARTITION p202105
 VALUES LESS THAN (TO_DAYS('2021-06-01')),
		PARTITION p202106
 VALUES LESS THAN (TO_DAYS('2021-07-01')),
        PARTITION p202107
 VALUES LESS THAN (TO_DAYS('2021-08-01')),
		PARTITION p202108
 VALUES LESS THAN (TO_DAYS('2021-09-01')),
		PARTITION p202109
 VALUES LESS THAN (TO_DAYS('2021-10-01')),
        PARTITION p202110
 VALUES LESS THAN (TO_DAYS('2021-11-01')),
		PARTITION p202111
 VALUES LESS THAN (TO_DAYS('2021-12-01')),
		PARTITION p202112
 VALUES LESS THAN (TO_DAYS('2022-01-01')),
PARTITION p_future
 VALUES LESS THAN (MAXVALUE)
    );

报错信息

[Err] 1503 - A PRIMARY KEY must include all columns in the table's partitioning function  分区的字段必须是要包含在主键当中。这时候分区的字段要么是主键,要么把分区字段加入到主键中,从而形成复合主键。 或者不指定主键! 修改sql如下

CREATE TABLE tmp_User
(
   UserNum              BIGINT NOT NULL AUTO_INCREMENT,
   UserName             VARCHAR(30),
   CreateDate              DATE NOT NULL,
   Remark              VARCHAR(64),
   PRIMARY KEY (UserNum,CreateDate)
)
ENGINE = INNODB
 PARTITION BY RANGE
 (TO_DAYS(CreateDate))
    (
        PARTITION p_first
 VALUES LESS THAN (TO_DAYS('2021-01-01')),
        PARTITION p202101
 VALUES LESS THAN (TO_DAYS('2021-02-01')),
		PARTITION p202102
 VALUES LESS THAN (TO_DAYS('2021-03-01')),
 		PARTITION p202103
 VALUES LESS THAN (TO_DAYS('2021-04-01')),
        PARTITION p202104
 VALUES LESS THAN (TO_DAYS('2021-05-01')),
		PARTITION p202105
 VALUES LESS THAN (TO_DAYS('2021-06-01')),
		PARTITION p202106
 VALUES LESS THAN (TO_DAYS('2021-07-01')),
        PARTITION p202107
 VALUES LESS THAN (TO_DAYS('2021-08-01')),
		PARTITION p202108
 VALUES LESS THAN (TO_DAYS('2021-09-01')),
		PARTITION p202109
 VALUES LESS THAN (TO_DAYS('2021-10-01')),
        PARTITION p202110
 VALUES LESS THAN (TO_DAYS('2021-11-01')),
		PARTITION p202111
 VALUES LESS THAN (TO_DAYS('2021-12-01')),
		PARTITION p202112
 VALUES LESS THAN (TO_DAYS('2022-01-01')),
PARTITION p_future
 VALUES LESS THAN (MAXVALUE)
    );

检查表结构

SHOW CREATE TABLE tmp_User;

造数据

DROP PROCEDURE idata;
DELIMITER ;;
CREATE PROCEDURE idata()
BEGIN
DECLARE i INT;
SET i=1;
WHILE(i<=1000)DO
INSERT INTO tmp_User (UserName,CreateDate,Remark) 
VALUES(CONCAT('张',i), DATE_ADD(NOW(),INTERVAL 1 DAY),'存储insert');
SET i=i+1;
END WHILE;
END;;
DELIMITER ;
CALL idata();

检查分区信息

-- 检查分区
SELECT TABLE_NAME,PARTITION_NAME,PARTITION_METHOD,
PARTITION_EXPRESSION,PARTITION_DESCRIPTION,TABLE_ROWS,
DATA_FREE,CREATE_TIME,UPDATE_TIME
FROM information_schema.`PARTITIONS` WHERE TABLE_NAME = 'tmp_User';

如果以上数据是来自生产库数据,测试过程中不能随便改动。

需要先创建一张相同的表,创建方法如下

create table xx as 只复制表结构和数据,不包含主键,索引,分区等信息

CREATE TABLE tmp_user1 AS
SELECT * FROM tmp_user;

create table xx like 只复制表结构无数据,包含主键,索引,分区等信息

CREATE TABLE tmp_user2 LIKE tmp_user;

数据insert

REPLACE INTO tmp_User2
SELECT * FROM tmp_User;

检查分区信息

SELECT TABLE_NAME,PARTITION_NAME,PARTITION_METHOD,
PARTITION_EXPRESSION,PARTITION_DESCRIPTION,TABLE_ROWS,
DATA_FREE,CREATE_TIME,UPDATE_TIME
FROM information_schema.`PARTITIONS` WHERE TABLE_NAME = 'tmp_User';

删除全部分区语句通过select语句实现

SELECT CONCAT('alter table ',table_name,' drop partition ',partition_name,';')
FROM information_schema.`PARTITIONS` 
WHERE TABLE_NAME LIKE 'tmp_User2' ;

执行删除分区命令删除到最后一个分区时会报错 [Err] 1508 - Cannot REMOVE ALL PARTITIONS, USE DROP TABLE instead 因为是分区表,所以不能把所有的分区都删掉,只能删除表。

如何让分区表变为普通表

MySQL5.6开始支持 ALTER TABLE xx EXCHANGE PARTITION的语法。 允许分区或自分区的数据与另一个分区的数据进行交换。 如果非分区表的数据为空,相当于将分区中的数据移动到非分区表。 如果分区表数据为空,相当于将外部表数据导入分区中。

注意事项

要交换的表和分区表的表结构相同且不能含有分区。 非分区表的数据必须在交换的分区内定义。 被交换的表不能含有外键,且其他表不能含有对该表的外键引用。 用户需要有ALTER、INSERT、CREATE、DROP的权限。 使用该语句时触发器不会被触发。 AUTO_INCREMENT列将会被重置。 如果分区表和非分区表的数据都为空这种方法就不适用了!

分区表和非分区表的数据都为空采取一下措施

表的重命名

RENAME TABLE tmp_User2 TO tmp_User2old ;

表的创建

CREATE TABLE tmp_User2old
(
   UserNum              BIGINT NOT NULL AUTO_INCREMENT,
   UserName             VARCHAR(30),
   CreateDate              DATE NOT NULL,
   Remark              VARCHAR(64),
   PRIMARY KEY (UserNum)
)
ENGINE = INNODB;

数据insert

REPLACE INTO tmp_User2 
SELECT * FROM tmp_User2old ;

tmp表删除

DROP TABLE IF EXISTS tmp_User2old ;

参考文献:

《MySQL技术内幕:sql编程》第十章 分区

MySQL删除大量数据的一些建议 - Gary Chen的文章 - 知乎 https://zhuanlan.zhihu.com/p/20209766

相关推荐