我有一个表,其中包含Mysql服务器中的所有历史数据,并且它非常巨大(大约7亿行)。我正在创建一个具有相同列但具有分区的新表,然后我需要将旧表中的所有数据复制到新的分区表中。我已经有了正确的脚本来执行此操作,但我认为它可能会锁定表。我不希望发生这种情况,因为它位于生产服务器上。我应该怎么做才能避免锁定桌子?
假设表格具有完全相同的列,您可以执行以下操作:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
INSERT INTO NEW_TABLE (SELECT * FROM OLD_TABLE);
COMMIT ;
我根据 Wistar's 评论添加了一些附加解释。这里可以使用的读取级别是:
我希望这有帮助。
我不知道你的脚本是什么,但我建议你用卡盘插入。 请参阅此示例。
如果您使用
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
,您可能会插入错误的行版本。如果您使用一个选择插入所有行,那么您不仅会拥有锁,而且不会获得最佳性能。
你可以做类似的事情
INSERT INTO NEW TABLE (SELECT * FROM OLD_TABLE LIMIT 1000 OFFSET 0 )
INSERT INTO NEW TABLE (SELECT * FROM OLD_TABLE LIMIT 1000 OFFSET 1000 )
INSERT INTO NEW TABLE (SELECT * FROM OLD_TABLE LIMIT 1000 OFFSET 2000 )
...
或使用 prepare 语句 和 while
CREATE PROCEDURE myproc()
BEGIN
@rows :=0
SELECT COUNT(*) FROM OLD_TABLE into @rows
DECLARE i int DEFAULT 0;
WHILE i <= @rows DO
PREPARE stmt1 FROM 'INSERT INTO NEW TABLE (SELECT * FROM OLD_TABLE LIMIT 1000 OFFSET ? )'
EXECUTE stmt1 USING @i;
DEALLOCATE PREPARE stmt1;
SET i = i + 1000;
END WHILE;
END
当然,您可以根据您的配置通过更改
LIMIT
大小来调整块大小
为了减少使用
OFFSET
的缺点,这篇文章 描述了一种在数字主键 JOIN
可用时使用 id
的可能方法,以强制使用正确的索引。请注意,为了跟踪流程,会创建一个“procedure_log”表,并在处理一批后逐渐更新:
对于 MySQL:
DROP PROCEDURE IF EXISTS copyTable;
DELIMITER |
CREATE PROCEDURE copyTable()
BEGIN
DECLARE batchSize INT DEFAULT 100;
DECLARE i INT DEFAULT 0;
DECLARE rowCount INT;
# Note that we use a WHERE clause to prevent a full table scan / use the index properly
SET rowCount = (SELECT COUNT(id) FROM my_table WHERE id IS NOT NULL);
CREATE TABLE IF NOT EXISTS my_table_copy LIKE my_table;
CREATE TABLE IF NOT EXISTS procedure_log ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, entry TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) );
WHILE i <= rowCount DO
INSERT IGNORE INTO my_table_copy (
SELECT source.* FROM (
SELECT id FROM my_table ORDER BY id LIMIT i, batchSize
) tmp
JOIN my_table source ON source.id = tmp.id
ORDER BY source.id
);
SET i = i + batchSize;
INSERT INTO procedure_log (entry) VALUES (CONCAT('Copied batch from my_table => my_table_copy, batch: ', batchSize, ', offset: ', i, ', rowCount: ', rowCount));
END WHILE;
END |
DELIMITER ;
这就是我创建一个智能备份程序的方式,每当我调用它时都会复制所有表记录
DROP PROCEDURE IF EXISTS copyRawToBackup;
DELIMITER |
CREATE PROCEDURE copyRawToBackup()
BEGIN
DECLARE batchSize INT DEFAULT 5000;
DECLARE rowCount BIGINT;
DECLARE startId BIGINT;
DECLARE i BIGINT DEFAULT 0;
CREATE TABLE IF NOT EXISTS `db`.`backup_table` LIKE `db`.`original_table`;
SET rowCount = (SELECT `id` FROM `db`.`original_table` ORDER BY `id` DESC LIMIT 1);
WHILE i <= rowCount DO
SET startId = (SELECT `id` FROM `db`.`backup_table` ORDER BY `id` DESC LIMIT 1);
INSERT INTO `db`.`backup_table` (SELECT * FROM `db`.`original_table` WHERE `id`>startId ORDER BY `id` LIMIT batchSize OFFSET i );
SET i = i + batchSize;
END WHILE;
END |
DELIMITER ;
CALL copyRawToBackup();