<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從源碼分析 XtraBackup 的備份流程

          共 9560字,需瀏覽 20分鐘

           ·

          2021-11-20 08:09

          MySQL物理備份工具,常用的有兩個:MySQL Enterprise Backup 和 XtraBackup。

          前者常用于MySQL企業(yè)版,后者常用于MySQL社區(qū)版、Percona Server for MySQL 和 MariaDB。

          所以,如果我們使用的是后三者,在實例較大的情況下,一般都會選擇XtraBackup作為備份恢復(fù)工具。

          熟悉一個工具,不僅僅是要了解它的用法,更重要的是掌握用法背后的原理。畢竟,用法只是“術(shù)”,原理才是“道”。所謂,明道才能優(yōu)術(shù)。

          了解XtraBackup的原理,比較經(jīng)典的一篇文章是淘寶數(shù)據(jù)庫內(nèi)核日報的《Percona XtraBackup 備份原理》http://mysql.taobao.org/monthly/2016/03/07/

          但看文章始終有隔靴搔癢之感,而且很多細(xì)節(jié)性的東西文章也不會提到,譬如我們比較關(guān)心的全局讀鎖。

          下面我們就從源碼的角度看看XtraBackup的備份原理,主要包括兩部分:

          1. XtraBackup的備份流程。
          2. XtraBackup中全局讀鎖的加鎖邏輯。因篇幅較長,這一部分會放到下篇文章介紹。

          分析版本:XtraBackup 2.4.24

          XtraBackup的備份流程

          XtraBackup的main函數(shù)定義在 storage/innobase/xtrabackup/src/xtrabackup.cc 文件中。

          可以看到,對于--backup選項,會調(diào)用xtrabackup_backup_func函數(shù)。

          int?main(int?argc,?char?**argv)
          {
          ????...
          ?/*?--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);
          ?}

          ?if?(xtrabackup_copy_back?||?xtrabackup_move_back)?{
          ??if?(!check_if_param_set("datadir"))?{
          ???msg("Error:?datadir?must?be?specified.\n");
          ???exit(EXIT_FAILURE);
          ??}
          ??mysql_mutex_init(key_LOCK_keyring_operations,
          ?????&LOCK_keyring_operations,?MY_MUTEX_INIT_FAST);
          ??if?(!copy_back(server_argc,?server_defaults))?{
          ???exit(EXIT_FAILURE);
          ??}
          ??mysql_mutex_destroy(&LOCK_keyring_operations);
          ?}
          ????...
          ?msg_ts("completed?OK!\n");

          ?exit(EXIT_SUCCESS);
          }


          下面重點看看xtrabackup_backup_func函數(shù)的處理邏輯。


          xtrabackup_backup_func

          該函數(shù)同樣位于xtrabackup.cc文件中。

          void
          xtrabackup_backup_func(void)
          {
          ????...
          ?/*?start?back?ground?thread?to?copy?newer?log?*/
          ?/*?創(chuàng)建redo?log拷貝線程?*/
          ?os_thread_id_t?log_copying_thread_id;
          ?datafiles_iter_t?*it;
          ????...
          ?/*?get?current?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_read(max_cp_group,?max_cp_field);
          ?buf?=?log_sys->checkpoint_buf;

          ?checkpoint_lsn_start?=?mach_read_from_8(buf?+?LOG_CHECKPOINT_LSN);
          ?checkpoint_no_start?=?mach_read_from_8(buf?+?LOG_CHECKPOINT_NO);
          ????...??
          ?/*?copy?log?file?by?current?position?*/
          ?/*?從最新的checkpoint?lsn開始拷貝redo?log?*/
          ?if(xtrabackup_copy_logfile(checkpoint_lsn_start,?FALSE))
          ??exit(EXIT_FAILURE);

          ?mdl_taken?=?true;

          ?log_copying_stop?=?os_event_create("log_copying_stop");
          ?debug_sync_point("xtrabackup_pause_after_redo_catchup");
          ?os_thread_create(log_copying_thread,?NULL,?&log_copying_thread_id);

          ?/*?Populate?fil_system?with?tablespaces?to?copy?*/
          ?/*?獲取ibdata1,undo?tablespaces及所有的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);
          ?}
          ????...
          ?/*?Create?data?copying?threads?*/
          ?/*?創(chuàng)建數(shù)據(jù)拷貝線程?*/
          ?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是拷貝并發(fā)線程數(shù),由--parallel參數(shù)指定?*/
          ?for?(i?=?0;?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);
          ?}
          ????
          ?/*?循環(huán)等待,直到拷貝結(jié)束?*/
          ?/*?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);
          ?}
          ?}
          ?
          ?/*?調(diào)用backup_start函數(shù),這個函數(shù)會加全局讀鎖,拷貝非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);
          ?}
          ?
          ?/*?讀取最新的checkpoint?lsn,用于后續(xù)的增量備份?*/
          ?/*?read?the?latest?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:
          ?/*?停止redo?log拷貝線程.?將備份的元數(shù)據(jù)信息記錄在XTRABACKUP_METADATA_FILENAME中,即xtrabackup_checkpoints?*/
          ?/*?stop?log_copying_thread?*/
          ?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);
          ?}
          ?
          ?/*?調(diào)用backup_finish函數(shù),這個函數(shù)會釋放全局讀鎖?*/
          ?if?(!backup_finish())?{
          ??exit(EXIT_FAILURE);
          ?}
          ????...
          }

          該函數(shù)的處理流程如下:

          1. 創(chuàng)建redo log拷貝線程,從最近的checkpoint lsn開始拷貝redo log。
          2. 創(chuàng)建數(shù)據(jù)文件拷貝線程,拷貝ibdata1,undo tablespaces及所有的ibd文件。
            這里可通過設(shè)置--parallel進(jìn)行多線程備份,提高物理文件的拷貝效率。不設(shè)置則默認(rèn)為1。
          3. ibd文件拷貝完成后,調(diào)用backup_start函數(shù)。
          4. 停止redo log拷貝線程。
          5. 調(diào)用backup_finish函數(shù)。

          接下來重點看看backup_start和backup_finish這兩個函數(shù)的實現(xiàn)邏輯。


          backup_start

          該函數(shù)位于backup_copy.cc文件中。

          bool
          backup_start()
          {
          ?/*?opt_no_lock指的是--no-lock參數(shù)?*/
          ?if?(!opt_no_lock)?{
          ?/*?如果指定了--safe-slave-backup,會關(guān)閉SQL線程,等待Slave_open_temp_tables變量為0。
          ????如果使用的是statement格式,且使用了臨時表,建議設(shè)置--safe-slave-backup。
          ????對于row格式,無需指定該選項?*/

          ??if?(opt_safe_slave_backup)?{
          ???if?(!wait_for_safe_slave(mysql_connection))?{
          ????return(false);
          ???}
          ??}
          ??/*?調(diào)用backup_files函數(shù)備份非ibd文件,加了全局讀鎖還會調(diào)用一次。
          ?????這一次,實際上針對的是--rsync方式?*/

          ??if?(!backup_files(fil_path_to_mysql_datadir,?true))?{
          ???return(false);
          ??}

          ??history_lock_time?=?time(NULL);
          ??/*?加全局讀鎖,如果支持備份鎖,且沒有設(shè)置--no-backup-locks,會優(yōu)先使用備份鎖?*/
          ??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);
          ???}
          ??}
          ?}
          ?/*?如果設(shè)置了--slave-info,會將SHOW?SLAVE?STATUS的相關(guān)信息,記錄在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設(shè)置的是ON(默認(rèn)是AUTO),則會將SHOW?MASTER?STATUS的相關(guān)信息,記錄在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);
          ?}

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

          該函數(shù)的處理流程如下:

          1. 調(diào)用lock_tables_maybe函數(shù)加全局讀鎖。lock_tables_maybe函數(shù)的處理邏輯會在下篇文章介紹。

          2. 調(diào)用backup_files函數(shù)備份非ibd文件。具體來說,會備份以下面這些關(guān)鍵字作為后綴的文件。

            const?char?*ext_list[]?=?{"frm",?"isl",?"MYD",?"MYI",?"MAD",?"MAI",
            ????????????????"MRG",?"TRG",?"TRN",?"ARM",?"ARZ",?"CSM",?"CSV",?"opt",?"par",
            ????????????????NULL};
          3. 如果命令行中指定了 --slave-info ,則會執(zhí)行 SHOW SLAVE STATUS 獲取復(fù)制的相關(guān)信息。

          4. 如果命令行中指定了 --binlog-info ,則會執(zhí)行 SHOW MASTER STATU 獲取 Binlog 的位置點信息。binlog-info無需顯式指定,因為它的默認(rèn)值為AUTO,如果開啟了Binlog,則為ON。

          backup_finish

          該函數(shù)位于backup_copy.cc文件中。

          bool
          backup_finish()
          {
          ?/*?release?all?locks?*/
          ?/*?釋放所有鎖,如果鎖定了Binlog,還會解鎖Binlog?*/
          ?if?(!opt_no_lock)?{
          ??unlock_all(mysql_connection);
          ??history_lock_time?=?time(NULL)?-?history_lock_time;
          ?}?else?{
          ??history_lock_time?=?0;
          ?}
          ??/*?如果設(shè)置了--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());
          ?}
          /*?生成配置文件,backup-my.cnf?*/
          ?if?(!write_backup_config_file())?{
          ??return(false);
          ?}
          ?
          /*?將備份的相關(guān)信息記錄在xtrabackup_info文件中?*/
          ?if?(!write_xtrabackup_info(mysql_connection))?{
          ??return(false);
          ?}

          ?return(true);
          }

          該函數(shù)的處理流程如下:

          1. 釋放全局讀鎖。
          2. 拷貝ib_buffer_pool和ib_lru_dump文件。
          3. 將備份的相關(guān)信息記錄在xtrabackup_info文件中。
            如果設(shè)置了--history ,還會將備份信息記錄在 PERCONA_SCHEMA庫下的xtrabackup_history表中。

          總結(jié)

          綜合上面的分析,XtraBackup的備份流程如下圖所示。

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷性爱五月天 | 人妻超碰 | 精品九九九一区二区 | 国产AV无码成人精品一区 | 天天插插插插 |