我有一个在MySQL服务器的所有历史数据的表格,这是非常庞大(约700万行)。我创建一个新表使用相同的列,但使用分区,然后我需要的所有数据从旧表复制到新的分区表。我已经得到了正确的脚本来做到这一点,但我认为它可能会锁定表。我不希望这种情况发生,因为这是生产服务器上。我应该怎么做,以避免锁定表?
给该表有你可以做这样的事情完全一样的列:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
INSERT INTO NEW_TABLE (SELECT * FROM OLD_TABLE);
COMMIT ;
我已经包括了基于Wistar's评论一些额外的解释。这里可以使用的读级别是:
我希望这有帮助。
我不知道什么是你的脚本,但我建议你用吸盘插入。 See this example。
如果您使用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 statement同时
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
大小根据你的配置调整块大小
复制块。你有一个AUTO_INCREMENT
PRIMARY KEY
?如果是这样那么做
WHERE id >= $x AND id < $x + 1000
如果有很多的差距,或者如果您有其他问题,则见other techniques for efficiently chunking。
The evils of Pagination via OFFSET
。
更重要的是,是使用pt-online-schema-alter
来自Percona的。它大部分的思维背后究竟我所描述的,再加上它允许您在副本正在做写入表。 (它使用TRIGGERs
去实现它。)
为了减少使用OFFSET
的缺点,this article描述使用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 ;