This blog discusses newly added options for Percona XtraBackup 2.4.8 and how they can impact your database backups.
To avoid issues with MySQL 5.7 skipping the redo log for DDL, Percona XtraBackup has implemented three new options ( xtrabackup --lock-ddl, xtrabackup --lock-ddl-timeout, xtrabackup --lock-ddl-per-table) that can be used to place MDL locks on tables while they are copied.
So why we need those options? Let’s discuss the process used to get there.
Originally, we found problems while running DDLs: Percona XtraBackup produced corrupted backups as described in two reports:
After experimenting, it was clear that the core cause of those fails was MySQL 5.7 skipping redo logging for some DDLs. This is a newly added feature to MySQL named Sorted Index Builds. You can read more from following links:
To prevent this we introduced a solution: wWhen Percona XtraBackup detects skipping the redo log), it aborts the backup to prevent creating a corrupted backup.
The scary error message you get with this fix is:
[FATAL] InnoDB: An optimized(without redo logging) DDLoperation has been performed. All modified pages may not have been flushed to the disk yet.
Percona XtraBackup will not be able to take a consistent backup. Retry the backup operation
We need to avoid aborting backup with this message. So how do we do that? Let’s create a test case first and reproduce the issue.
Prepare two tables:
|
1 |
sysbench /usr/share/sysbench/oltp_insert.lua --db-driver=mysql --mysql-db=db1 --mysql-user=msandbox --mysql-password=msandbox --table-size=2000000 --mysql-socket=/tmp/mysql_sandbox20393.sock prepare<br><br>sysbench /usr/share/sysbench/oltp_insert.lua --db-driver=mysql --mysql-db=db2 --mysql-user=msandbox --mysql-password=msandbox --table-size=2000000 --mysql-socket=/tmp/mysql_sandbox20393.sock prepare |
Create a test.sh file and place it in the sandbox:
|
1 |
#!/bin/bash<br><br>echo "drop table if exists db1.sb1"|./use<br>echo "create table sb1 as select id,c from sbtest1 where id < 150000;"|./use db1<br>echo "create unique index ix on sb1 (id)"|./use db1<br>sleep 1<br>echo "drop table if exists db2.sb1"|./use<br>echo "create table sb1 as select id,c from sbtest1 where id < 150000;"|./use db2<br>echo "create unique index ix on sb1 (id)"|./use db2<br> |
Run the script in a loop while the backup is taken:
|
1 |
$ while true; do bash test.sh; done |
Try to take a backup:
|
1 |
xtrabackup --defaults-file=/home/shahriyar.rzaev/sandboxes/rsandbox_Percona-Server-5_7_18/master/my.sandbox.cnf <br>--user=msandbox --password='msandbox' --target-dir=/home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-11-45 <br>--backup --host=127.0.0.1 --port=20393 --binlog-info=AUTO --galera-info --parallel 4 <br>--check-privileges --no-version-check |
You will likely get something like:
|
1 |
InnoDB: An optimized (without redo logging) DDLoperation has been performed. All modified pages may not have been flushed to the disk yet. <br>PXB will not be able take a consistent backup. Retry the backup operation |
Ok, now we have reproduced the error. To avoid this error, XtraBackup has the new options as mentioned above.
Using --lock-ddl:
|
1 |
xtrabackup --defaults-file=/home/shahriyar.rzaev/sandboxes/rsandbox_Percona-Server-5_7_18/master/my.sandbox.cnf <br>--user=msandbox --password='msandbox' --target-dir=/home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-16-56 <br>--backup --host=127.0.0.1 --port=20393 --binlog-info=AUTO --galera-info --parallel 4 <br>--check-privileges --no-version-check --lock-ddl |
The new thing you should notice is:
|
1 |
170726 11:16:56 Executing LOCK TABLES FOR BACKUP... |
And the backup status:
|
1 |
xtrabackup: Transaction log of lsn (2808294311) to (2808304872) was copied.<br>170726 11:20:42 completed OK! |
Another new option is --lock-ddl-per-table:
|
1 |
xtrabackup --defaults-file=/home/shahriyar.rzaev/sandboxes/rsandbox_Percona-Server-5_7_18/master/my.sandbox.cnf <br>--user=msandbox --password='msandbox' --target-dir=/home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-31-56 <br>--backup --host=127.0.0.1 --port=20393 --binlog-info=AUTO --galera-info --parallel 4 --check-privileges --no-version-check --lock-ddl-per-table<br> |
The new output will look like this:
|
1 |
170726 11:32:33 [01] Copying ./ibdata1 to /home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-31-56/ibdata1<br>170726 11:32:33 Locking MDL for db1.sb1<br>170726 11:32:33 [02] Copying ./db1/sb1.ibd to /home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-31-56/db1/sb1.ibd<br>170726 11:32:33 Locking MDL for db1.sbtest1<br>170726 11:32:33 Locking MDL for db2.sb1<br>170726 11:32:33 [03] Copying ./db1/sbtest1.ibd to /home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-31-56/db1/sbtest1.ibd<br>170726 11:32:33 [04] Copying ./db2/sb1.ibd to /home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-26_11-31-56/db2/sb1.ibd<br>170726 11:32:33 [04] ...done<br>170726 11:32:33 >> log scanned up to (2892754398)<br>170726 11:32:34 Locking MDL for db2.sbtest1 |
The result of the backup:
|
1 |
170726 11:35:45 Unlocking MDL for all tables<br>xtrabackup: Transaction log of lsn (2871333326) to (2892754764) was copied.<br>170726 11:35:45 completed OK! |
The another thing I should add here is about using --lock-ddl with non-Percona Server for MySQL servers. For example., using it with MariaDB:
|
1 |
2017-07-26 12:08:32 ERROR FULL BACKUP FAILED!<br>2017-07-26 12:08:37 ERROR 170726 12:08:32 Connecting to MySQL server host: 127.0.0.1, user: msandbox, password: set, port: 10207, socket: /tmp/mysql_sandbox10207.sock<br>Using server version 10.2.7-MariaDB<br>170726 12:08:32 Error: LOCK TABLES FOR BACKUP is not supported. |
But you can use --lock-ddl-per-table with any server. Use --lock-ddl-per-table with caution, it can block updates to tables for highly loaded servers under some circumstances. Let’s explore one:
|
1 |
Table: CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT);<br><br> Cases:<br><br> connection 1:<br><br> - BEGIN; SELECT * FROM sb1 LIMIT 1; <--- MDL<br> <br> connection 2:<br><br> - UPDATE sb1 SET c = '288' WHERE id = 34; <--- completes OK<br><br> connection 3:<br><br> - CREATE INDEX sb1_1 ON sb1 (c(10)); <--- WAITING for MDL<br><br> connection 2:<br><br> - UPDATE sb1 SET c = '288' WHERE id = 34; <--- WAITING for MDL<br><br> connection 1:<br><br> - COMMIT;<br><br> connection 2 and 3 are able to complete now<br> |
If one connection holds an MDL lock, and another connection does ALTER TABLE (CREATE INDEX is mapped to an ALTER TABLE statement to create indexes), then updates to that table are blocked.
Testing this with the backup process is quite easy:
Sample table:
|
1 |
CREATE TABLE `sb1` (<br> `id` int(11) NOT NULL DEFAULT '0',<br> `c` char(120) NOT NULL DEFAULT '',<br> UNIQUE KEY `ix` (`id`)<br>) ENGINE=InnoDB DEFAULT CHARSET=latin1<br><br>select count(*) from sb1;<br>+----------+<br>| count(*) |<br>+----------+<br>| 149999 |<br>+----------+<br><br>select * from sb1 limit 3;<br>+----+-------------------------------------------------------------------------------------------------------------------------+<br>| id | c |<br>+----+-------------------------------------------------------------------------------------------------------------------------+<br>| 1 | 83868641912-28773972837-60736120486-75162659906-27563526494-20381887404-41576422241-93426793964-56405065102-33518432330 |<br>| 2 | 38014276128-25250245652-62722561801-27818678124-24890218270-18312424692-92565570600-36243745486-21199862476-38576014630 |<br>| 3 | 33973744704-80540844748-72700647445-87330233173-87249600839-07301471459-22846777364-58808996678-64607045326-48799346817 |<br>+----+-------------------------------------------------------------------------------------------------------------------------+<br><br> |
So our “connection 1:” is an xtrabackup command:
|
1 |
xtrabackup --defaults-file=/home/shahriyar.rzaev/sandboxes/rsandbox_Percona-Server-5_7_18/master/my.sandbox.cnf <br>--user=msandbox --password='msandbox' --target-dir=/home/shahriyar.rzaev/backup_dir/ps_5.7_master/full/2017-07-28_07-55-30 <br>--backup --host=127.0.0.1 --port=20393 --binlog-info=AUTO --galera-info --parallel 4 --check-privileges --no-version-check <br>--lock-ddl-per-table<br> |
So after running the backup command and doing the same steps for “connection 2” and “connection 3,” the result is something like this in processlist:
|
1 |
show processlist;<br>+----+----------+-----------------+------+---------+------+---------------------------------+----------------------------------------+-----------+---------------+<br>| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |<br>+----+----------+-----------------+------+---------+------+---------------------------------+----------------------------------------+-----------+---------------+<br>| 4 | root | localhost | db1 | Sleep | 28 | | NULL | 0 | 1 |<br>| 10 | root | localhost | db1 | Query | 26 | Waiting for table metadata lock | CREATE INDEX sb1_1 ON sb1 (c(10)) | 0 | 0 |<br>| 11 | root | localhost | db1 | Query | 6 | Waiting for table metadata lock | UPDATE sb1 SET c = '288' WHERE id = 34 | 0 | 0 |<br>| 12 | root | localhost | NULL | Query | 0 | starting | show processlist | 0 | 0 |<br>| 13 | msandbox | localhost:36546 | NULL | Sleep | 31 | | NULL | 1 | 116 |<br>| 14 | msandbox | localhost:36550 | NULL | Sleep | 17 | | NULL | 1 | 1 |<br>+----+----------+-----------------+------+---------+------+---------------------------------+----------------------------------------+-----------+---------------+<br>6 rows in set (0.00 sec)<br> |
Updates are only completed after the backup, as described. It should be clear now why you should use it with caution.
The last thing we should discuss is if you do not want to use any “hacks” with xtrabackup , you can do things on the MySQL side such as:
In conclusion, we do not have a panacea for this issue but, you can use some of the described tricks to get rid of this problem. Thanks for reading!