What mysqldump does by default
How backup jobs lose consistency without anyone noticing
Why InnoDB tables need --single-transaction
- It only protects transactional tables. MyISAM and MEMORY tables do not take part in the snapshot and can still drift during the dump. A database with mixed engines is only partly consistent.
- It is not isolated from schema changes. If another session runs ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, or TRUNCATE TABLE during the dump, the affected table can come out wrong or the dump can fail. Plugin updates and migrations often run exactly these statements, so keep them away from the backup window.
- A long snapshot is not free. On a busy server, InnoDB has to keep old row versions until the dump finishes, and the dump still adds read I/O. --quick, also part of --opt, streams rows instead of buffering whole tables in memory.
- Options that record binary log or replication positions (--source-data on newer MySQL, --master-data on older MySQL and MariaDB) or --flush-logs take a short global read lock at the start. That is normally brief, but it is not zero.
Check what your backup job really runs
BASH
# Cron entries for the current user and system-wide (paths vary by distro)
crontab -l
sudo grep -rn 'mysqldump' /etc/cron* /var/spool/cron 2>/dev/null
# While the job is running, show the exact arguments it was started with
ps -eo pid,etime,args | grep '[m]ysqldump'
# Options mysqldump picks up from option files such as my.cnf and ~/.my.cnf
mysqldump --print-defaultsSQL
-- Count tables per engine in each application database
SELECT table_schema, engine, COUNT(*) AS tables
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
AND table_type = 'BASE TABLE'
GROUP BY table_schema, engine
ORDER BY table_schema, engine;The production pattern, and a restore test that proves it
BASH
# Dump one database with a consistent InnoDB snapshot
set -o pipefail
mysqldump --single-transaction shopdb | gzip > /backup/shopdb-$(date +%F).sql.gz
# Test restore into a separate, empty database (file name is a placeholder)
mysql -e 'CREATE DATABASE restore_test'
gunzip < /backup/shopdb-YYYY-MM-DD.sql.gz | mysql restore_test
# Illustrative check: line items whose parent order is missing (adjust table and column names)
mysql restore_test -e 'SELECT COUNT(*) FROM order_items oi LEFT JOIN orders o ON o.id = oi.order_id WHERE o.id IS NULL'