Reputation: 321
I have a table that has all history data in Mysql server and it is very huge (about 700 million rows). I am creating a new table with the same columns but with partitioning, then I need to copy all data from the old table into the new partitioned table. I have already got the right script to do that but I think it might lock the table. I don't want that happen because it is on production server. What should I do to avoid locking the table?
Upvotes: 7
Views: 7540
Reputation: 19
To reduce the disadvantages of using OFFSET
, this article describes a possible approach to use a JOIN
when a numeric primary key id
is available to force the use of the proper index. Note that to keep track of process, a "procedure_log" table is created and gradually updated after processing a batch:
For 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 ;
Upvotes: 0
Reputation: 142483
Copy in chunks. Do you have an AUTO_INCREMENT
PRIMARY KEY
? If so then do
WHERE id >= $x AND id < $x + 1000
If there are lots of gaps, or if you have other issues, then see other techniques for efficiently chunking.
The evils of Pagination via OFFSET
.
Better yet, is to use pt-online-schema-alter
from Percona. It does most of the thinking behind what I described, plus it allows you to write to the table while the copy is being done. (It uses TRIGGERs
to achieve it.)
Upvotes: 0
Reputation: 3790
I do not know what is your script but I suggest you insert with chucks. See this example.
If you use SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
you might incorrect version of your row being inserted. If you insert all the rows with one selects, well you will not only have locks but it won't be the best performance wise.
You could do something like
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 )
...
or use prepare statement and 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
Of course, you can adjust the chunk size according to your config by changing the LIMIT
size
Upvotes: 0
Reputation: 1842
Give that the tables have the exact same columns you can do something like this:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
INSERT INTO NEW_TABLE (SELECT * FROM OLD_TABLE);
COMMIT ;
I've included some additional explanation based on Wistar's comment. The read levels that can be used here are:
I hope this helps.
Upvotes: 7