从源码分析 XtraBackup 的备份原理

MySQL物理备份工具,常用的有两个:MySQL Enterprise Backup 和 XtraBackup。

前者常用于MySQL企业版,后者常用于MySQL社区版、Percona Server for MySQL 和 MariaDB。

所以,如果我们使用的是后三者,在实例较大的情况下,一般都会选择XtraBackup作为备份恢复工具。

熟悉一个工具,不仅仅是要了解它的用法,更重要的是掌握用法背后的原理。毕竟,用法只是”术”,原理才是”道”。所谓,明道才能优术。

了解XtraBackup的原理,比较经典的一篇文章是淘宝数据库内核日报的《Percona XtraBackup 备份原理

但看文章始终有隔靴搔痒之感,而且很多细节性的东西文章也不会提到,譬如我们比较关心的全局读锁。

下面我们就从源码的角度看看XtraBackup的备份原理,主要包括两部分:

  1. XtraBackup的备份流程。
  2. XtraBackup中全局读锁的加锁逻辑。因篇幅较长,这一部分会放到下篇文章介绍。

分析版本:XtraBackup 2.4.24

XtraBackup的备份流程

XtraBackup的main函数定义在 storage/innobase/xtrabackup/src/xtrabackup.cc 文件中。

可以看到,对于–backup选项,会调用xtrabackup_backup_func函数。

int&#xA0;main(int&#xA0;argc,&#xA0;char&#xA0;**argv)<br>{<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;/*&#xA0;--backup&#xA0;*/<br>&#xA0;if&#xA0;(xtrabackup_backup)&#xA0;{<br>&#xA0;&#xA0;xtrabackup_backup_func();<br>&#xA0;}<br><br>&#xA0;/*&#xA0;--stats&#xA0;*/<br>&#xA0;if&#xA0;(xtrabackup_stats)&#xA0;{<br>&#xA0;&#xA0;xtrabackup_stats_func(server_argc,&#xA0;server_defaults);<br>&#xA0;}<br><br>&#xA0;/*&#xA0;--prepare&#xA0;*/<br>&#xA0;if&#xA0;(xtrabackup_prepare)&#xA0;{<br>&#xA0;&#xA0;xtrabackup_prepare_func(server_argc,&#xA0;server_defaults);<br>&#xA0;}<br><br>&#xA0;if&#xA0;(xtrabackup_copy_back&#xA0;||&#xA0;xtrabackup_move_back)&#xA0;{<br>&#xA0;&#xA0;if&#xA0;(!check_if_param_set("datadir"))&#xA0;{<br>&#xA0;&#xA0;&#xA0;msg("Error:&#xA0;datadir&#xA0;must&#xA0;be&#xA0;specified.\n");<br>&#xA0;&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;mysql_mutex_init(key_LOCK_keyring_operations,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&LOCK_keyring_operations,&#xA0;MY_MUTEX_INIT_FAST);<br>&#xA0;&#xA0;if&#xA0;(!copy_back(server_argc,&#xA0;server_defaults))&#xA0;{<br>&#xA0;&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;mysql_mutex_destroy(&LOCK_keyring_operations);<br>&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;msg_ts("completed&#xA0;OK!\n");<br><br>&#xA0;exit(EXIT_SUCCESS);<br>}<br>

下面重点看看xtrabackup_backup_func函数的处理逻辑。

xtrabackup_backup_func

该函数同样位于xtrabackup.cc文件中。

void<br>xtrabackup_backup_func(void)<br>{<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;/*&#xA0;start&#xA0;back&#xA0;ground&#xA0;thread&#xA0;to&#xA0;copy&#xA0;newer&#xA0;log&#xA0;*/<br>&#xA0;/*&#xA0;&#x521B;&#x5EFA;redo&#xA0;log&#x62F7;&#x8D1D;&#x7EBF;&#x7A0B;&#xA0;*/<br>&#xA0;os_thread_id_t&#xA0;log_copying_thread_id;<br>&#xA0;datafiles_iter_t&#xA0;*it;<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;/*&#xA0;get&#xA0;current&#xA0;checkpoint_lsn&#xA0;*/<br>&#xA0;/*&#xA0;Look&#xA0;for&#xA0;the&#xA0;latest&#xA0;checkpoint&#xA0;from&#xA0;any&#xA0;of&#xA0;the&#xA0;log&#xA0;groups&#xA0;*/<br>&#xA0;/*&#xA0;&#x83B7;&#x53D6;&#x6700;&#x65B0;&#x7684;checkpoint&#xA0;lsn&#xA0;*/<br>&#xA0;mutex_enter(&log_sys->mutex);<br><br>&#xA0;err&#xA0;=&#xA0;recv_find_max_checkpoint(&max_cp_group,&#xA0;&max_cp_field);<br><br>&#xA0;if&#xA0;(err&#xA0;!=&#xA0;DB_SUCCESS)&#xA0;{<br><br>&#xA0;&#xA0;ut_free(log_hdr_buf_);<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br><br>&#xA0;log_group_header_read(max_cp_group,&#xA0;max_cp_field);<br>&#xA0;buf&#xA0;=&#xA0;log_sys->checkpoint_buf;<br><br>&#xA0;checkpoint_lsn_start&#xA0;=&#xA0;mach_read_from_8(buf&#xA0;+&#xA0;LOG_CHECKPOINT_LSN);<br>&#xA0;checkpoint_no_start&#xA0;=&#xA0;mach_read_from_8(buf&#xA0;+&#xA0;LOG_CHECKPOINT_NO);<br>&#xA0;&#xA0;&#xA0;&#xA0;...&#xA0;&#xA0;<br>&#xA0;/*&#xA0;copy&#xA0;log&#xA0;file&#xA0;by&#xA0;current&#xA0;position&#xA0;*/<br>&#xA0;/*&#xA0;&#x4ECE;&#x6700;&#x65B0;&#x7684;checkpoint&#xA0;lsn&#x5F00;&#x59CB;&#x62F7;&#x8D1D;redo&#xA0;log&#xA0;*/<br>&#xA0;if(xtrabackup_copy_logfile(checkpoint_lsn_start,&#xA0;FALSE))<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br><br>&#xA0;mdl_taken&#xA0;=&#xA0;true;<br><br>&#xA0;log_copying_stop&#xA0;=&#xA0;os_event_create("log_copying_stop");<br>&#xA0;debug_sync_point("xtrabackup_pause_after_redo_catchup");<br>&#xA0;os_thread_create(log_copying_thread,&#xA0;NULL,&#xA0;&log_copying_thread_id);<br><br>&#xA0;/*&#xA0;Populate&#xA0;fil_system&#xA0;with&#xA0;tablespaces&#xA0;to&#xA0;copy&#xA0;*/<br>&#xA0;/*&#xA0;&#x83B7;&#x53D6;ibdata1&#xFF0C;undo&#xA0;tablespaces&#x53CA;&#x6240;&#x6709;&#x7684;ibd&#x6587;&#x4EF6;&#xA0;*/<br>&#xA0;err&#xA0;=&#xA0;xb_load_tablespaces();<br>&#xA0;if&#xA0;(err&#xA0;!=&#xA0;DB_SUCCESS)&#xA0;{<br>&#xA0;&#xA0;msg("xtrabackup:&#xA0;error:&#xA0;xb_load_tablespaces()&#xA0;failed&#xA0;with"<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"error&#xA0;code&#xA0;%lu\n",&#xA0;err);<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>&#xA0;/*&#xA0;Create&#xA0;data&#xA0;copying&#xA0;threads&#xA0;*/<br>&#xA0;/*&#xA0;&#x521B;&#x5EFA;&#x6570;&#x636E;&#x62F7;&#x8D1D;&#x7EBF;&#x7A0B;&#xA0;*/<br>&#xA0;data_threads&#xA0;=&#xA0;(data_thread_ctxt_t&#xA0;*)<br>&#xA0;&#xA0;ut_malloc_nokey(sizeof(data_thread_ctxt_t)&#xA0;*<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;xtrabackup_parallel);<br>&#xA0;count&#xA0;=&#xA0;xtrabackup_parallel;<br>&#xA0;mutex_create(LATCH_ID_XTRA_COUNT_MUTEX,&#xA0;&count_mutex);<br>&#xA0;<br>&#xA0;/*&#xA0;&#x62F7;&#x8D1D;&#x7269;&#x7406;&#x6587;&#x4EF6;&#xFF0C;&#x5176;&#x4E2D;&#xFF0C;xtrabackup_parallel&#x662F;&#x62F7;&#x8D1D;&#x5E76;&#x53D1;&#x7EBF;&#x7A0B;&#x6570;&#xFF0C;&#x7531;--parallel&#x53C2;&#x6570;&#x6307;&#x5B9A;&#xA0;*/<br>&#xA0;for&#xA0;(i&#xA0;=&#xA0;0;&#xA0;i&#xA0;< (uint) xtrabackup_parallel; i++) {<br>&#xA0;&#xA0;data_threads[i].it&#xA0;=&#xA0;it;<br>&#xA0;&#xA0;data_threads[i].num&#xA0;=&#xA0;i+1;<br>&#xA0;&#xA0;data_threads[i].count&#xA0;=&#xA0;&count;<br>&#xA0;&#xA0;data_threads[i].count_mutex&#xA0;=&#xA0;&count_mutex;<br>&#xA0;&#xA0;data_threads[i].error&#xA0;=&#xA0;&data_copying_error;<br>&#xA0;&#xA0;os_thread_create(data_copy_thread_func,&#xA0;data_threads&#xA0;+&#xA0;i,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&data_threads[i].id);<br>&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;/*&#xA0;&#x5FAA;&#x73AF;&#x7B49;&#x5F85;&#xFF0C;&#x76F4;&#x5230;&#x62F7;&#x8D1D;&#x7ED3;&#x675F;&#xA0;*/<br>&#xA0;/*&#xA0;Wait&#xA0;for&#xA0;threads&#xA0;to&#xA0;exit&#xA0;*/<br>&#xA0;while&#xA0;(1)&#xA0;{<br>&#xA0;&#xA0;os_thread_sleep(1000000);<br>&#xA0;&#xA0;mutex_enter(&count_mutex);<br>&#xA0;&#xA0;if&#xA0;(count&#xA0;==&#xA0;0)&#xA0;{<br>&#xA0;&#xA0;&#xA0;mutex_exit(&count_mutex);<br>&#xA0;&#xA0;&#xA0;break;<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;mutex_exit(&count_mutex);<br>&#xA0;}<br><br>&#xA0;mutex_free(&count_mutex);<br>&#xA0;ut_free(data_threads);<br>&#xA0;datafiles_iter_free(it);<br><br>&#xA0;if&#xA0;(data_copying_error)&#xA0;{<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br><br>&#xA0;if&#xA0;(changed_page_bitmap)&#xA0;{<br>&#xA0;&#xA0;xb_page_bitmap_deinit(changed_page_bitmap);<br>&#xA0;}<br>&#xA0;}<br>&#xA0;<br>&#xA0;/*&#xA0;&#x8C03;&#x7528;backup_start&#x51FD;&#x6570;&#xFF0C;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x4F1A;&#x52A0;&#x5168;&#x5C40;&#x8BFB;&#x9501;&#xFF0C;&#x62F7;&#x8D1D;&#x975E;ibd&#x6587;&#x4EF6;&#xA0;*/<br>&#xA0;if&#xA0;(!backup_start())&#xA0;{<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br>&#xA0;if(opt_lock_ddl_per_table&#xA0;&&&#xA0;opt_debug_sleep_before_unlock){<br>&#xA0;&#xA0;msg_ts("Debug&#xA0;sleep&#xA0;for&#xA0;%u&#xA0;seconds\n",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;opt_debug_sleep_before_unlock);<br>&#xA0;&#xA0;os_thread_sleep(opt_debug_sleep_before_unlock&#xA0;*&#xA0;1000000);<br>&#xA0;}<br>&#xA0;<br>&#xA0;/*&#xA0;&#x8BFB;&#x53D6;&#x6700;&#x65B0;&#x7684;checkpoint&#xA0;lsn&#xFF0C;&#x7528;&#x4E8E;&#x540E;&#x7EED;&#x7684;&#x589E;&#x91CF;&#x5907;&#x4EFD;&#xA0;*/<br>&#xA0;/*&#xA0;read&#xA0;the&#xA0;latest&#xA0;checkpoint&#xA0;lsn&#xA0;*/<br>&#xA0;latest_cp&#xA0;=&#xA0;0;<br>&#xA0;{<br>&#xA0;&#xA0;log_group_t*&#xA0;max_cp_group;<br>&#xA0;&#xA0;ulint&#xA0;max_cp_field;<br>&#xA0;&#xA0;ulint&#xA0;err;<br><br>&#xA0;&#xA0;mutex_enter(&log_sys->mutex);<br><br>&#xA0;&#xA0;err&#xA0;=&#xA0;recv_find_max_checkpoint(&max_cp_group,&#xA0;&max_cp_field);<br><br>&#xA0;&#xA0;if&#xA0;(err&#xA0;!=&#xA0;DB_SUCCESS)&#xA0;{<br>&#xA0;&#xA0;&#xA0;msg("xtrabackup:&#xA0;Error:&#xA0;recv_find_max_checkpoint()&#xA0;failed.\n");<br>&#xA0;&#xA0;&#xA0;mutex_exit(&log_sys->mutex);<br>&#xA0;&#xA0;&#xA0;goto&#xA0;skip_last_cp;<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;log_group_header_read(max_cp_group,&#xA0;max_cp_field);<br><br>&#xA0;&#xA0;xtrabackup_choose_lsn_offset(checkpoint_lsn_start);<br><br>&#xA0;&#xA0;latest_cp&#xA0;=&#xA0;mach_read_from_8(log_sys->checkpoint_buf&#xA0;+<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;LOG_CHECKPOINT_LSN);<br><br>&#xA0;&#xA0;mutex_exit(&log_sys->mutex);<br><br>&#xA0;&#xA0;msg("xtrabackup:&#xA0;The&#xA0;latest&#xA0;check&#xA0;point&#xA0;(for&#xA0;incremental):&#xA0;"<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"'"&#xA0;LSN_PF&#xA0;"'\n",&#xA0;latest_cp);<br>&#xA0;}<br>skip_last_cp:<br>&#xA0;/*&#xA0;&#x505C;&#x6B62;redo&#xA0;log&#x62F7;&#x8D1D;&#x7EBF;&#x7A0B;.&#xA0;&#x5C06;&#x5907;&#x4EFD;&#x7684;&#x5143;&#x6570;&#x636E;&#x4FE1;&#x606F;&#x8BB0;&#x5F55;&#x5728;XTRABACKUP_METADATA_FILENAME&#x4E2D;&#xFF0C;&#x5373;xtrabackup_checkpoints&#xA0;*/<br>&#xA0;/*&#xA0;stop&#xA0;log_copying_thread&#xA0;*/<br>&#xA0;log_copying&#xA0;=&#xA0;FALSE;<br>&#xA0;os_event_set(log_copying_stop);<br>&#xA0;msg("xtrabackup:&#xA0;Stopping&#xA0;log&#xA0;copying&#xA0;thread.\n");<br>&#xA0;while&#xA0;(log_copying_running)&#xA0;{<br>&#xA0;&#xA0;msg(".");<br>&#xA0;&#xA0;os_thread_sleep(200000);&#xA0;/*0.2&#xA0;sec*/<br>&#xA0;}<br>&#xA0;msg("\n");<br><br>&#xA0;os_event_destroy(log_copying_stop);<br>&#xA0;if&#xA0;(ds_close(dst_log_file))&#xA0;{<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br><br>&#xA0;if&#xA0;(!validate_missing_encryption_tablespaces())&#xA0;{<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br><br><br>&#xA0;if(!xtrabackup_incremental)&#xA0;{<br>&#xA0;&#xA0;strcpy(metadata_type,&#xA0;"full-backuped");<br>&#xA0;&#xA0;metadata_from_lsn&#xA0;=&#xA0;0;<br>&#xA0;}&#xA0;else&#xA0;{<br>&#xA0;&#xA0;strcpy(metadata_type,&#xA0;"incremental");<br>&#xA0;&#xA0;metadata_from_lsn&#xA0;=&#xA0;incremental_lsn;<br>&#xA0;}<br>&#xA0;metadata_to_lsn&#xA0;=&#xA0;latest_cp;<br>&#xA0;metadata_last_lsn&#xA0;=&#xA0;log_copy_scanned_lsn;<br><br>&#xA0;if&#xA0;(!xtrabackup_stream_metadata(ds_meta))&#xA0;{<br>&#xA0;&#xA0;msg("xtrabackup:&#xA0;Error:&#xA0;failed&#xA0;to&#xA0;stream&#xA0;metadata.\n");<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br>&#xA0;<br>&#xA0;/*&#xA0;&#x8C03;&#x7528;backup_finish&#x51FD;&#x6570;&#xFF0C;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x4F1A;&#x91CA;&#x653E;&#x5168;&#x5C40;&#x8BFB;&#x9501;&#xA0;*/<br>&#xA0;if&#xA0;(!backup_finish())&#xA0;{<br>&#xA0;&#xA0;exit(EXIT_FAILURE);<br>&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;...<br>}<br></ (uint) xtrabackup_parallel; i++) {

该函数的处理流程如下:

  1. 创建redo log拷贝线程,从最近的checkpoint lsn开始拷贝redo log。
  2. 创建数据文件拷贝线程,拷贝ibdata1,undo tablespaces及所有的ibd文件。这里可通过设置–parallel进行多线程备份,提高物理文件的拷贝效率。不设置则默认为1。
  3. ibd文件拷贝完成后,调用backup_start函数。
  4. 停止redo log拷贝线程。
  5. 调用backup_finish函数。

接下来重点看看backup_start和backup_finish这两个函数的实现逻辑。

backup_start

该函数位于backup_copy.cc文件中。

bool<br>backup_start()<br>{<br>&#xA0;/*&#xA0;opt_no_lock&#x6307;&#x7684;&#x662F;--no-lock&#x53C2;&#x6570;&#xA0;*/<br>&#xA0;if&#xA0;(!opt_no_lock)&#xA0;{<br>&#xA0;/*&#xA0;&#x5982;&#x679C;&#x6307;&#x5B9A;&#x4E86;--safe-slave-backup&#xFF0C;&#x4F1A;&#x5173;&#x95ED;SQL&#x7EBF;&#x7A0B;&#xFF0C;&#x7B49;&#x5F85;Slave_open_temp_tables&#x53D8;&#x91CF;&#x4E3A;0&#x3002;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#x5982;&#x679C;&#x4F7F;&#x7528;&#x7684;&#x662F;statement&#x683C;&#x5F0F;&#xFF0C;&#x4E14;&#x4F7F;&#x7528;&#x4E86;&#x4E34;&#x65F6;&#x8868;&#xFF0C;&#x5EFA;&#x8BAE;&#x8BBE;&#x7F6E;--safe-slave-backup&#x3002;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#x5BF9;&#x4E8E;row&#x683C;&#x5F0F;&#xFF0C;&#x65E0;&#x9700;&#x6307;&#x5B9A;&#x8BE5;&#x9009;&#x9879;&#xA0;*/<br>&#xA0;&#xA0;if&#xA0;(opt_safe_slave_backup)&#xA0;{<br>&#xA0;&#xA0;&#xA0;if&#xA0;(!wait_for_safe_slave(mysql_connection))&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;/*&#xA0;&#x8C03;&#x7528;backup_files&#x51FD;&#x6570;&#x5907;&#x4EFD;&#x975E;ibd&#x6587;&#x4EF6;&#xFF0C;&#x52A0;&#x4E86;&#x5168;&#x5C40;&#x8BFB;&#x9501;&#x8FD8;&#x4F1A;&#x8C03;&#x7528;&#x4E00;&#x6B21;&#x3002;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#x8FD9;&#x4E00;&#x6B21;&#xFF0C;&#x5B9E;&#x9645;&#x4E0A;&#x9488;&#x5BF9;&#x7684;&#x662F;--rsync&#x65B9;&#x5F0F;&#xA0;*/<br>&#xA0;&#xA0;if&#xA0;(!backup_files(fil_path_to_mysql_datadir,&#xA0;true))&#xA0;{<br>&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;history_lock_time&#xA0;=&#xA0;time(NULL);<br>&#xA0;&#xA0;/*&#xA0;&#x52A0;&#x5168;&#x5C40;&#x8BFB;&#x9501;&#xFF0C;&#x5982;&#x679C;&#x652F;&#x6301;&#x5907;&#x4EFD;&#x9501;&#xFF0C;&#x4E14;&#x6CA1;&#x6709;&#x8BBE;&#x7F6E;--no-backup-locks&#xFF0C;&#x4F1A;&#x4F18;&#x5148;&#x4F7F;&#x7528;&#x5907;&#x4EFD;&#x9501;&#xA0;*/<br>&#xA0;&#xA0;if&#xA0;(!lock_tables_maybe(mysql_connection,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;opt_backup_lock_timeout,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;opt_backup_lock_retry_count))&#xA0;{<br>&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;}<br>&#xA0;}<br>&#xA0;/*&#xA0;&#x5907;&#x4EFD;&#x975E;ibd&#x6587;&#x4EF6;&#xA0;*/<br>&#xA0;if&#xA0;(!backup_files(fil_path_to_mysql_datadir,&#xA0;false))&#xA0;{<br>&#xA0;&#xA0;return(false);<br>&#xA0;}<br><br>&#xA0;//&#xA0;There&#xA0;is&#xA0;no&#xA0;need&#xA0;to&#xA0;stop&#xA0;slave&#xA0;thread&#xA0;before&#xA0;coping&#xA0;non-Innodb&#xA0;data&#xA0;when<br>&#xA0;//&#xA0;--no-lock&#xA0;option&#xA0;is&#xA0;used&#xA0;because&#xA0;--no-lock&#xA0;option&#xA0;requires&#xA0;that&#xA0;no&#xA0;DDL&#xA0;or<br>&#xA0;//&#xA0;DML&#xA0;to&#xA0;non-transaction&#xA0;tables&#xA0;can&#xA0;occur.<br>&#xA0;if&#xA0;(opt_no_lock)&#xA0;{<br>&#xA0;&#xA0;if&#xA0;(opt_safe_slave_backup)&#xA0;{<br>&#xA0;&#xA0;&#xA0;if&#xA0;(!wait_for_safe_slave(mysql_connection))&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;}<br>&#xA0;}<br>&#xA0;/*&#xA0;&#x5982;&#x679C;&#x8BBE;&#x7F6E;&#x4E86;--slave-info&#xFF0C;&#x4F1A;&#x5C06;SHOW&#xA0;SLAVE&#xA0;STATUS&#x7684;&#x76F8;&#x5173;&#x4FE1;&#x606F;&#xFF0C;&#x8BB0;&#x5F55;&#x5728;xtrabackup_slave_info&#x4E2D;&#xA0;*/<br>&#xA0;if&#xA0;(opt_slave_info)&#xA0;{<br>&#xA0;&#xA0;/*&#xA0;&#x5982;&#x679C;&#x4E4B;&#x524D;&#x4F7F;&#x7528;&#x4E86;&#x5907;&#x4EFD;&#x9501;&#xFF0C;&#x8FD9;&#x91CC;&#x4F1A;&#x5148;&#x9501;&#x5B9A;Binlog&#xFF08;LOCK&#xA0;BINLOG&#xA0;FOR&#xA0;BACKUP&#xFF09;*/<br>&#xA0;&#xA0;lock_binlog_maybe(mysql_connection,&#xA0;opt_backup_lock_timeout,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;opt_backup_lock_retry_count);<br><br>&#xA0;&#xA0;if&#xA0;(!write_slave_info(mysql_connection))&#xA0;{<br>&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;}<br>&#xA0;}<br><br>&#xA0;/*&#xA0;The&#xA0;only&#xA0;reason&#xA0;why&#xA0;Galera/binlog&#xA0;info&#xA0;is&#xA0;written&#xA0;before<br>&#xA0;wait_for_ibbackup_log_copy_finish()&#xA0;is&#xA0;that&#xA0;after&#xA0;that&#xA0;call&#xA0;the&#xA0;xtrabackup<br>&#xA0;binary&#xA0;will&#xA0;start&#xA0;streamig&#xA0;a&#xA0;temporary&#xA0;copy&#xA0;of&#xA0;REDO&#xA0;log&#xA0;to&#xA0;stdout&#xA0;and<br>&#xA0;thus,&#xA0;any&#xA0;streaming&#xA0;from&#xA0;innobackupex&#xA0;would&#xA0;interfere.&#xA0;The&#xA0;only&#xA0;way&#xA0;to<br>&#xA0;avoid&#xA0;that&#xA0;is&#xA0;to&#xA0;have&#xA0;a&#xA0;single&#xA0;process,&#xA0;i.e.&#xA0;merge&#xA0;innobackupex&#xA0;and<br>&#xA0;xtrabackup.&#xA0;*/<br>&#xA0;if&#xA0;(opt_galera_info)&#xA0;{<br>&#xA0;&#xA0;if&#xA0;(!write_galera_info(mysql_connection))&#xA0;{<br>&#xA0;&#xA0;&#xA0;return(false);<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;write_current_binlog_file(mysql_connection);<br>&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;/*&#xA0;&#x5982;&#x679C;--binlog-info&#x8BBE;&#x7F6E;&#x7684;&#x662F;ON&#xFF08;&#x9ED8;&#x8BA4;&#x662F;AUTO&#xFF09;&#xFF0C;&#x5219;&#x4F1A;&#x5C06;SHOW&#xA0;MASTER&#xA0;STATUS&#x7684;&#x76F8;&#x5173;&#x4FE1;&#x606F;&#xFF0C;&#x8BB0;&#x5F55;&#x5728;xtrabackup_binlog_info&#x4E2D;&#xA0;*/<br>&#xA0;if&#xA0;(opt_binlog_info&#xA0;==&#xA0;BINLOG_INFO_ON)&#xA0;{<br>&#xA0;&#xA0;lock_binlog_maybe(mysql_connection,&#xA0;opt_backup_lock_timeout,<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;opt_backup_lock_retry_count);<br>&#xA0;&#xA0;write_binlog_info(mysql_connection);<br>&#xA0;}<br><br>&#xA0;if&#xA0;(have_flush_engine_logs)&#xA0;{<br>&#xA0;&#xA0;msg_ts("Executing&#xA0;FLUSH&#xA0;NO_WRITE_TO_BINLOG&#xA0;ENGINE&#xA0;LOGS...\n");<br>&#xA0;&#xA0;xb_mysql_query(mysql_connection,<br>&#xA0;&#xA0;&#xA0;"FLUSH&#xA0;NO_WRITE_TO_BINLOG&#xA0;ENGINE&#xA0;LOGS",&#xA0;false);<br>&#xA0;}<br><br>&#xA0;return(true);<br>}

该函数的处理流程如下:

  1. 调用lock_tables_maybe函数加全局读锁。lock_tables_maybe函数的处理逻辑会在下篇文章介绍。
  2. 调用backup_files函数备份非ibd文件。具体来说,会备份以下面这些关键字作为后缀的文件。
const&#xA0;char&#xA0;*ext_list[]&#xA0;=&#xA0;{"frm",&#xA0;"isl",&#xA0;"MYD",&#xA0;"MYI",&#xA0;"MAD",&#xA0;"MAI",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;"MRG",&#xA0;"TRG",&#xA0;"TRN",&#xA0;"ARM",&#xA0;"ARZ",&#xA0;"CSM",&#xA0;"CSV",&#xA0;"opt",&#xA0;"par",<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;NULL};
  1. 如果命令行中指定了 –slave-info ,则会执行 SHOW SLAVE STATUS 获取复制的相关信息。
  2. 如果命令行中指定了 –binlog-info ,则会执行 SHOW MASTER STATU 获取 Binlog 的位置点信息。binlog-info无需显式指定,因为它的默认值为AUTO,如果开启了Binlog,则为ON。

backup_finish

该函数位于backup_copy.cc文件中。

bool<br>backup_finish()<br>{<br>&#xA0;/*&#xA0;release&#xA0;all&#xA0;locks&#xA0;*/<br>&#xA0;/*&#xA0;&#x91CA;&#x653E;&#x6240;&#x6709;&#x9501;&#xFF0C;&#x5982;&#x679C;&#x9501;&#x5B9A;&#x4E86;Binlog&#xFF0C;&#x8FD8;&#x4F1A;&#x89E3;&#x9501;Binlog&#xA0;*/<br>&#xA0;if&#xA0;(!opt_no_lock)&#xA0;{<br>&#xA0;&#xA0;unlock_all(mysql_connection);<br>&#xA0;&#xA0;history_lock_time&#xA0;=&#xA0;time(NULL)&#xA0;-&#xA0;history_lock_time;<br>&#xA0;}&#xA0;else&#xA0;{<br>&#xA0;&#xA0;history_lock_time&#xA0;=&#xA0;0;<br>&#xA0;}<br>&#xA0;&#xA0;/*&#xA0;&#x5982;&#x679C;&#x8BBE;&#x7F6E;&#x4E86;--safe-slave-backup&#xFF0C;&#x4E14;SQL&#x7EBF;&#x7A0B;&#x505C;&#x6B62;&#x4E86;&#xFF0C;&#x4F1A;&#x5F00;&#x542F;SQL&#x7EBF;&#x7A0B;&#xA0;*/<br>&#xA0;if&#xA0;(opt_safe_slave_backup&#xA0;&&&#xA0;sql_thread_started)&#xA0;{<br>&#xA0;&#xA0;msg("Starting&#xA0;slave&#xA0;SQL&#xA0;thread\n");<br>&#xA0;&#xA0;xb_mysql_query(mysql_connection,<br>&#xA0;&#xA0;&#xA0;&#xA0;"START&#xA0;SLAVE&#xA0;SQL_THREAD",&#xA0;false);<br>&#xA0;}<br><br>&#xA0;/*&#xA0;Copy&#xA0;buffer&#xA0;pool&#xA0;dump&#xA0;or&#xA0;LRU&#xA0;dump&#xA0;*/<br>&#xA0;/*&#xA0;&#x62F7;&#x8D1D;ib_buffer_pool&#x548C;ib_lru_dump&#x6587;&#x4EF6;&#xA0;*/<br>&#xA0;if&#xA0;(!opt_rsync)&#xA0;{<br>&#xA0;&#xA0;if&#xA0;(opt_dump_innodb_buffer_pool)&#xA0;{<br>&#xA0;&#xA0;&#xA0;check_dump_innodb_buffer_pool(mysql_connection);<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;if&#xA0;(buffer_pool_filename&#xA0;&&&#xA0;file_exists(buffer_pool_filename))&#xA0;{<br>&#xA0;&#xA0;&#xA0;const&#xA0;char&#xA0;*dst_name;<br><br>&#xA0;&#xA0;&#xA0;dst_name&#xA0;=&#xA0;trim_dotslash(buffer_pool_filename);<br>&#xA0;&#xA0;&#xA0;copy_file(ds_data,&#xA0;buffer_pool_filename,&#xA0;dst_name,&#xA0;0);<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;if&#xA0;(file_exists("ib_lru_dump"))&#xA0;{<br>&#xA0;&#xA0;&#xA0;copy_file(ds_data,&#xA0;"ib_lru_dump",&#xA0;"ib_lru_dump",&#xA0;0);<br>&#xA0;&#xA0;}<br>&#xA0;&#xA0;if&#xA0;(file_exists("ddl_log.log"))&#xA0;{<br>&#xA0;&#xA0;&#xA0;copy_file(ds_data,&#xA0;"ddl_log.log",&#xA0;"ddl_log.log",&#xA0;0);<br>&#xA0;&#xA0;}<br>&#xA0;}<br><br>&#xA0;msg_ts("Backup&#xA0;created&#xA0;in&#xA0;directory&#xA0;'%s'\n",&#xA0;xtrabackup_target_dir);<br>&#xA0;if&#xA0;(mysql_binlog_position&#xA0;!=&#xA0;NULL)&#xA0;{<br>&#xA0;&#xA0;msg("MySQL&#xA0;binlog&#xA0;position:&#xA0;%s\n",&#xA0;mysql_binlog_position);<br>&#xA0;}<br>&#xA0;if&#xA0;(!mysql_slave_position.empty()&#xA0;&&&#xA0;opt_slave_info)&#xA0;{<br>&#xA0;&#xA0;msg("MySQL&#xA0;slave&#xA0;binlog&#xA0;position:&#xA0;%s\n",<br>&#xA0;&#xA0;&#xA0;mysql_slave_position.c_str());<br>&#xA0;}<br>/*&#xA0;&#x751F;&#x6210;&#x914D;&#x7F6E;&#x6587;&#x4EF6;&#xFF0C;backup-my.cnf&#xA0;*/<br>&#xA0;if&#xA0;(!write_backup_config_file())&#xA0;{<br>&#xA0;&#xA0;return(false);<br>&#xA0;}<br>&#xA0;<br>/*&#xA0;&#x5C06;&#x5907;&#x4EFD;&#x7684;&#x76F8;&#x5173;&#x4FE1;&#x606F;&#x8BB0;&#x5F55;&#x5728;xtrabackup_info&#x6587;&#x4EF6;&#x4E2D;&#xA0;*/<br>&#xA0;if&#xA0;(!write_xtrabackup_info(mysql_connection))&#xA0;{<br>&#xA0;&#xA0;return(false);<br>&#xA0;}<br><br>&#xA0;return(true);<br>}

该函数的处理流程如下:

  1. 释放全局读锁。
  2. 拷贝ib_buffer_pool和ib_lru_dump文件。
  3. 将备份的相关信息记录在xtrabackup_info文件中。如果设置了–history ,还会将备份信息记录在 PERCONA_SCHEMA库下的xtrabackup_history表中。

总结

综合上面的分析,XtraBackup的备份流程如下图所示。

从源码分析 XtraBackup 的备份原理

Original: https://www.cnblogs.com/ivictor/p/15547387.html
Author: iVictor
Title: 从源码分析 XtraBackup 的备份原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/599399/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球