innobackupex备份源码解析

目前MySQL的物理备份大多数采用xtrabackupex进行,其备份过程如下图所示,这里通过解析 xtrabackup 的源码来详细看看其是如何进行备份的,xtrabackup 版本为 2.4.26。

 

 

 这里只解析其全量备份的过程,通过源码可以发现很多细节,其核心详细的备份流程如下:

1. 从 log group 的 log header 中获取 lastest checkpoint lsn.

2. 从 lastest checkpoint lsn 开始从 log group 中拷贝 redo log,直到没有 redo log为止,此时拷贝的 redo log 点位记为 log_copy_scanned_lsn

3. 启动后台的 redo log copy 线程,循环从 log_copy_scanned_lsn 点位开始拷贝 redo log 。

4. 获取 ibdata1, undo tablespace 和所有的 ibd 文件,根据指定的 --parallel 参数创建并发的拷贝线程,拷贝数据文件。

5. 待数据拷贝结束,则加全局读锁。这里有一个小细节,如果 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 的后台线程

6. 开始拷贝非 ibd 文件。

7. 如果设置了 slave_info,则会将SHOW SLAVE STATUS的相关信息,记录在xtrabackup_slave_info中;如果之前使用了备份锁,这里会锁定 BINLOG: LOCK BINLOG FOR BACKUP;

8. 输出 SHOW MASTER STATUS 信息到 xtrabackup_binlog_info中;

9. 执行 FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS,将 redo log持久化到磁盘供 redo log copy 线程拷贝

10. 读取最新的 checkpoint lsn,用于后续的增量备份。

11. 停止redo log拷贝线程. 将备份的元数据信息记录在XTRABACKUP_METADATA_FILENAME中,即xtrabackup_checkpoints。这里停止之后,redo log copy 线程会做最后一次 copy, 而后停止。

12. 释放全局读锁,UNLOCK BINLOG(如果之前锁定了BINLOG) & UNLOCK TABLES,生成back-my.cnf 配置文件,将备份相关信息记录到 xtrabackup_info 文件中。

13. 结束。

其核心代码如下:

/**
 * xtrabackup 入口函数
*/
int main(int argc, char **argv)
{
	...

	/* --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);
	}

	...
}

  

/**
 * 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。
	*/
	os_thread_id_t log_copying_thread_id;
	datafiles_iter_t *it;

	log_hdr_buf_ = static_cast<byte *>
		(ut_malloc_nokey(LOG_FILE_HDR_SIZE + UNIV_PAGE_SIZE_MAX));
	log_hdr_buf = static_cast<byte *>
		(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);

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);

	/* 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);
	}
skip_last_cp:
	/* stop log_copying_thread 
	停止redo log拷贝线程. 将备份的元数据信息记录在XTRABACKUP_METADATA_FILENAME中,即xtrabackup_checkpoints。
log_copying = FALSE 后, 后台的 redo log copy 线程会做最后一次 copy。 */ 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); /*0.2 sec*/ } msg("\n"); 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); } }

  

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);
}

  

/**
 * 调用 backup_start() 函数, 这个函数会加全局读锁, 拷贝非 ibd 文件
*/
bool
backup_start()
{
	// opt_no_lock 指的是 no-lock 参数
	if (!opt_no_lock) {
		/* 如果指定了--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);
		}

		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);
}

 

/*********************************************************************//**
加全局读锁, 如果支持备份锁, 且没有设置 --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);
}

  

/* 调用backup_finish函数,这个函数会释放全局读锁 */
bool
backup_finish()
{
	/* release all locks 
	释放所有锁, 如果锁定了 binlog, 还会解锁 binlog。
        这里执行 UNLOCK BINLOG;& UNLOCK TABLES;
	*/
	if (!opt_no_lock) {
		unlock_all(mysql_connection);
		history_lock_time = time(NULL) - history_lock_time;
	} else {
		history_lock_time = 0;
	}
	/**
	 * 如果设置了 --safe-slave-backup, 且 SQL 线程停止了, 则会开启 SQL 线程
	*/
	if (opt_safe_slave_backup && sql_thread_started) {
		msg("Starting slave SQL thread\n");
		xb_mysql_query(mysql_connection,
				"START SLAVE SQL_THREAD", false);
	}

	/* 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);
}

  

posted @ 2022-06-15 17:54  卷毛狒狒  阅读(419)  评论(0编辑  收藏  举报