1
Part1 引言
在日常数据库运维中,经常要对数据库进行热备。热备的一个关键点是保证数据的一致性,即在备份进行时发生的数据更改,不会在备份结果中出现。mysqldump是实际场景中最常使用的备份工具之一,通过选择合适的选项做备份,mysqldump可以保证数据的一致性,同时尽可能保证进行中的业务不受影响。
那么mysqldump是如何实现一致性备份的?以下我将结合mysqldump过程中mysqld生成的general log与mysqldump的源码来解释mysqldump一致性备份的原理。
注:以下的实例基于MySQL 8.0.18,在不同版本上mysqldump的部分实现会有不同
首先用mysqldump执行一次一致性备份:
$ mysqldump -uroot -p --skip-opt --default-character-set=utf8 --single-transaction --master-data=2 --no-autocommit -B d1> backup.sql
复制
1. 关键参数解释:
-single-transaction:执行一致性备份;
-master-data=2:要求dump结果中以注释形式保存备份时的binlog位置信息;
-B:指定要dump的数据库,在这里d1是一个使用InnoDB作为存储引擎的库,其中只有一个表t1。

2. mysqldump一致性备份的主要执行流程
1
连接server;
两次关闭所有表,第二次关表同时加读锁;
设置隔离级别为“可重复读”,开始事务并创建快照;
获取当前binlog位置;
解锁所有表;
对指定的库与表进行dump。
1
Part2 流程剖析
1. 连接server

if (connect_to_db(current_host, current_user, opt_password)) { free_resources(); exit(EX_MYSQLERR);}
复制
2. 两次关闭所有表,第二次关表同时加读锁

if ((opt_lock_all_tables || opt_master_data || (opt_single_transaction && flush_logs)) && do_flush_tables_read_lock(mysql)) goto err;
复制
通过--lock-all-tables选项显式要求给所有表加锁。 通过--master-data选项要求dump出来的结果中包含binlog位置。 通过--single-transaction指定了进行单事务的一致性备份,同时通过--flush-logs要求刷新log文件。
static int do_flush_tables_read_lock(MYSQL *mysql_con) { return (mysql_query_with_error_report( mysql_con, 0, ((opt_master_data != 0) ? "FLUSH *!40101 LOCAL */ TABLES" : "FLUSH TABLES")) || mysql_query_with_error_report(mysql_con, 0, "FLUSH TABLES WITH READ LOCK"));}
复制
3. 设置隔离级别为“可重复读”,开始事务并创建快照

if (opt_single_transaction && start_transaction(mysql)) goto err;
复制
static int start_transaction(MYSQL *mysql_con) { 省略部分非关键代码与注释 return ( mysql_query_with_error_report(mysql_con, 0, "SET SESSION TRANSACTION ISOLATION " "LEVEL REPEATABLE READ") || mysql_query_with_error_report(mysql_con, 0, "START TRANSACTION " "/*!40100 WITH CONSISTENT SNAPSHOT */"));}
复制
4. 获取当前binlog位置

if (opt_master_data && do_show_master_status(mysql)) goto err;
复制
static int do_show_master_status(MYSQL *mysql_con) { MYSQL_ROW row; MYSQL_RES *master; const char *comment_prefix = (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) { return 1; } else { row = mysql_fetch_row(master); if (row && row[0] && row[1]) { print_comment(md_result_file, 0, "\n--\n-- Position to start replication or point-in-time " "recovery from\n--\n\n"); // 写入dump结果 fprintf(md_result_file, "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", comment_prefix, row[0], row[1]); check_io(md_result_file); } // ... } return 0;}
复制
5. 解锁所有表

if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ goto err;
复制
6. 对指定的库与表进行dump

static int dump_databases(char **db_names) { int result = 0; char **db; DBUG_TRACE; for (db = db_names; *db; db++) { if (is_infoschema_db(*db)) die(EX_USAGE, "Dumping \'%s\' DB content is not supported", *db); if (dump_all_tables_in_db(*db)) result = 1; } if (!result && seen_views) { for (db = db_names; *db; db++) { if (dump_all_views_in_db(*db)) result = 1; } } return result;} /* dump_databases */
复制
// 创建savepoint if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { verbose_msg("-- Setting savepoint...\n"); if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp")) return 1; } while ((table = getTableName(0))) { char *end = my_stpcpy(afterdot, table); if (include_table(hash_key, end - hash_key)) { dump_table(table, database); // 对表进行dump // 省略部分代码... // ROLLBACK操作 /** ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata lock on table which was already dumped. This allows to avoid blocking concurrent DDL on this table without sacrificing correctness, as we won't access table second time and dumps created by --single-transaction mode have validity point at the start of transaction anyway. Note that this doesn't make --single-transaction mode with concurrent DDL safe in general case. It just improves situation for people for whom it might be working. */ if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) { verbose_msg("-- Rolling back to savepoint sp...\n"); if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp")) maybe_exit(EX_MYSQLERR); }
复制
1
Part3 小结
1
Part4 扩展阅读—Percona的实现
1. Backup Lock
阻塞对MyISAM, MEMORY, CSV, ARCHIVE表的更新操作; 阻塞对任何表的DDL操作; 不阻塞对临时表与log表的更新操作。
if (opt_lock_all_tables || (opt_master_data && (!has_consistent_binlog_pos || !has_consistent_gtid_executed)) || (opt_single_transaction && flush_logs)) { if (do_flush_tables_read_lock(mysql)) goto err; ftwrl_done = true; } else if (opt_lock_for_backup && do_lock_tables_for_backup(mysql)) goto err;
复制
static int do_lock_tables_for_backup(MYSQL *mysql_con) noexcept { return mysql_query_with_error_report(mysql_con, 0, "LOCK TABLES FOR BACKUP");}
复制
2. Binlog Snapshot
mysqldump — A Database Backup Program Introducing backup locks in Percona Server
特惠体验云数据库
↓↓更多惊喜优惠请点这儿~
文章转载自腾讯云数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
【MySQL 30周年庆】MySQL 8.0 OCP考试限时免费!教你免费领考券
墨天轮小教习
877次阅读
2025-04-25 18:53:11
MySQL 30 周年庆!MySQL 8.4 认证免费考!这次是认真的。。。
严少安
540次阅读
2025-04-25 15:30:58
墨天轮个人数说知识点合集
JiekeXu
452次阅读
2025-04-01 15:56:03
MySQL数据库当前和历史事务分析
听见风的声音
429次阅读
2025-04-01 08:47:17
MySQL 生产实践-Update 二级索引导致的性能问题排查
chengang
395次阅读
2025-03-28 16:28:31
【活动】分享你的压箱底干货文档,三篇解锁进阶奖励!
墨天轮编辑部
372次阅读
2025-04-17 17:02:24
MySQL 9.3 正式 GA,我却大失所望,新特性亮点与隐忧并存?
JiekeXu
358次阅读
2025-04-15 23:49:58
3月“墨力原创作者计划”获奖名单公布
墨天轮编辑部
336次阅读
2025-04-15 14:48:05
openHalo问世,全球首款基于PostgreSQL兼容MySQL协议的国产开源数据库
严少安
309次阅读
2025-04-07 12:14:29
记录MySQL数据库的一些奇怪的迁移需求!
陈举超
207次阅读
2025-04-15 15:27:53