1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【Mysql源碼分析】基于行的復(fù)制實現(xiàn)之“主從關(guān)系建立”

        共 14172字,需瀏覽 29分鐘

         ·

        2020-10-20 17:32

        作者:c rain
        來源:SegmentFault 思否社區(qū)




        前言


        經(jīng)常聽到別人說Mysql的SBR、RBR、MBR,如果不清楚,那么可以跟著文章一起來學(xué)習(xí)。由于涉及到主從的內(nèi)容比較多,需要拆分成多篇內(nèi)容來概述,這章先從基礎(chǔ)知識和主從關(guān)系建立開始講起。還會出一篇文章詳細講解從主同步。


        1.了解什么是SBR、RBR、MBR?

        2.了解下主從配置該如何配置?

        3.了解主從關(guān)系如何建立?




        1.配置Mysql主從


        在本文中,分為一主一從。主監(jiān)聽的端口為3306,從監(jiān)聽的端口為3309。


        1.1主服務(wù)配置


        master配置,配置文件my.cnf:


        [mysqld]
        port=3306
        basedir=/usr/local/mysql8.0.20
        datadir=/usr/local/mysql8.0.20/data
        socket=/tmp/mysql.sock
        #explicit_defaults_for_timestamp=true
        lower_case_table_names=2?#表名存儲為給定的大小寫但是比較的時候是小寫的
        log_bin=mysql-bin
        server_id?=10


        主服務(wù)啟動Mysql命令:


        #?sudo?bin/mysqld?--defaults-file=/usr/local/mysql8.0.20/etc/my.cnf?--user=root


        客戶端連接master端


        #?mysql?--socket=/tmp/mysql.sock?-u?root


        啟動master后,需要創(chuàng)建一個用戶,用于主從同步:


        mysql>?GRANT?REPLICATION?SLAVE?ON?*.*?TO?repl@'localhost'?IDENTIFIED?BY?'123456';


        查看主服務(wù)狀態(tài)


        mysql>?show?master?status?\G;


        1.2 從服務(wù)配置


        slave配置,配置文件salve.conf:


        [mysqld]
        port=3309
        basedir=/usr/local/mysql8.0.20
        datadir=/usr/local/mysql8.0.20/data1
        socket=/tmp/mysqlslave.sock
        #explicit_defaults_for_timestamp=true
        lower_case_table_names=2?#表名存儲為給定的大小寫但是比較的時候是小寫的
        log_bin=mysql-bin
        server_id=2
        relay_log=/usr/local/mysql8.0.20/mysql-relay-bin
        read_only=1?????#執(zhí)行模式


        從服務(wù)啟動Mysql命令:


        #?sudo?bin/mysqld?--defaults-file=/usr/local/mysql8.0.20/etc/salve.conf?--user=root


        連接Slave端:


        #?mysql?--socket=/tmp/mysqlslave.sock?-u?root


        Slave端主從同步配置:


        mysql>
        change?master?to?master_host='localhost',?
        master_user='repl',
        master_password='123456',
        master_log_file='mysql-bin.000001',
        master_log_pos=0;


        Slave/Master


        #查看binlog事件
        mysql>?SHOW?BINLOG?EVENTS;


        如果發(fā)現(xiàn)主從沒有同步,可以使用如下命令查看相應(yīng)的狀態(tài):


        #查看slave狀態(tài)
        mysql>?show?slave?status;


        如果遇到從庫報這個錯誤:Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file'

        Got fatal error 1236 from master when reading data from binary log: 'could not find next log'


        此時可以操作如下三個命令進行處理:


        #關(guān)閉slave
        mysql>?stop?slave;
        #重置slave
        mysql>?reset?slave;
        #啟動slave
        mysql>?start?slave;




        2.MYSQL中BINLOG_FORMAT的三種模式


        mysql三種復(fù)制方式:基于SQL語句的復(fù)制(statement-based replication, SBR),基于行的復(fù)制(row-based replication, RBR),混合模式復(fù)制(mixed-based replication, MBR)。對應(yīng)的,binlog的格式也有三種:STATEMENT,ROW,MIXED。


        2.1 STATEMENT模式(SBR)


        每一條會修改數(shù)據(jù)的sql語句會記錄到binlog中。優(yōu)點是并不需要記錄每一條sql語句和每一行的數(shù)據(jù)變化,減少了binlog日志量,節(jié)約IO,提高性能。缺點是在某些情況下會導(dǎo)致master-slave中的數(shù)據(jù)不一致(如sleep()函數(shù), last_insert_id(),以及user-defined functions(udf)等會出現(xiàn)問題)


        2.2 ROW模式(RBR)


        不記錄每條sql語句的上下文信息,僅需記錄哪條數(shù)據(jù)被修改了,修改成什么樣了。而且不會出現(xiàn)某些特定情況下的存儲過程、或function、或trigger的調(diào)用和觸發(fā)無法被正確復(fù)制的問題。缺點是會產(chǎn)生大量的日志,尤其是alter table的時候會讓日志暴漲。


        2.3 MIXED模式(MBR)


        以上兩種模式的混合使用,一般的復(fù)制使用STATEMENT模式保存binlog,對于STATEMENT模式無法復(fù)制的操作使用ROW模式保存binlog,MySQL會根據(jù)執(zhí)行的SQL語句選擇日志保存方式。


        binlog復(fù)制配置在mysql的配置文件my.cnf中,可以通過一下選項配置binglog相關(guān),配置如下:


        #binlog日志格式,mysql默認采用statement,建議使用mixed
        binlog_format???????????=?MIXED????

        #binlog日志文件
        log-bin?????????????????=?/data/mysql/mysql-bin.log??

        #binlog過期清理時間
        expire_logs_days????????=?7????

        #binlog每個日志文件大小
        max_binlog_size?????????=?100m??????????????????

        #binlog緩存大小
        binlog_cache_size???????=?4m????????????????????

        #最大binlog緩存大小
        max_binlog_cache_size???=?512m


        2.4 優(yōu)缺點對比


        對于執(zhí)行的SQL語句中包含now()這樣的時間函數(shù),會在日志中產(chǎn)生對應(yīng)的unix_timestamp()*1000的時間字符串,slave在完成同步時,取用的是sqlEvent發(fā)生的時間來保證數(shù)據(jù)的準確性。另外對于一些功能性函數(shù)slave能完成相應(yīng)的數(shù)據(jù)同步,而對于上面指定的一些類似于UDF函數(shù),導(dǎo)致Slave無法知曉的情況,則會采用ROW格式存儲這些Binlog,以保證產(chǎn)生的Binlog可以供Slave完成數(shù)據(jù)同步。


        現(xiàn)在來比較以下 SBR 和 RBR 這2種模式各自的優(yōu)缺點:


        SBR 的優(yōu)點:


        • 技術(shù)比較成熟
        • binlog文件較小
        • binlog中包含了所有數(shù)據(jù)庫更改信息,可以據(jù)此來審核數(shù)據(jù)庫的安全等情況
        • binlog可以用于實時的還原,而不僅僅用于復(fù)制
        • 主從版本可以不一樣,從服務(wù)器版本可以比主服務(wù)器版本高

        SBR 的缺點:


        • 不是所有的UPDATE語句都能被復(fù)制,尤其是包含不確定操作的時候。
        • 調(diào)用具有不確定因素的 UDF 時復(fù)制也可能出問題
        • 使用以下函數(shù)的語句也無法被復(fù)制:
          • LOAD_FILE()
          • UUID()
          • USER()
          • FOUND_ROWS()
          • SYSDATE() (除非啟動時啟用了 --sysdate-is-now 選項)

        • INSERT ... SELECT 會產(chǎn)生比 RBR 更多的行級鎖

        • 復(fù)制需要進行全表掃描(WHERE 語句中沒有使用到索引)的 UPDATE 時,需要比 RBR 請求更多的行級鎖

        • 對于有 AUTO_INCREMENT 字段的 InnoDB表而言,INSERT 語句會阻塞其他 INSERT 語句

        • 對于一些復(fù)雜的語句,在從服務(wù)器上的耗資源情況會更嚴重,而 RBR 模式下,只會對那個發(fā)生變化的記錄產(chǎn)生影響

        • 存儲函數(shù)(不是存儲過程)在被調(diào)用的同時也會執(zhí)行一次 NOW() 函數(shù),這個可以說是壞事也可能是好事

        • 確定了的 UDF 也需要在從服務(wù)器上執(zhí)行

        • 數(shù)據(jù)表必須幾乎和主服務(wù)器保持一致才行,否則可能會導(dǎo)致復(fù)制出錯

        • 執(zhí)行復(fù)雜語句如果出錯的話,會消耗更多資源


        RBR 的優(yōu)點:


        任何情況都可以被復(fù)制,這對復(fù)制來說是最安全可靠的和其他大多數(shù)數(shù)據(jù)庫系統(tǒng)的復(fù)制技術(shù)一樣多數(shù)情況下,從服務(wù)器上的表如果有主鍵的話,復(fù)制就會快了很多。復(fù)制以下幾種語句時的行鎖更少:

        • INSERT ... SELECT
        • 包含 AUTO_INCREMENT 字段的 INSERT
        • 沒有附帶條件或者并沒有修改很多記錄的 UPDATE 或 DELETE 語句執(zhí)行 INSERT,UPDATE,DELETE 語句時鎖更少,從服務(wù)器上采用多線程來執(zhí)行復(fù)制成為可能。

        RBR 的缺點:


        • binlog 大了很多
        • 復(fù)雜的回滾時 binlog 中會包含大量的數(shù)據(jù)

        主服務(wù)器上執(zhí)行 UPDATE 語句時,所有發(fā)生變化的記錄都會寫到 binlog 中,而 SBR 只會寫一次,這會導(dǎo)致頻繁發(fā)生 binlog 的并發(fā)寫問題

        • UDF 產(chǎn)生的大 BLOB 值會導(dǎo)致復(fù)制變慢

        無法從 binlog 中看到都復(fù)制了寫什么語句
        當在非事務(wù)表上執(zhí)行一段堆積的SQL語句時,最好采用 SBR 模式,否則很容易導(dǎo)致主從服務(wù)器的數(shù)據(jù)不一致情況發(fā)生。

        另外,針對系統(tǒng)庫 mysql 里面的表發(fā)生變化時的處理規(guī)則如下:

        • 如果是采用 INSERT,UPDATE,DELETE 直接操作表的情況,則日志格式根據(jù) binlog_format 的設(shè)定而記錄

        • 如果是采用 GRANT,REVOKE,SET PASSWORD 等管理語句來做的話,那么無論如何都采用 SBR 模式記錄


          注:采用 RBR 模式后,能解決很多原先出現(xiàn)的主鍵重復(fù)問題。



        2.5 如何查看復(fù)制格式


        通過如下命令即可查看復(fù)制格式,如圖2-4-1:

        mysql>?show?variables?like?'binlog_format';



        圖2-4-1 查看復(fù)制格式

        默認binlog_format參數(shù)為行復(fù)制,在源碼mysql-8.0.20/sql/sys_vars.cc中

        static?Sys_var_enum?Sys_binlog_format(
        ????"binlog_format",
        ????"What?form?of?binary?logging?the?master?will?"
        ????"use:?either?ROW?for?row-based?binary?logging,?STATEMENT?"
        ????"for?statement-based?binary?logging,?or?MIXED.?MIXED?is?statement-"
        ????"based?binary?logging?except?for?those?statements?where?only?row-"
        ????"based?is?correct:?those?which?involve?user-defined?functions?(i.e.?"
        ????"UDFs)?or?the?UUID()?function;?for?those,?row-based?binary?logging?is?"
        ????"automatically?used.?If?NDBCLUSTER?is?enabled?and?binlog-format?is?"
        ????"MIXED,?the?format?switches?to?row-based?and?back?implicitly?per?each?"
        ????"query?accessing?an?NDBCLUSTER?table",
        ????SESSION_VAR(binlog_format),?CMD_LINE(REQUIRED_ARG,?OPT_BINLOG_FORMAT),
        ????binlog_format_names,?
        ????DEFAULT(BINLOG_FORMAT_ROW),?//默認binlog的同步為行復(fù)制
        ????NO_MUTEX_GUARD,
        ????NOT_IN_BINLOG,?ON_CHECK(binlog_format_check),
        ????ON_UPDATE(fix_binlog_format_after_update));

        通過上面代碼可以看出mysql-8.0.20默認為行復(fù)制。

        那么繼續(xù)來看一下binlog_format都有那些模式,可以看mysql-8.0.20/sql/system_variables.h文件

        //?Values?for?binlog_format?sysvar
        enum?enum_binlog_format?{
        ??BINLOG_FORMAT_MIXED?=?0,??///<混合模式?statement?if?safe,?otherwise?row?-?autodetected
        ??BINLOG_FORMAT_STMT?=?1,???///??BINLOG_FORMAT_ROW?=?2,????///<行復(fù)制?row-based
        ??BINLOG_FORMAT_UNSPEC?=
        ??????3??///};

        基于如下代碼可以得知,binlog_format包含BINLOG_FORMAT_MIXED、BINLOG_FORMAT_STMT、BINLOG_FORMAT_ROW三種模式,也就是對應(yīng):STATEMENT模式(SBR)、ROW模式(RBR)、MIXED模式(MBR)



        3.建立主從關(guān)系


        通過張圖來看一下主從關(guān)系建立的相關(guān)流程,如圖3-1-1:


        圖3-1-1 主從同步建立


        3.1 slave端start_salve方法


        在mysql主從建立中,是由slave端先發(fā)起,當執(zhí)行“start slave;” 語句時,會調(diào)用rpl_slave.cc中的start_slave方法,其中實現(xiàn)如下:

        bool?start_slave(THD?*thd,?LEX_SLAVE_CONNECTION?*connection_param,
        ?????????????????LEX_MASTER_INFO?*master_param,?int?thread_mask_input,
        ?????????????????Master_info?*mi,?bool?set_mts_settings)?{
        ??bool?is_error?=?false;
        ??int?thread_mask;

        ??DBUG_TRACE;
        ????
        ??lock_slave_threads(mi);??//停止運行線程
        ??//?獲取已停止線程的掩碼
        ??init_thread_mask(&thread_mask,?mi,?true?/*?inverse?*/);
        ??/*
        ????我們將停止下面的所有線程。但如果用戶想只啟動一個線程,
        ????就好像另一個線程正在運行一樣(就像我們不要想碰另一根線),
        ????所以將位設(shè)置為0其他線程
        ??*/
        ??if?(thread_mask_input)?{
        ????thread_mask?&=?thread_mask_input;
        ??}
        ??if?(thread_mask)??//?一些線程停止,啟動它們
        ??{
        ????if?(load_mi_and_rli_from_repositories(mi,?false,?thread_mask))?{
        ??????is_error?=?true;
        ??????my_error(ER_MASTER_INFO,?MYF(0));
        ????}?else?if?(*mi->host?||?!(thread_mask?&?SLAVE_IO))?{
        ??????/*
        ????????如果我們要啟動IO線程,我們需要考慮通過啟動從機提供的選項。
        ??????*/
        ??????if?(thread_mask?&?SLAVE_IO)?{
        ????????if?(connection_param->user)?{?//設(shè)置用戶
        ??????????mi->set_start_user_configured(true);
        ??????????mi->set_user(connection_param->user);
        ????????}
        ????????if?(connection_param->password)?{?//設(shè)置密碼
        ??????????mi->set_start_user_configured(true);
        ??????????mi->set_password(connection_param->password);
        ????????}
        ????????if?(connection_param->plugin_auth)?//設(shè)置授權(quán)插件
        ??????????mi->set_plugin_auth(connection_param->plugin_auth);
        ????????if?(connection_param->plugin_dir)?//插件目錄
        ??????????mi->set_plugin_dir(connection_param->plugin_dir);
        ??????}

        ????????//...

        ????????//初始化設(shè)置
        ????????int?slave_errno?=?mi->rli->init_until_option(thd,?master_param);
        ????????if?(slave_errno)?{
        ??????????my_error(slave_errno,?MYF(0));
        ??????????is_error?=?true;
        ????????}

        ????????if?(!is_error)?is_error?=?check_slave_sql_config_conflict(mi->rli);
        ??????}?else?if?(master_param->pos?||?master_param->relay_log_pos?||
        ?????????????????master_param->gtid)
        ????????push_warning(thd,?Sql_condition::SL_NOTE,?ER_UNTIL_COND_IGNORED,
        ?????????????????????ER_THD(thd,?ER_UNTIL_COND_IGNORED));

        ??????if?(!is_error)
        ????????//啟動slave線程
        ????????is_error?=
        ????????????start_slave_threads(false?/*need_lock_slave=false*/,
        ????????????????????????????????true?/*wait_for_start=true*/,?mi,?thread_mask);
        ????}?else?{
        ??????is_error?=?true;
        ??????my_error(ER_BAD_SLAVE,?MYF(0));
        ????}
        ??}?else?{
        ????/*?如果所有線程都已啟動,則沒有錯誤,只有一個警告?*/
        ????push_warning_printf(
        ????????thd,?Sql_condition::SL_NOTE,?ER_SLAVE_CHANNEL_WAS_RUNNING,
        ????????ER_THD(thd,?ER_SLAVE_CHANNEL_WAS_RUNNING),?mi->get_channel());
        ??}

        ??/*
        ????如果有人試圖啟動,請清除啟動信息,IO線程以避免任何安全問題。
        ??*/
        ??if?(is_error?&&?(thread_mask?&?SLAVE_IO)?==?SLAVE_IO)?mi->reset_start_info();

        ??unlock_slave_threads(mi);

        ??mi->channel_unlock();

        ??return?is_error;
        }

        從如上代碼可以得知調(diào)用“start slave;”時,會對線程做一些停止操作。然后進行一些設(shè)置后,調(diào)用start_slave_threads方法啟動slave線程。然后start_slave_threads是一個比較關(guān)鍵的方法。

        那么接下來看一下start_slave_threads方法,實現(xiàn)如下:

        bool?start_slave_threads(bool?need_lock_slave,?bool?wait_for_start,
        ?????????????????????????Master_info?*mi,?int?thread_mask)?{
        ??mysql_mutex_t?*lock_io?=?nullptr,?*lock_sql?=?nullptr,
        ????????????????*lock_cond_io?=?nullptr,?*lock_cond_sql?=?nullptr;
        ??mysql_cond_t?*cond_io?=?nullptr,?*cond_sql?=?nullptr;
        ??bool?is_error?=?false;
        ??DBUG_TRACE;
        ??DBUG_EXECUTE_IF("uninitialized_master-info_structure",?mi->inited?=?false;);

        ??//...
        ??
        ??if?(thread_mask?&?SLAVE_IO)??//判斷是否支持SLAVE_IO
        ????is_error?=?start_slave_thread(
        #ifdef?HAVE_PSI_THREAD_INTERFACE
        ????????key_thread_slave_io,
        #endif
        ????????handle_slave_io,?lock_io,?lock_cond_io,?cond_io,?&mi->slave_running,
        ????????&mi->slave_run_id,?mi);??//調(diào)用handle_slave_io方法
        ????????
        ??if?(!is_error?&&?(thread_mask?&?SLAVE_SQL))?{?//判斷是否支持SLAVE_SQL
        ???
        ????//...
        ????if?(!is_error)
        ??????is_error?=?start_slave_thread(
        #ifdef?HAVE_PSI_THREAD_INTERFACE
        ??????????key_thread_slave_sql,
        #endif
        ??????????handle_slave_sql,?lock_sql,?lock_cond_sql,?cond_sql,
        ??????????&mi->rli->slave_running,?&mi->rli->slave_run_id,?mi);?//調(diào)用handle_slave_sql方法
        ????if?(is_error)
        ??????terminate_slave_threads(mi,?thread_mask?&?SLAVE_IO,
        ??????????????????????????????rpl_stop_slave_timeout,?need_lock_slave);
        ??}
        ??return?is_error;
        }

        通過如上方法可以得知thread_mask掩碼是用于判斷是否支持SLAVE_IO與支持SLAVE_SQL。如果支持則線程調(diào)用對應(yīng)的方法。因為考慮到主題為主從關(guān)系建立,這里主要關(guān)注一下handle_slave_io方法。

        圖3-1-2 主從同步協(xié)議


        如圖3-1-2中IO Thread與SQL Thread其實就是對應(yīng)handle_slave_io方法與handle_slave_sql方法。

        3.2 slave端handle_slave_io方法


        handle_slave_io方法為建立主從的主要方法,其中包含了初始化slave線程、發(fā)起連接master、注冊slave到master,發(fā)起COM_BINLOG_DUMP或COM_BINLOG_DUMP_GTID操作等,實現(xiàn)如下:

        extern?"C"?void?*handle_slave_io(void?*arg)?{
        ??//...
        ??my_thread_init();
        ??{
        ????//初始化slave線程
        ????if?(init_slave_thread(thd,?SLAVE_THD_IO))?{
        ??????mysql_cond_broadcast(&mi->start_cond);
        ??????mysql_mutex_unlock(&mi->run_lock);
        ??????mi->report(ERROR_LEVEL,?ER_SLAVE_FATAL_ERROR,
        ?????????????????ER_THD(thd,?ER_SLAVE_FATAL_ERROR),
        ?????????????????"Failed?during?slave?I/O?thread?initialization?");
        ??????goto?err;
        ????}

        ????//...
        ????
        ????mysql_cond_broadcast(&mi->start_cond);?//調(diào)用做喚醒操作

        ????
        ????//...
        ????
        ????//發(fā)起登陸操作
        ????successfully_connected?=?!safe_connect(thd,?mysql,?mi);
        ????//?we?can?get?killed?during?safe_connect
        #ifdef?HAVE_SETNS
        ????if?(mi->is_set_network_namespace())?{
        ??????//?Restore?original?network?namespace?used?to?be?before?connection?has
        ??????//?been?created
        ??????successfully_connected?=
        ??????????restore_original_network_namespace()?|?successfully_connected;
        ????}
        #endif

        ????//...

        ????/*
        ??????注冊slave到master
        ????*/
        ????THD_STAGE_INFO(thd,?stage_registering_slave_on_master);
        ????if?(register_slave_on_master(mysql,?mi,?&suppress_warnings))?{
        ??????if?(!check_io_slave_killed(thd,?mi,
        ?????????????????????????????????"Slave?I/O?thread?killed?"
        ?????????????????????????????????"while?registering?slave?on?master"))?{
        ????????LogErr(ERROR_LEVEL,?ER_RPL_SLAVE_IO_THREAD_CANT_REGISTER_ON_MASTER);
        ????????if?(try_to_reconnect(thd,?mysql,?mi,?&retry_count,?suppress_warnings,
        ?????????????????????????????reconnect_messages[SLAVE_RECON_ACT_REG]))
        ??????????goto?err;
        ??????}?else
        ????????goto?err;
        ??????goto?connected;
        ????}

        ????//...
        ????
        ????while?(!io_slave_killed(thd,?mi))?{
        ??????MYSQL_RPL?rpl;

        ??????THD_STAGE_INFO(thd,?stage_requesting_binlog_dump);
        ??????if?(request_dump(thd,?mysql,?&rpl,?mi,?&suppress_warnings))?{?//發(fā)起dump指令
        ????????LogErr(ERROR_LEVEL,?ER_RPL_SLAVE_ERROR_REQUESTING_BINLOG_DUMP,
        ???????????????mi->get_for_channel_str());
        ????????if?(check_io_slave_killed(thd,?mi,
        ??????????????????????????????????"Slave?I/O?thread?killed?while?\
        requesting?master?dump"
        )?||
        ????????????try_to_reconnect(thd,?mysql,?mi,?&retry_count,?suppress_warnings,
        ?????????????????????????????reconnect_messages[SLAVE_RECON_ACT_DUMP]))
        ??????????goto?err;
        ????????goto?connected;
        ??????}
        ??????//...
        ????}
        ??
        ??//...
        ??
        ??my_thread_end();?//線程結(jié)束
        #if?OPENSSL_VERSION_NUMBER?
        ??ERR_remove_thread_state(0);
        #endif?/*?OPENSSL_VERSION_NUMBER?
        ??my_thread_exit(nullptr);?//退出線程
        ??return?(nullptr);??//?Avoid?compiler?warnings
        }

        從代碼中可以得知調(diào)用safe_connect進行slave對master的登陸,具體登陸可以協(xié)議可以看一下之前寫的文章:
        https://blog.csdn.net/byxiaoyuonly/article/details/108212013。

        然后又調(diào)用register_slave_on_master會發(fā)送COM_REGISTER_SLAVE指令進行把slave注冊到master,再調(diào)用request_dump發(fā)起binlog_dump指令。

        圖3-2-1 發(fā)送COM_BINLOG_DUMP


        在request_dump發(fā)送指令其實支持COM_BINLOG_DUMP與COM_BINLOG_DUMP_GTID兩種,但是具體發(fā)什么取決于是否開啟gtid設(shè)置,如圖3-2-1所示。

        3.4 master建立實現(xiàn)


        通過對上面內(nèi)容的了解,我們得知register_slave_on_master會發(fā)起發(fā)起COM_REGISTER_SLAVE對把slave注冊到master,然后調(diào)用request_dump發(fā)起binlog_dump指令。master指令處理如下:

        bool?dispatch_command(THD?*thd,?const?COM_DATA?*com_data,
        ??????????????????????enum?enum_server_command?command)?{
        ??
        ??//...
        ??
        ??switch?(command)?{
        ????
        ????case?COM_REGISTER_SLAVE:?{?//注冊slave到master
        ??????//?TODO:?access?of?protocol_classic?should?be?removed
        ??????if?(!register_slave(thd,?thd->get_protocol_classic()->get_raw_packet(),
        ??????????????????????????thd->get_protocol_classic()->get_packet_length()))
        ????????my_ok(thd);
        ??????break;
        ????}
        ????
        ????//...
        ????
        ????case?COM_BINLOG_DUMP_GTID:?//binlog_dump_gtid
        ??????//?TODO:?access?of?protocol_classic?should?be?removed
        ??????error?=?com_binlog_dump_gtid(
        ??????????thd,?(char?*)thd->get_protocol_classic()->get_raw_packet(),
        ??????????thd->get_protocol_classic()->get_packet_length());
        ??????break;
        ????case?COM_BINLOG_DUMP:?//binlog_dump
        ??????//?TODO:?access?of?protocol_classic?should?be?removed
        ??????error?=?com_binlog_dump(
        ??????????thd,?(char?*)thd->get_protocol_classic()->get_raw_packet(),
        ??????????thd->get_protocol_classic()->get_packet_length());
        ??????break;
        ??}

        ??return?error;
        }

        注冊slave到master過程可以看后續(xù)的數(shù)據(jù)包,這邊可以接著看一下對應(yīng)的com_binlog_dump方法實現(xiàn):

        bool?com_binlog_dump(THD?*thd,?char?*packet,?size_t?packet_length)?{
        ??DBUG_TRACE;
        ??ulong?pos;
        ??ushort?flags?=?0;
        ??const?uchar?*packet_position?=?(uchar?*)packet;
        ??size_t?packet_bytes_todo?=?packet_length;

        ??DBUG_ASSERT(!thd->status_var_aggregated);
        ??thd->status_var.com_other++;
        ??thd->enable_slow_log?=?opt_log_slow_admin_statements;
        ??if?(check_global_access(thd,?REPL_SLAVE_ACL))?return?false;

        ??/*
        ????4?bytes?is?too?little,?but?changing?the?protocol?would?break
        ????compatibility.??This?has?been?fixed?in?the?new?protocol.?@see
        ????com_binlog_dump_gtid().
        ??*/
        ??READ_INT(pos,?4);
        ??READ_INT(flags,?2);
        ??READ_INT(thd->server_id,?4);

        ??DBUG_PRINT("info",
        ?????????????("pos=%lu?flags=%d?server_id=%d",?pos,?flags,?thd->server_id));

        ??kill_zombie_dump_threads(thd);

        ??query_logger.general_log_print(thd,?thd->get_command(),?"Log:?'%s'??Pos:?%ld",
        ?????????????????????????????????packet?+?10,?(long)pos);
        ??mysql_binlog_send(thd,?thd->mem_strdup(packet?+?10),?(my_off_t)pos,?nullptr,
        ????????????????????flags);?//發(fā)送binlog

        ??unregister_slave(thd,?true,?true?/*need_lock_slave_list=true*/);
        ??/*?如果我們到了這里,線程需要終止?*/
        ??return?true;

        error_malformed_packet:
        ??my_error(ER_MALFORMED_PACKET,?MYF(0));
        ??return?true;
        }

        在mysql_binlog_send中其實調(diào)用Binlog_sender的run方法,sender.run()方法中又調(diào)用Binlog_sender::init 初始化檢測、Binlog_sender::check_start_file() 檢查文件等。最終調(diào)用Binlog_sender::send_binlog對從服務(wù)發(fā)送binlog,如圖3-4-1所示。


        圖3-4-1 send binlog



        4.主從建立過程中數(shù)據(jù)包


        圖4-1 主從建立過程數(shù)據(jù)包


        通過圖4-1可以得知在主從關(guān)系建立會發(fā)起如下操作:

        #查詢當前時間戳
        SELECT?UNIX_TIMESTAMP()

        #查詢master的serverid
        SELECT?@@GLOBAL.SERVER_ID

        #設(shè)置心跳周期,單位為納秒,其實只有30s。初始化心跳如圖4-2
        SET?@master_heartbeat_period=?30000001024

        #設(shè)置master_binlog_checksum
        SET?@master_binlog_checksum=?@@global.binlog_checksum

        #查詢master_binlog_checksum
        SELECT?@master_binlog_checksum

        #獲得是否支持gtid
        SELECT?@@GLOBAL.GTID_MODE

        #查詢server?uuid
        SELECT?@@GLOBAL.SERVER_UUID

        #設(shè)置slave?uuid
        SET?@slave_uuid=?'2dc27df4-e143-11ea-b396-cc679ee1902b'


        圖4-2 初始化心跳周期




        總結(jié)


        1. mysql復(fù)制模式分為三種:STATEMENT模式(SBR)、ROW模式(RBR)、MIXED模式(MBR)。
        2. mysql8.0.20默認ROW模式(RBR)。
        3. 發(fā)送binlog支持兩種形式:COM_BINLOG_DUMP與COM_BINLOG_DUMP_GTID。
        4. 默認情況master_heartbeat_period為30秒,單位為納秒。
        5. IO Thread與SQL Thread其實就是對應(yīng)handle_slave_io方法與handle_slave_sql方法。



        -?END -

        瀏覽 26
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            色射爱| 今天成全在线观看高清| 欧美一级婬片AAAAAA片| 美女裸身18禁| 日本一区二区三区四区在线观看| 日韩天堂av| 亚洲一区视频| 亚洲日韩Av无码中文字幕美国| 丁香五月六月| 亚洲无码图| 国产无码高清在线观看| 一级特黄大片录像i| 人妻无码HEYZO少妇精品| 天堂无码视频| 久久人妻熟女中文字幕av蜜芽| 中文字幕人妻互换av久久| 日韩伊人网| 亚洲性无码| 艹美女视频| 无码九九九| 国产69av| 国产激情欧洲在线观看一区二区三区| 在线观看国产视频| 草逼网址| 欧美操逼视频网站| 北条麻妃在线不卡| 免费国产三级片| 黄色电影视频在线| 日韩一区二区三区四区久久久精品有吗| www.av免费| 欧美精品A级片| 婷婷五月天激情网| 狠狠色婷婷7777| 国产毛片一区二区三区| 久久久久久久久免费看无码| 奇米99| 成人爽爽视频| 三级大香蕉| 国产性爱免费视频| 高清免费在线中文Av| 青娱乐国产精品| 国产精品乱码毛片在线人与| 欧美激情网站| 南京搡BBBB搡BBBB| 操B视频网站| 亚洲视频免费看| 日韩va中文字幕无码免费| 97精品人人妻人人| 18禁网站免费观看| 国产AV无码影院| 国产精品夜夜爽3000| 日产久久久| 四虎最新视频| 色婷婷在线播放| a在线视频| 99热这里只有精品99| 91久久婷婷| 久久久无码精品亚洲| 操操操影院| 国产麻豆三级片| 911精品人妻一区二区三区A片| 求欧美精品网址| 狼人一区二区| 中文字幕免费MV第一季歌词 | 狠狠色av| 色婷婷在线视频播放| 精品成人Av一区二区三区| 国产精品一品二区三区的使用体验 | 超碰人人操人人摸| 亚洲欧美国产高清vA在线播放| 黄片高清无码在线观看| 日本高清久久| 日韩香蕉网| 五月婷婷视频在线观看| 无码精品一区二区免费| 国产AV无码影院| 午夜人妻无码| 大香蕉精品一区| 在线观看一区二区视频| 蜜臀久久99精品久久一区二区| 美女av免费| 水多多成人网站A片| 无码久| 91网站在线播放| 国产精品7777| 91精品久久久久久综合五月天| 免费69视频| 西西444WWW无码视频软件功能介绍 | 99reav| 蜜桃成人无码区免费视频网站 | 韩国无码一区二区| 毛片aaa| 西西WWW888大胆无码| www.re99| 日韩欧美群交| 亚洲精品秘一区二区三区影| 91精品久久久久久久久久久久| 亚洲娱乐在线| 国产一区一区| 2025国产在线| 91麻豆精品国产91久久久久久| 亚洲免费一级片| 丁香激情五月少妇| 日日艹夜夜艹| 婷婷俺也去| 中文字幕在线观看日本| sm在线观看| 五月天国产| 大香蕉欧美视频| 丁香婷婷五月色成人网站| 麻豆视频一区| 2014AV天堂网| 欧美级毛片一夜| 午夜精品久久久久久久| 黄色小视频在线观看| 黄色片A片| 国精产品一区二区三区黑人和中国 | 久久久黄片| 大香伊人| www.欧美日韩| 亚洲无码影视| 黄色大片久草| 激情五月毛片| 天堂网免费视频| 亚洲成人在线一区| 欧美色图色就是色| 精品国产一级| 91人人精品| 特级西西444WWW高清| 婷婷久久在线| 国产尤物视频| 91蜜桃网| 亚洲免费中文字幕| 日本一级黃色大片看免费| 国产精品内射婷婷一级二| 91久久国产综合久久| 中文字幕无码视频| 日韩人妻精品无码久久边| 婷婷九月色| 中文av网站| 操逼地址| 日韩毛片大全| 俺也来www俺也色com| 小视频你懂的| 亚洲精品久久久久久久久豆丁网| 国产激情视频在线播放| 中文字幕亚洲综合| 伊人成人电影| 国产一级a毛一级a爰片| 影音先锋人妻限定| 超碰在线观看2407| 亚洲插逼视频| 无码人妻AⅤ一区二区三区| 亚洲激色| 高清亚洲| 美女久久| 亚洲在线无码视频| 国产美女操逼| 国产又爽又黄免费视频网站| 草久精品| 色九月婷婷| 偷拍视频图片综合网| 日韩精品成人av| 成人电影久久久| 在线观看免费A片| 一级a一级a免费观看视频Al明星 | 免费手机av| 精品一区二区三区四区视频| 亚洲永久免费| 欧美日韩久久| 无套免费视频欧美| 日韩无修正| 第一福利导航大全| 狼友综合| 国产三级在线观看视频| 亚洲成人视屏| 午夜天堂| 肏逼黄色一级| 国产高清精品在线| 欧美色女人| 久久婷婷国产综合| 777视频在线观看| 亚洲AV无码电影| 国产高清在线观看| 国产在线不卡年轻点的| 91香蕉国产成人App| 麻豆秘在线观看国产| 久久精品一二三| 亚洲色久悠悠| 成人区精品一区二区婷婷| 日韩中文字幕熟妇人妻| 美女做爱在线观看| 久婷婷| 国产成人精品一区二区三区视频| 成人动漫在线观看| 中文字幕人妻一区| 搡BBBB搡BBBB搡BBB| 亚洲无码高清在线观看视频| 星空AV| 91精品婷婷国产综合久久蝌蚪 | 成人影片在线观看18| 天天干免费视频| 亚洲精品国偷拍自产在线观看蜜桃| 91精品国产麻豆国产自产在线| 国产又爽又黄免费网站校园里| 欧美怡春院| 北条麻妃二区三区| 久久黄色视屏| 欧美日韩亚洲中文字幕| 免费毛片网| 久草视频免费在线观看| 91成人毛片| 国产又爽又黄视频| 日韩av高清| 欧美一级aa| 欧美色图在线视频| 婷婷开心五月天| 免费无码国产| 欧洲性爱视频| 靠逼久久| 一级A片免费看| 欧美成人一区二区三区片| 黄色一级小说| 色婷婷五月天在线观看| 国产成人三级片| 2018天天操| 色综合五月婷婷| 国产乱论视频| 操碰视频在线| 特级西西西西4444级酉西88wwww特| 色色色色色欧美| 久久久在线视频| 搡老熟女-91Porn| 日韩无码二区| 天天干天天干天天| 安徽妇搡BBBB搡BBB| 91蝌蚪视频在线播放| 久久精品91| 手机看片久久| 日本无码久久嗯啊流水| 一区四区视频| 国产经典午夜福利视频合集| 亲子乱AⅤ一区二区三区| 国产精品无毛五区六区| 国产成人三级片在线观看| 影音先锋国产在线| 国产成人综合网| 欧美视频在线观看| 西西444WWW无码大胆在线观看| 五月天婷婷成人| 亚洲电影中文字幕| 中文字幕三区| 五月婷婷色色网| 超碰人人妻| 欧美操逼视频网站| 国产午夜视频| 午夜成人免费福利| www.中文无码| 欧美色色色色色色| 亚洲天堂无码| 国产一级操逼片| 亚洲国产精品二二三三区| 日韩精品网| 中文字幕高清在线中文字幕中文字幕| 久9久9久9久9久9久9| 自拍偷拍av| 天堂在线免费视频| 亚洲无码免费在线视频| 日韩中文字码无砖| 精品人妻一区二区免费蜜桃视频| 漂亮人妻吃鸡啪啪哥哥真的好| 一本久久综合亚洲鲁鲁五月天 | 亚洲永久免费| 五月丁香花婷婷| 精品人妻一区二区免费蜜桃| 日本视频一区二区三区| 中文字幕视频2023| 影音先锋女人aV鲁色资源网站| 国产粉嫩在线观看| 高清无码一区二区三区四区| 无码国产av| 人人操人人透| 亚洲无码入口| 婷婷在线观看视频| 国产乱码在线| 高清无码免费观看视频| 超碰在线国产| 天天看天天干| 国产精品久久7777777精品无码| 精品国产久| 69av在线播放| 白峰美羽人妻AND-499| 国产精品不卡在线| 国产又爽又黄A片| 六月婷婷深爱| 国产精品在线免费观看| 欧美色色色色色| 亚洲777| 国产精品人人人人| 无码人妻在线|