目前MySQL的物理备份大多数采用xtrabackupex进行,其备份过程如下图所示,这里通过解析 xtrabackup 的源码来详细看看其是如何进行备份的,xtrabackup 版本为 2.4.26。
这里只解析其全量备份的过程,通过源码可以发现很多细节,其核心详细的备份流程如下:
-
从 log group 的 log header 中获取 lastest checkpoint lsn.
-
从 lastest checkpoint lsn 开始从 log group 中拷贝 redo log,直到没有 redo log为止,此时拷贝的 redo log 点位记为 log_copy_scanned_lsn
-
启动后台的 redo log copy 线程,循环从 log_copy_scanned_lsn 点位开始拷贝 redo log 。
-
获取 ibdata1, undo tablespace 和所有的 ibd 文件,根据指定的 –parallel 参数创建并发的拷贝线程,拷贝数据文件。
-
待数据拷贝结束,则加全局读锁。这里有一个小细节,如果 MySQL 支持备份锁,且没有指定 –no-backup-locks,则会优先使用备份锁:LOCK TABLES FOR BACKUP;
如果不能使用备份锁,则走正常的加锁流程,这其中包含 xtrabackupex 防止阻塞逻辑。
5.1. 如果没有指定 kill-long-queries-timeout & ftwrl-wait-timeout,则首先执行 FLUSH NO_WRITE_TO_BINLOG TABLES;这是为了防止因为存在 long update 操作而导致 FTWRL操作阻塞整个MySQL服务。当存在 long update操作,FLUSH TABLES 将被阻塞,但是整个 mysql 服务不会被阻塞。当 long update结束,FTWRL操作会很快结束。
5.2. 接着,针对 ftwrl-wait-timeout & ftwrl-wait-threshold 参数,ftwrl-wait-threshold 为认定操作未 long update 操作的阈值,针对 long update操作,xtrabackup 最多再等待 ftwrl-wait-timeout,如果超时 long update操作尚未结束,则 xtrabackup 直接退出。
5.3. 紧接着处理 kill-long-queries-timeout 参数,启动一个后台线程,监控当前数据库的所有操作;该参数为开始 FTWRL 到 KILL 掉阻塞该操作的 query之间的耗时。
5.4. FLUSH TABLES WITH READ LOCK。
5.5. 关闭处理 kill-long-queries-timeout 的后台线程
-
开始拷贝非 ibd 文件。
-
如果设置了 slave_info,则会将SHOW SLAVE STATUS的相关信息,记录在xtrabackup_slave_info中;如果之前使用了备份锁,这里会锁定 BINLOG: LOCK BINLOG FOR BACKUP;
-
输出 SHOW MASTER STATUS 信息到 xtrabackup_binlog_info中;
-
执行 FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS,将 redo log持久化到磁盘供 redo log copy 线程拷贝
-
读取最新的 checkpoint lsn,用于后续的增量备份。
-
停止redo log拷贝线程. 将备份的元数据信息记录在XTRABACKUP_METADATA_FILENAME中,即xtrabackup_checkpoints。这里停止之后,redo log copy 线程会做最后一次 copy, 而后停止。
-
释放全局读锁,UNLOCK BINLOG(如果之前锁定了BINLOG) & UNLOCK TABLES,生成back-my.cnf 配置文件,将备份相关信息记录到 xtrabackup_info 文件中。
-
结束。
其核心代码如下:
cpp;gutter:true;
/<strong>
* xtrabackup 入口函数
*/
int main(int argc, char </strong>argv)
{
...</p>
<pre><code>/* --backup
backup 逻辑, 这里主要关注 backup
*/
if (xtrabackup_backup) {
xtrabackup_backup_func();
}
/* --stats */
if (xtrabackup_stats) {
xtrabackup_stats_func(server_argc, server_defaults);
}
/* --prepare */
if (xtrabackup_prepare) {
xtrabackup_prepare_func(server_argc, server_defaults);
}
...
</code></pre>
<p>}</p>
<pre><code>
;gutter:true;
/**
* backup 逻辑
*/
void
xtrabackup_backup_func(void)
{
/* start back ground thread to copy newer log
创建 redo log 后台拷贝线程。可以看到,其首先在主线程中从 lastest chechkpoint lsn 点位开始拷贝 log group 中的 redo log,直到拷贝完成,拷贝完成的点位记为log_copy_scanned_lsn ,而后再启动后台的 redo log copy 线程,从log_copy_scanned_lsn位置继续拷贝 redo log。
cpp;gutter:true;
<em>/
os_thread_id_t log_copying_thread_id;
datafiles_iter_t </em>it;</p>
<pre><code>log_hdr_buf_ = static_cast
(ut_malloc_nokey(LOG_FILE_HDR_SIZE + UNIV_PAGE_SIZE_MAX));
log_hdr_buf = static_cast
(ut_align(log_hdr_buf_, UNIV_PAGE_SIZE_MAX));
/* get current checkpoint_lsn
获取当前的 checkpoint_lsn
*/
/* Look for the latest checkpoint from any of the log groups
获取最新的 checkpoint lsn
*/
mutex_enter(&log_sys->mutex);
err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
if (err != DB_SUCCESS) {
ut_free(log_hdr_buf_);
exit(EXIT_FAILURE);
}
// 读取 log group header page
log_group_header_read(max_cp_group, max_cp_field);
buf = log_sys->checkpoint_buf;
// 读取 checkpoint_lsn 和 checkpoint_no
checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN);
checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
mutex_exit(&log_sys->mutex);
</code></pre>
<p>reread_log_header:
fil_io(IORequest(IORequest::READ), true,
page_id_t(max_cp_group->space_id, 0),
univ_page_size,
0, LOG_FILE_HDR_SIZE,
log_hdr_buf, max_cp_group);</p>
<pre><code>/* check consistency of log file header to copy */
mutex_enter(&log_sys->mutex);
err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
if (err != DB_SUCCESS) {
ut_free(log_hdr_buf_);
exit(EXIT_FAILURE);
}
log_group_header_read(max_cp_group, max_cp_field);
buf = log_sys->checkpoint_buf;
if(checkpoint_no_start != mach_read_from_8(buf + LOG_CHECKPOINT_NO)) {
checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN);
checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
mutex_exit(&log_sys->mutex);
goto reread_log_header;
}
mutex_exit(&log_sys->mutex);
xtrabackup_init_datasinks();
if (!select_history()) {
exit(EXIT_FAILURE);
}
/* open the log file
打开 xtrabackup_logfile
*/
memset(&stat_info, 0, sizeof(MY_STAT));
dst_log_file = ds_open(ds_redo, XB_LOG_FILENAME, &stat_info);
if (dst_log_file == NULL) {
msg("xtrabackup: error: failed to open the target stream for "
"'%s'.\n", XB_LOG_FILENAME);
ut_free(log_hdr_buf_);
exit(EXIT_FAILURE);
}
/* label it
在 xtrabackup_logfile 头部写入 lobel 信息:
*/
strcpy((char*) log_hdr_buf + LOG_HEADER_CREATOR, "xtrabkup ");
ut_sprintf_timestamp(
(char*) log_hdr_buf + (LOG_HEADER_CREATOR
+ (sizeof "xtrabkup ") - 1));
if (ds_write(dst_log_file, log_hdr_buf, LOG_FILE_HDR_SIZE)) {
msg("xtrabackup: error: write to logfile failed\n");
ut_free(log_hdr_buf_);
exit(EXIT_FAILURE);
}
ut_free(log_hdr_buf_);
/* start flag */
log_copying = TRUE;
/* start io throttle */
if(xtrabackup_throttle) {
os_thread_id_t io_watching_thread_id;
io_ticket = xtrabackup_throttle;
wait_throttle = os_event_create("wait_throttle");
os_thread_create(io_watching_thread, NULL,
&io_watching_thread_id);
}
mutex_enter(&log_sys->mutex);
xtrabackup_choose_lsn_offset(checkpoint_lsn_start);
mutex_exit(&log_sys->mutex);
if (opt_lock_ddl_per_table) {
mdl_lock_tables();
}
/* copy log file by current position
从最新的 checkpoint_lsn 开始拷贝 redo log; 拷贝到最新 redo log 结束, 停止拷贝, 停止拷贝点位: log_copy_scanned_lsn
*/
if(xtrabackup_copy_logfile(checkpoint_lsn_start, FALSE))
exit(EXIT_FAILURE);
/*
* From this point forward, recv_parse_or_apply_log_rec_body should fail if
* MLOG_INDEX_LOAD event is parsed as its not safe to continue the backup
* in any situation (with or without --lock-ddl-per-table).
*/
mdl_taken = true;
log_copying_stop = os_event_create("log_copying_stop");
debug_sync_point("xtrabackup_pause_after_redo_catchup");
// 创建 redo log 后台拷贝线程
os_thread_create(log_copying_thread, NULL, &log_copying_thread_id);
/* Populate fil_system with tablespaces to copy
获取 ibdata1, undo tablespace 和所有的 ibd 文件
*/
err = xb_load_tablespaces();
if (err != DB_SUCCESS) {
msg("xtrabackup: error: xb_load_tablespaces() failed with"
"error code %lu\n", err);
exit(EXIT_FAILURE);
}
/* FLUSH CHANGED_PAGE_BITMAPS call */
if (!flush_changed_page_bitmaps()) {
exit(EXIT_FAILURE);
}
debug_sync_point("xtrabackup_suspend_at_start");
if (xtrabackup_incremental) {
if (!xtrabackup_incremental_force_scan &&
have_changed_page_bitmaps) {
changed_page_bitmap = xb_page_bitmap_init();
}
if (!changed_page_bitmap) {
msg("xtrabackup: using the full scan for incremental "
"backup\n");
} else if (incremental_lsn != checkpoint_lsn_start) {
/* Do not print that bitmaps are used when dummy bitmap
is build for an empty LSN range. */
msg("xtrabackup: using the changed page bitmap\n");
}
}
ut_a(xtrabackup_parallel > 0);
if (xtrabackup_parallel > 1) {
msg("xtrabackup: Starting %u threads for parallel data "
"files transfer\n", xtrabackup_parallel);
}
it = datafiles_iter_new(f_system);
if (it == NULL) {
msg("xtrabackup: Error: datafiles_iter_new() failed.\n");
exit(EXIT_FAILURE);
}
/* Create data copying threads
创建数据拷贝线程
*/
data_threads = (data_thread_ctxt_t *)
ut_malloc_nokey(sizeof(data_thread_ctxt_t) *
xtrabackup_parallel);
count = xtrabackup_parallel;
mutex_create(LATCH_ID_XTRA_COUNT_MUTEX, &count_mutex);
// 拷贝物理文件, 其中, xtrabackup_parallel 是拷贝并发线程数, 由 --parallel 参数指定
for (i = 0; i < (uint) xtrabackup_parallel; i++) {
data_threads[i].it = it;
data_threads[i].num = i+1;
data_threads[i].count = &count;
data_threads[i].count_mutex = &count_mutex;
data_threads[i].error = &data_copying_error;
// 创建数据拷贝线程
os_thread_create(data_copy_thread_func, data_threads + i,
&data_threads[i].id);
}
/* Wait for threads to exit
循环等待, 直到数据拷贝结束
*/
while (1) {
os_thread_sleep(1000000);
mutex_enter(&count_mutex);
if (count == 0) {
mutex_exit(&count_mutex);
break;
}
mutex_exit(&count_mutex);
}
mutex_free(&count_mutex);
ut_free(data_threads);
datafiles_iter_free(it);
if (data_copying_error) {
exit(EXIT_FAILURE);
}
if (changed_page_bitmap) {
xb_page_bitmap_deinit(changed_page_bitmap);
}
}
// 调用 backup_start() 函数, 这个函数会加全局读锁, 拷贝非 ibd 文件
if (!backup_start()) {
exit(EXIT_FAILURE);
}
if(opt_lock_ddl_per_table && opt_debug_sleep_before_unlock){
msg_ts("Debug sleep for %u seconds\n",
opt_debug_sleep_before_unlock);
os_thread_sleep(opt_debug_sleep_before_unlock * 1000000);
}
/* read the latest checkpoint lsn
读取最新的 checkpoint lsn, 用于后续的增量备份
*/
latest_cp = 0;
{
log_group_t* max_cp_group;
ulint max_cp_field;
ulint err;
mutex_enter(&log_sys->mutex);
err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
if (err != DB_SUCCESS) {
msg("xtrabackup: Error: recv_find_max_checkpoint() failed.\n");
mutex_exit(&log_sys->mutex);
goto skip_last_cp;
}
log_group_header_read(max_cp_group, max_cp_field);
xtrabackup_choose_lsn_offset(checkpoint_lsn_start);
latest_cp = mach_read_from_8(log_sys->checkpoint_buf +
LOG_CHECKPOINT_LSN);
mutex_exit(&log_sys->mutex);
msg("xtrabackup: The latest check point (for incremental): "
"'" LSN_PF "'\n", latest_cp);
}
</code></pre>
<p>skip_last_cp:
/<em> stop log_copying_thread
停止redo log拷贝线程. 将备份的元数据信息记录在XTRABACKUP_METADATA_FILENAME中,即xtrabackup_checkpoints。 log_copying = FALSE 后, 后台的 redo log copy 线程会做最后一次 copy。
</em>/
log_copying = FALSE;
os_event_set(log_copying_stop);
msg("xtrabackup: Stopping log copying thread.\n");
while (log_copying_running) {
msg(".");
os_thread_sleep(200000); /<em>0.2 sec</em>/
}
msg("\n");</p>
<pre><code>os_event_destroy(log_copying_stop);
if (ds_close(dst_log_file)) {
exit(EXIT_FAILURE);
}
if (!validate_missing_encryption_tablespaces()) {
exit(EXIT_FAILURE);
}
if(!xtrabackup_incremental) {
strcpy(metadata_type, "full-backuped");
metadata_from_lsn = 0;
} else {
strcpy(metadata_type, "incremental");
metadata_from_lsn = incremental_lsn;
}
metadata_to_lsn = latest_cp;
metadata_last_lsn = log_copy_scanned_lsn;
if (!xtrabackup_stream_metadata(ds_meta)) {
msg("xtrabackup: Error: failed to stream metadata.\n");
exit(EXIT_FAILURE);
}
/* 调用backup_finish函数,这个函数会释放全局读锁 */
if (!backup_finish()) {
exit(EXIT_FAILURE);
}
if (xtrabackup_extra_lsndir) {
char filename[FN_REFLEN];
sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
XTRABACKUP_METADATA_FILENAME);
if (!xtrabackup_write_metadata(filename)) {
msg("xtrabackup: Error: failed to write metadata "
"to '%s'.\n", filename);
exit(EXIT_FAILURE);
}
sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
XTRABACKUP_INFO);
if (!xtrabackup_write_info(filename)) {
msg("xtrabackup: Error: failed to write info "
"to '%s'.\n", filename);
exit(EXIT_FAILURE);
}
}
if (opt_lock_ddl_per_table) {
mdl_unlock_all();
}
if (opt_transition_key != NULL || opt_generate_transition_key) {
if (!xb_tablespace_keys_dump(ds_data, opt_transition_key,
opt_transition_key != NULL ?
strlen(opt_transition_key) : 0)) {
msg("xtrabackup: Error: failed to dump "
"tablespace keys.\n");
exit(EXIT_FAILURE);
}
}
xtrabackup_destroy_datasinks();
if (wait_throttle) {
/* wait for io_watching_thread completion */
while (io_watching_thread_running) {
os_thread_sleep(1000000);
}
os_event_destroy(wait_throttle);
wait_throttle = NULL;
}
msg("xtrabackup: Transaction log of lsn (" LSN_PF ") to (" LSN_PF
") was copied.\n", checkpoint_lsn_start, log_copy_scanned_lsn);
xb_filters_free();
xb_data_files_close();
recv_sys_debug_free();
log_shutdown();
trx_pool_close();
lock_sys_close();
os_thread_free();
row_mysql_close();
sync_check_close();
xb_keyring_shutdown();
/* Make sure that the latest checkpoint made it to xtrabackup_logfile */
if (latest_cp > log_copy_scanned_lsn) {
msg("xtrabackup: error: last checkpoint LSN (" LSN_PF
") is larger than last copied LSN (" LSN_PF ").\n",
latest_cp, log_copy_scanned_lsn);
exit(EXIT_FAILURE);
}
</code></pre>
<p>}</p>
<pre><code>
;gutter:true;
static
#ifndef __WIN__
void*
#else
ulint
#endif
log_copying_thread(
void* arg __attribute__((unused)))
{
/*
Initialize mysys thread-specific memory so we can
use mysys functions in this thread.
*/
my_thread_init();
ut_a(dst_log_file != NULL);
log_copying_running = TRUE;
while(log_copying) {
os_event_reset(log_copying_stop);
os_event_wait_time_low(log_copying_stop,
xtrabackup_log_copy_interval * 1000ULL,
0);
if (log_copying) {
if(xtrabackup_copy_logfile(log_copy_scanned_lsn,
FALSE)) {
exit(EXIT_FAILURE);
}
}
}
/* last copying */
if(xtrabackup_copy_logfile(log_copy_scanned_lsn, TRUE)) {
exit(EXIT_FAILURE);
}
log_copying_running = FALSE;
my_thread_end();
os_thread_exit();
return(0);
}
cpp;gutter:true;
/<em><em>
* 调用 backup_start() 函数, 这个函数会加全局读锁, 拷贝非 ibd 文件
</em>/
bool
backup_start()
{
// opt_no_lock 指的是 no-lock 参数
if (!opt_no_lock) {
/</em> 如果指定了--safe-slave-backup,会关闭SQL线程,等待Slave_open_temp_tables变量为0。
如果使用的是statement格式,且使用了临时表,建议设置--safe-slave-backup。
对于row格式,无需指定该选项 */
if (opt_safe_slave_backup) {
if (!wait_for_safe_slave(mysql_connection)) {
return(false);
}
}
// 调用 backup_files 备份非 ibd 文件, 加了全局读锁还会调用一次.
// 这一次, 实际上针对的是 --rsync 方式。 这里不做深入研究。
if (!backup_files(fil_path_to_mysql_datadir, true)) {
return(false);
}</p>
<pre><code> history_lock_time = time(NULL);
// 加全局读锁, 如果支持备份锁, 且没有设置 --no-backup-locks, 会优先使用备份锁。
if (!lock_tables_maybe(mysql_connection,
opt_backup_lock_timeout,
opt_backup_lock_retry_count)) {
return(false);
}
}
// 备份非 ibd 文件
if (!backup_files(fil_path_to_mysql_datadir, false)) {
return(false);
}
// There is no need to stop slave thread before coping non-Innodb data when
// --no-lock option is used because --no-lock option requires that no DDL or
// DML to non-transaction tables can occur.
if (opt_no_lock) {
if (opt_safe_slave_backup) {
if (!wait_for_safe_slave(mysql_connection)) {
return(false);
}
}
}
// 如果设置了 slave_info, 会将SHOW SLAVE STATUS的相关信息,记录在xtrabackup_slave_info中
if (opt_slave_info) {
/* 如果之前使用了备份锁,这里会先锁定Binlog(LOCK BINLOG FOR BACKUP)*/
lock_binlog_maybe(mysql_connection, opt_backup_lock_timeout,
opt_backup_lock_retry_count);
if (!write_slave_info(mysql_connection)) {
return(false);
}
}
/* The only reason why Galera/binlog info is written before
wait_for_ibbackup_log_copy_finish() is that after that call the xtrabackup
binary will start streamig a temporary copy of REDO log to stdout and
thus, any streaming from innobackupex would interfere. The only way to
avoid that is to have a single process, i.e. merge innobackupex and
xtrabackup. */
if (opt_galera_info) {
if (!write_galera_info(mysql_connection)) {
return(false);
}
write_current_binlog_file(mysql_connection);
}
/* 如果--binlog-info设置的是ON(默认是AUTO),则会将SHOW MASTER STATUS的相关信息,记录在xtrabackup_binlog_info中 */
if (opt_binlog_info == BINLOG_INFO_ON) {
lock_binlog_maybe(mysql_connection, opt_backup_lock_timeout,
opt_backup_lock_retry_count);
write_binlog_info(mysql_connection);
}
// 执行 FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS, 将 redo log 持久化到磁盘共 redo log copy 线程拷贝。
if (have_flush_engine_logs) {
msg_ts("Executing FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS...\n");
xb_mysql_query(mysql_connection,
"FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS", false);
}
return(true);
</code></pre>
<p>}</p>
<pre><code>
;gutter:true;
/*********************************************************************//**
加全局读锁, 如果支持备份锁, 且没有设置 –no-backup-locks, 会优先使用备份锁
Function acquires either a backup tables lock, if supported
by the server, or a global read lock (FLUSH TABLES WITH READ LOCK)
otherwise.
@returns true if lock acquired */
bool
lock_tables_maybe(MYSQL *connection, int timeout, int retry_count)
{
if (tables_locked || opt_lock_ddl_per_table) {
return(true);
}
// 如果指定了 –no-backup-locks, 则使用备份锁。目前, mysql5.7.27 不支持备份锁
if (have_backup_locks) {
return lock_tables_for_backup(connection, timeout, retry_count);
}
// lock_wait_timeout
if (have_lock_wait_timeout) {
char query[200];
ut_snprintf(query, sizeof(query),
"SET SESSION lock_wait_timeout=%d", timeout);
xb_mysql_query(connection, query, false);
}
// 没有 lock_wait_timeout & kill_long_query_timeout
if (!opt_lock_wait_timeout && !opt_kill_long_queries_timeout) {
/*
首先 FLUSH TABLES. 如果 long update 正在进行, 那么 FLUSH TABLES 将等待, 但不会暂停整个 mysqld 服务,
当 long update 完成时, FLUSH TABLES WITH READ LOCK 将启动被很快成功。
因此, FLUSH TABLES 是为了降低 mysqldump 和大多数客户端连接都暂停的可能性。 FLUSH TABLES 语句被某个表阻塞不影响其他表的操作,
例如 table A 正在进行 long update, 那么 flush tables 将被该 long update 阻塞, 但是 FLUSH tables 阻塞过程中 table B 可以进行操作。
然而, 如果在两次刷新之间进行了 long update, 那将出现长时间的暂停。
lock_wait_timeout 选项具有相同的用途, 与该技巧不兼容。
*/
msg_ts("Executing FLUSH NO_WRITE_TO_BINLOG TABLES…\n");
// 执行 FLUSH NO_WRITE_TO_BINLOG TABLES, 这是 FLUSH TABLES 操作, 该语句不写入 binlog
// 关闭所有打开的表, 强制关闭所有打开正在使用的表,
xb_mysql_query(connection,
"FLUSH NO_WRITE_TO_BINLOG TABLES", false);
}
// opt_lock_wait_timeout: 对于 long query 最多再等待 opt_lock_wait_timeout 时间, 如果该时间内 long query 执行完成,
// 则继续执行, 否则退出。
// opt_lock_wait_threshold: long query 的阈值, 对于 long query 最多再等待 lock_wait_timeout.
if (opt_lock_wait_timeout) {
if (!wait_for_no_updates(connection, opt_lock_wait_timeout,
opt_lock_wait_threshold)) {
return(false);
}
}
msg_ts("Executing FLUSH TABLES WITH READ LOCK…\n");
// opt_kill_long_queries_timeout: 从 flush tables with read lock 到 kill 掉阻塞他的操作之前等待的秒数。
if (opt_kill_long_queries_timeout) {
start_query_killer();
}
if (have_galera_enabled) {
xb_mysql_query(connection,
"SET SESSION wsrep_causal_reads=0", false);
}
/**
* FLUSH TABLES WITH READ LOCK.
* 关闭所有被打开的表, 并且使用全局读锁锁住所有库的所有表。
* 如果有事务存在, 那么事务提交时将被 hang 住, 不会回滚。
*/
xb_mysql_query(connection, "FLUSH TABLES WITH READ LOCK", false);
if (opt_kill_long_queries_timeout) {
stop_query_killer();
}
tables_locked = true;
return(true);
}
cpp;gutter:true;
/<em> 调用backup_finish函数,这个函数会释放全局读锁 </em>/
bool
backup_finish()
{
/<em> release all locks
释放所有锁, 如果锁定了 binlog, 还会解锁 binlog。
这里执行 UNLOCK BINLOG;& UNLOCK TABLES;
</em>/
if (!opt_no_lock) {
unlock_all(mysql_connection);
history_lock_time = time(NULL) - history_lock_time;
} else {
history_lock_time = 0;
}
/*<em>
* 如果设置了 --safe-slave-backup, 且 SQL 线程停止了, 则会开启 SQL 线程
</em>/
if (opt_safe_slave_backup && sql_thread_started) {
msg("Starting slave SQL thread\n");
xb_mysql_query(mysql_connection,
"START SLAVE SQL_THREAD", false);
}</p>
<pre><code>/* Copy buffer pool dump or LRU dump
拷贝 ib_buffer_pool 文件和 ib_lru_dump 文件
*/
if (!opt_rsync) {
if (opt_dump_innodb_buffer_pool) {
check_dump_innodb_buffer_pool(mysql_connection);
}
if (buffer_pool_filename && file_exists(buffer_pool_filename)) {
const char *dst_name;
dst_name = trim_dotslash(buffer_pool_filename);
copy_file(ds_data, buffer_pool_filename, dst_name, 0);
}
if (file_exists("ib_lru_dump")) {
copy_file(ds_data, "ib_lru_dump", "ib_lru_dump", 0);
}
if (file_exists("ddl_log.log")) {
copy_file(ds_data, "ddl_log.log", "ddl_log.log", 0);
}
}
msg_ts("Backup created in directory '%s'\n", xtrabackup_target_dir);
if (mysql_binlog_position != NULL) {
msg("MySQL binlog position: %s\n", mysql_binlog_position);
}
if (!mysql_slave_position.empty() && opt_slave_info) {
msg("MySQL slave binlog position: %s\n",
mysql_slave_position.c_str());
}
// 生成配置文件: back-my.cnf
if (!write_backup_config_file()) {
return(false);
}
// 将备份的相关信息记录在 xtrabackup_info 文件中
if (!write_xtrabackup_info(mysql_connection)) {
return(false);
}
return(true);
</code></pre>
<p>}
Original: https://www.cnblogs.com/juanmaofeifei/p/16375772.html
Author: 卷毛狒狒
Title: innobackupex备份源码解析
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590929/
转载文章受原作者版权保护。转载请注明原作者出处!