TWiki
>
CF Web
>
MySQLBackup
>
MySQLBackupValidation
(2015-09-11,
FraserGunn
)
(raw view)
E
dit
A
ttach
---+ CS !MySQL Database Backup and Recovery Validation for System Administrators The CS !MySQL database backup and recovery procedures should be validated periodically. ---++ Example restore of mysql.cs with verification The database backups on CS !MySQL servers use Percona innobackupex augmented with !MySQL binary logs (binlog). This example shows verification of the innobackupex backup and restore procedure by comparing before and after logical dumps using mysqldump. Additional checks during the procedure demonstrate other validation opportunities. ---+++ Overview of the procedure * Daily cron jobs on the production !MySQL server currently take an innobackupex backup and a mysqldump logical backup at different times to a local filesystem. * An hourly cron job on the backup server uses rsync to copy the local backups and the binlogs. * In this test we copy these files from the backup server to a spare unused !MySQL server. * Perform an innobackupex restore on the spare server. * Do a binlog point-in-time restore to match the time of the mysqldump logical backup. * Use mysqldump on the restored database. * Compare the old and new mysqldumps. ---+++ Detailed procedure * On the mysql remote backup host, find the latest backup files, including the mysqldump logical dump. <verbatim> ubuntu1404-202# latestmysqldump=`ls -d /mysql/backups/mysql.cs/mysqldump-*--all-databases.sql | tail -1` ubuntu1404-202# latestinnobackupex=`ls -d /mysql/backups/mysql.cs/*-innobackupex | tail -1` ubuntu1404-202# latestetcmysql=`ls -d /mysql/backups/mysql.cs/*-etcmysql.tar | tail -1` ubuntu1404-202# last2daysbinlog=`find /mysql/backups/mysql.cs/binlog* -mmin -2880` ubuntu1404-202# ls -ldtr $latestmysqldump $latestinnobackupex $latestetcmysql $last2daysbinlog -rw-rw---- 1 mysql adm 42593850 Sep 9 06:39 /mysql/backups/mysql.cs/binlog.000235 -r-------- 1 root root 20480 Sep 10 05:27 /mysql/backups/mysql.cs/mysql-172-1441877221-etcmysql.tar dr-x------ 31 root root 4096 Sep 10 05:35 /mysql/backups/mysql.cs/mysql-172-1441877221-innobackupex -r-------- 1 root root 52827191306 Sep 10 06:11 /mysql/backups/mysql.cs/mysqldump-mysql-172-1441878421--all-databases.sql -rw-rw---- 1 mysql adm 42473665 Sep 10 06:42 /mysql/backups/mysql.cs/binlog.000236 -rw-rw---- 1 mysql adm 348 Sep 10 06:42 /mysql/backups/mysql.cs/binlog.index -rw-rw---- 1 mysql adm 15965897 Sep 10 12:39 /mysql/backups/mysql.cs/binlog.000237 </verbatim> * Copy them to an unused spare mysql server. <verbatim> ubuntu1404-202# for i in $latestmysqldump $latestinnobackupex $latestetcmysql $last2daysbinlog ; do echo === $i ; time rsync -a $i marmoset_db-002.student.cs:/var/backups/mysql/mysql.cs ; done === /mysql/backups/mysql.cs/mysqldump-mysql-172-1441878421--all-databases.sql real 16m48.973s ... === /mysql/backups/mysql.cs/mysql-172-1441877221-innobackupex real 20m40.535s ... === /mysql/backups/mysql.cs/mysql-172-1441877221-etcmysql.tar real 0m0.229s ... === /mysql/backups/mysql.cs/binlog.000235 real 0m1.371s ... === /mysql/backups/mysql.cs/binlog.000236 real 0m1.323s ... === /mysql/backups/mysql.cs/binlog.000237 real 0m0.851s ... === /mysql/backups/mysql.cs/binlog.index real 0m0.305s ... ubuntu1404-202# time rsync -a $latestmysqldump $latestinnobackupex $latestetcmysql $last2daysbinlog marmoset_db-002.student.cs:/var/backups/mysql/mysql.cs real 0m0.425s ... </verbatim> * On the spare mysql server, stop mysqld. It may be running independent of the service command for testing. Make sure both are stopped. mysqld may take several seconds to shut down. <verbatim> marmoset_db-002# service mysql status mysql stop/waiting marmoset_db-002# ps -elf | grep '[m]ysql' 4 S root 4721 4527 0 80 0 - 1112 wait Sep09 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --skip-networking --read-only 4 S mysql 5254 4721 0 80 0 - 645291 poll_s Sep09 pts/0 00:05:57 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-networking --read-only --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 marmoset_db-002# kill 5254 marmoset_db-002# ps -elf | grep '[m]ysql' [nothing] </verbatim> * This host currently has no useful mysql data, configuration or logs. So, just delete them before doing the restore. <verbatim> marmoset_db-002# rm -r /var/lib/mysql && mkdir /var/lib/mysql && chown mysql:mysql /var/lib/mysql && chmod 700 /var/lib/mysql marmoset_db-002# rm -r /etc/mysql marmoset_db-002# rm /var/log/mysql/* </verbatim> * Restore the mysql configuration from the backups we just copied here. Fix the bind-address to listen only on localhost for testing. <verbatim> marmoset_db-002# tar --directory / -xf `ls -1d /var/backups/mysql/mysql.cs/*-etcmysql.tar | tail -1` marmoset_db-002# cp /etc/mysql/my.cnf /tmp/mysqlbackuptest$$ marmoset_db-002# vi /etc/mysql/my.cnf marmoset_db-002# diff -t /tmp/mysqlbackuptest$$/my.cnf /etc/mysql/my.cnf 47c47,51 < bind-address = 172.19.155.9 --- > # Was: > # bind-address = 172.19.155.9 > # ( == mysql-172.cs.uwaterloo.ca. ) > # Testing: > bind-address = 127.0.0.1 </verbatim> * Use Percona innobackupex to restore the latest full backup. * First, the backup must be "prepared" before it can be restored. This modifies the backup. So, use a temporary copy. Check for "innobackupex: completed OK!". <verbatim> marmoset_db-002# latestinnobackupex=`ls -d /var/backups/mysql/mysql.cs/*-innobackupex | tail -1` marmoset_db-002# ls -ld $latestinnobackupex dr-x------ 1 root root 944 Sep 10 05:35 /var/backups/mysql/mysql.cs/mysql-172-1441877221-innobackupex marmoset_db-002# innobackupex --use-memory=2G --apply-log $latestinnobackupex [...] [notice (again)] If you use binary log and don't use any hack of group commit, the binary log position seems to be: InnoDB: Last MySQL binlog file position 0 39749596, file name /var/log/mysql/binlog.000236 xtrabackup: starting shutdown with innodb_fast_shutdown = 1 InnoDB: FTS optimize thread exiting. InnoDB: Starting shutdown... InnoDB: Shutdown completed; log sequence number 262165949596 150910 14:37:55 innobackupex: completed OK! </verbatim> * Now, restore from the prepared backup. Check for "innobackupex: completed OK!". * (Note, this LXC container has lots of RAM and a fast !SSD array. So the local copy-back is much faster than the earlier rsync.) <verbatim> marmoset_db-002# time innobackupex --copy-back $latestinnobackupex [...] 150910 14:42:54 innobackupex: completed OK! real 1m33.205s user 0m0.476s sys 1m9.568s </verbatim> * After the --copy-back, we need to fix permissions. <verbatim> marmoset_db-002# chown -R mysql:mysql /var/lib/mysql/. </verbatim> * Check total data size. <verbatim> marmoset_db-002# du -hs $latestinnobackupex /var/lib/mysql 61G /var/backups/mysql/mysql.cs/mysql-172-1441877221-innobackupex 61G /var/lib/mysql </verbatim> * The data is now restored to the state of the last full backup. * Start mysqld to facilitate further testing. * Use --skip-networking to make sure that users and applications can't connect to the database by accident. * This assumes there are no unexpected connections from the local host, e.g. cron jobs or local unprivileged users. * Additionally, enforce read-only (except for users with the SUPER privilege). <verbatim> marmoset_db-002# ps -elf | grep '[m]ysql' [nothing] marmoset_db-002# cd /tmp marmoset_db-002# mysqld_safe --skip-networking --read-only & [1] 6058 marmoset_db-002# 150910 14:49:00 mysqld_safe Can't log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect. 150910 14:49:00 mysqld_safe Logging to '/var/log/mysql/error.log'. 150910 14:49:00 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql </verbatim> * Check mysqld is running, not listening for TCP connections, is accepting local connections, and has reasonable status. <verbatim> marmoset_db-002# ps -elf | grep '[m]ysql' 4 S root 6058 4527 0 80 0 - 1112 wait 14:49 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --skip-networking --read-only 4 S mysql 6591 6058 0 80 0 - 563321 poll_s 14:49 pts/0 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-networking --read-only --log-error=/var/log/mysql/error.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 marmoset_db-002# lsof -i | grep mysql [nothing] marmoset_db-002# mysqladmin --defaults-file=/etc/mysql/debian.cnf status Uptime: 91 Threads: 1 Questions: 2 Slow queries: 0 Opens: 33 Flush tables: 1 Open tables: 26 Queries per second avg: 0.021 </verbatim> * Check the restored database by using a point-in-time recovery to match the state of the test logical dump we did at the beginning. * First, examine the binary logs and database state. Determine which binary logs to use and the start and end positions. * Find the start position, i.e. the binary log position of the full backup we just restored. <verbatim> marmoset_db-002# cat $latestinnobackupex/xtrabackup_binlog_info binlog.000236 39752067 </verbatim> * Find and check the binary log end position that corresponds to the mysqldump. * This procedure is made complicated because the dump took 24 minutes and each database in it is in some intermediate state. * The end time of the innobackupex backup. <verbatim> marmoset_db-002# grep end_time $latestinnobackupex/xtrabackup_info end_time = 2015-09-10 05:35:19 </verbatim> * The start time of the mysqldump. <verbatim> marmoset_db-002# oldmysqldump=`ls -d /var/backups/mysql/mysql.cs/mysqldump-*.sql | tail -1` marmoset_db-002# ls -l $oldmysqldump -r-------- 1 root root 52827191306 Sep 10 06:11 /var/backups/mysql/mysql.cs/mysqldump-mysql-172-1441878421--all-databases.sql marmoset_db-002# date --date=@1441878421 Thu Sep 10 05:47:01 EDT 2015 </verbatim> * The end time of the mysqldump. <verbatim> marmoset_db-002# tail -1 $oldmysqldump -- Dump completed on 2015-09-10 6:11:07 </verbatim> * The update time of the binary logs. <verbatim> marmoset_db-002# cd /var/backups/mysql/mysql.cs/ marmoset_db-002# ls -ldtr binlog* -rw-rw---- 1 mysql adm 42593850 Sep 9 06:39 binlog.000235 -rw-rw---- 1 mysql adm 42473665 Sep 10 06:42 binlog.000236 -rw-rw---- 1 mysql adm 348 Sep 10 06:42 binlog.index -rw-rw---- 1 mysql adm 18804316 Sep 10 13:40 binlog.000237 </verbatim> * So, all the transactions of interest are in binlog.000236. * Examine the transactions between the end of the innobackupex and the start of the mysqldump. <verbatim> marmoset_db-002# mysqlbinlog binlog.000236 --start-position=39752067 --stop-datetime='2015-09-10 05:47:01' | wc 60 230 2588 </verbatim> * Examine the transactions between the end of the innobackupex and the end of the mysqldump. <verbatim> marmoset_db-002# mysqlbinlog binlog.000236 --start-position=39752067 --stop-datetime='2015-09-10 6:11:07' | wc 60 230 2588 </verbatim> * Good! There were no database updates during the mysqldump. * Apply the transactions recorded in the binary logs between the start and end positions. Compare the before and after binlog state of the new database. <verbatim> marmoset_db-002# echo 'show binary logs; show master status;' | mysql --defaults-file=/etc/mysql/debian.cnf Log_name File_size binlog.000001 107 File Position Binlog_Do_DB Binlog_Ignore_DB binlog.000001 107 marmoset_db-002# mysqlbinlog binlog.000236 --start-position=39752067 --stop-datetime='2015-09-10 05:47:01' | mysql --defaults-file=/etc/mysql/debian.cnf marmoset_db-002# echo 'show binary logs; show master status;' | mysql --defaults-file=/etc/mysql/debian.cnf Log_name File_size binlog.000001 865 File Position Binlog_Do_DB Binlog_Ignore_DB binlog.000001 865 </verbatim> * Check that the last transaction was applied. Transactions that update date fields or autoincrement fields are especially useful. <verbatim> mysqlbinlog binlog.000236 --start-position=39752067 --stop-datetime='2015-09-10 6:11:07' ... use `app_saca`/*!*/; ... SET INSERT_ID=2312/*!*/; ... INSERT INTO apply (applyid, ...) VALUES("", ...) ... marmoset_db-002# echo 'select max(applyid) from app_saca.apply' | mysql --defaults-file=/etc/mysql/debian.cnf max(applyid) 2312 </verbatim> * Create a new mysqldump of the restored content to match the old mysqldump. <verbatim> marmoset_db-002# newdump=/var/backups/mysql/`hostname | sed 's/\..*//'`-`date +%s`-mysqldump--all-databases.sql && time mysqldump --defaults-file=/etc/mysql/debian.cnf --add-drop-database --all-databases > $newdump -- Warning: Skipping the data of table mysql.event. Specify the --events option explicitly. real 15m26.000s user 11m41.136s sys 1m19.480s </verbatim> * Check that the new mysqldump's size is close to the size of the files in the database and matches the size of the old mysqldump. <verbatim> marmoset_db-002# du -hs /var/lib/mysql 61G /var/lib/mysql marmoset_db-002# ls -ltr $oldmysqldump $newdump -r-------- 1 root root 52827191306 Sep 10 06:11 /var/backups/mysql/mysql.cs/mysqldump-mysql-172-1441878421--all-databases.sql -rw-r--r-- 1 root root 52827191306 Sep 10 18:12 /var/backups/mysql/marmoset_db-002-1441922239-mysqldump--all-databases.sql </verbatim> * Verify the innobackupex backup and restore process by comparing the old and new mysqldumps. Notice that there is essentially no difference. <verbatim> marmoset_db-002# time diff --speed-large-files $oldmysqldump $newdump 3678,3679c3678,3679 < CONSTRAINT `inv_dns_ibfk_2` FOREIGN KEY (`macAddressKey`) REFERENCES `inv_macAddress` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, < CONSTRAINT `inv_dns_ibfk_1` FOREIGN KEY (`pKey`) REFERENCES `inventory` (`pKey`) ON DELETE CASCADE ON UPDATE CASCADE --- > CONSTRAINT `inv_dns_ibfk_1` FOREIGN KEY (`pKey`) REFERENCES `inventory` (`pKey`) ON DELETE CASCADE ON UPDATE CASCADE, > CONSTRAINT `inv_dns_ibfk_2` FOREIGN KEY (`macAddressKey`) REFERENCES `inv_macAddress` (`id`) ON DELETE SET NULL ON UPDATE CASCADE 56352c56352 < -- Dump completed on 2015-09-10 6:11:07 --- > -- Dump completed on 2015-09-10 18:12:45 real 29m1.590s user 1m35.648s sys 27m16.928s </verbatim>
E
dit
|
A
ttach
|
Watch
|
P
rint version
|
H
istory
: r1
|
B
acklinks
|
V
iew topic
|
WYSIWYG
|
M
ore topic actions
Topic revision: r1 - 2015-09-11
-
FraserGunn
CF
Information in this area is meant for use by CSCF staff and is not official documentation, but anybody who is interested is welcome to use it if they find it useful.
CF Web
CF Web Home
Changes
Index
Search
Administration
Communication
Email
Hardware
HelpDeskGuide
Infrastructure
InternalProjects
Linux
MachineNotes
Macintosh
Management
Networking
Printing
Research
Security
Software
Solaris
StaffStuff
TaskGroups
TermGoals
Teaching
UserSupport
Vendors
Windows
XHier
Other Webs
CSEveryBody
Main
Sandbox
TWiki
UW
My links
People
CERAS
WatForm
Tetherless lab
Ubuntu Main.HowTo
eDocs
RGG NE notes
RGG
CS infrastructure
Grad images
Edit
Copyright © 2008-2025 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback