<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                在上一期的月報中,我們分析了 MySQL 5.6 并行復制是如何實現的,介紹了主要數據結構、Coordinator 線程的分發、Worker 線程的執行和checkpoint過程,讀者朋友可以[回顧下](http://mysql.taobao.org/monthly/2015/08/09/ "5.6 并行復制實現分析"),本篇將對恢復邏輯進行介紹。 在并行復制之前,SQL線程的恢復很簡單,從 relay-log.info 中取得上次執行到的位點,然后從這個位點開始執行即可。有了并行復制之后,情況就變得稍微復雜了些,worker 線程各自執行自己隊列的事務,在`stop slave`或者 mysqld crash的時候,隊列中的事務很可能沒有執行完,比如crash時GAQ的狀態如下圖1所示,中間存在空隙(gap),先分發給 worker a 的事務還未完成,而后分發給 worker b 的事務已經完成,對應就是 relay log 中間有一部分event沒執行。我們知道,SQL執行或者分發是順序讀relay log的,如果恢復時從 2 開始執行,3 和 4就會重復執行,如果從4開始執行,2就會被跳過,都不行。并行復制恢復的邏輯就是把 2 找出來執行,把空隙給填上,然后SQL線程就可以 5 開始愉快地跑下去了。 ![GAQ中的空隙](https://box.kancloud.cn/2015-09-24_56039e67ee8e7.png) 圖1\. GAQ中的空隙 ## 信息持久化 恢復離不開信息的持久化,每個worker線程對應一個worker.info,定期將執行位點信息刷入worker.info。類似于relay-log.info,worker.info 可以存在表中,也可以存在文件中,取決于配置`relay_log_info_repository`,刷寫頻率由?`sync_relay_log_info`?控制。 下面是relay-log.info中存的信息: ~~~ Number_of_lines: 后面有多少行(文件)或字段(表) Relay_log_name: 執行到的relay log 的文件名 Relay_log_pos: 執行到的relay log 的位置 Master_log_name: 執行到的對應在主庫 binlog 的文件名 Master_log_pos: 執行到的對應在主庫 binlog 的位置 Sql_delay: SQL線程必須落后master的時間,通過 CHANGE MASTER TO MASTER_DELAY=X 指定 Number_of_workers: worker線程個數 Id: 內部用的 ~~~ 下面是worker.info中存的信息: ~~~ Id: worker 的 id Relay_log_name: 執行到的relay log 的文件名 Relay_log_pos: 執行到的relay log 的位置 Master_log_name: 執行到的對應在主庫 binlog 的文件名 Master_log_pos: 執行到的對應在主庫 binlog 的位置 Checkpoint_relay_log_name: 上次 checkpoint 后,分發到的第一個 group 所在的 relay log 文件名 Checkpoint_relay_log_pos: 同上,對應 relay log 中的位置 Checkpoint_master_log_name: 同上,對應在主庫 binlog 的文件名 Checkpoint_master_log_pos: 同上,對應在主庫 binlog 中的位置 Checkpoint_seqno: 當前執行到事務序列,從上次checkpoint后開始算 Checkpoint_group_size: checkpoint_group_bitmap 的長度,多少個BYTE Checkpoint_group_bitmap: 從上次 checkpoitn 執行事務的標記 ~~~ 每個字段對應Slave_worer類的一個成員(Checkpoint_group_size除外),這其中比較重要的就是 Checkpoint_group_bitmap,記錄哪些事務是執行過的,下面會介紹對bitmap的操作。 ## bitmap 記錄執行事務 本節介紹對?`Slave_worker::group_executed`?這個bitmap的操作,在此之前需要介紹另一個變量?`Relay_log_info::checkpoint_seqno`,對 Coordinator 線程來說,表示從上次checkpoint調整后,下一個分發的事務編號,同時對應GAQ中事務(Slave_job_group)的個數,我們在上期介紹過,GAQ中存的是Coordinator 線程分發的、尚未被checkpoint出隊的事務(可能已經被worker執行完了);對woker線程來說,這個對應當前worker執行到的事務編號。 Coordinator 線程每分發一個事務,checkpoint_seqno 加 1;每次checkpoint后,會將 checkpoint_seqno 減去cnt(cnt為checkpoint時GAQ中出隊的事務的個數)。worker 線程每執行完一個事務,會將 group_executed 的 checkpoint_seqno 位置1;如果遇到checkpoint,會將bitmap向左移位。 如下圖所示,GAQ中第0、2、5個事務分發給了worker a,第0個已經執行完成,所以 worker a 的 bitmap 中,第0位置1;worker b 和 worker c 的 bitmap 同理,標識已經執行的事務。 ![worker線程的bitmap](https://box.kancloud.cn/2015-09-24_56039e684e9d1.png) 圖2\. worker的bitmap 假設這個時候 Coordinator 線程做了一次 checkpoint,將隊列頭部2個已經完成的事務出隊,然后將`rli->checkpoint_seqno`減2,同時將2累加到每個?`worker->bitmap_shifted`?中,當Coordinator 線程將新的事務分給worker的時候,會將?`worker->bitmap_shifted`?取出,存人當前`Slave_job_group.shifted`?中,當worker執行到這個group,就開始對 group_executed 進行偏移,偏移量就是`Slave_job_group.shitfed`?(再一次說明了GAQ中的Slave_job_group,充當了Coordinator 線程和worker線程通信的角色)。bitmap的變化就如下圖所示,checkpoint后,原來的0和1出隊,然后新的4、5、6加入進來,新分發給worker b 和 worker c 的 4 和 6 已經執行完成,所以bitamp和上圖相比,已經向左路偏移了2位,而新分發worker a的5并示執行,所以worker a 的bitmap還未偏移。 ![checkpoint后worker線程的bitmap](https://box.kancloud.cn/2015-09-24_56039e689c88f.png) 圖3\. checkpoint后worker的bitmap group_executed bitmap的長度和GAQ大小一樣,由配置`slave_checkpoint_group`決定。 ## 恢復邏輯 恢復的主要邏輯是`mts_recovery_groups()`?這個函數。 在啟動slave的時候,如果relay-log.info中存的Number_of_workers不為0,就說明之前是并行復制,然后調用?`mts_recovery_groups()`,進入恢復邏輯。如前所述,`mts_recovery_groups()`?的目的就是根據 slave_worker_info 和 slave_info 中信息,把空隙事務找出來。 首先會創建 Number_of_workers 個 worker,依次把每個worker.info的信息讀出來,然后把worker執行位點信息和relay-log.info中記錄的位點信息(低水位)相比,如果比后者小,說明崩潰前已經被checkpoint出隊,不可能造成空隙,直接跳過;如果比后者大,就把worker存入?`above_lwm_jobs`?數組。 `above_lwm_jobs`收集完成后,初始化bitmap?`rli->recovery_groups`,用來匯總每個worker的bitmap。對?`above_lwm_jobs`?中的每個worker,設置一個計數器`recovery_group_cnt`,從低水位位點開始掃relay log,每掃完一個事務,`recovery_group_cnt`加1,直到掃到worker.info中記錄的位點為止,之后把worker的bitmap匯總到`rli->recovery_groups`中,其間會統計一個最大的?`recovery_group_cnt`,記入`rli->mts_recovery_group_cnt`,這個對應高水位。 bitmap 匯總邏輯如下: ~~~ sql/rpl_slave.cc:8965 for (uint i= (w->checkpoint_seqno + 1) - recovery_group_cnt, j= 0; i <= w->checkpoint_seqno; i++, j++) { if (bitmap_is_set(&w->group_executed, i)) { DBUG_PRINT("mts", ("Setting bit %u.", j)); bitmap_fast_test_and_set(groups, j); } } ~~~ 之后SQL線程就可以從低水位往高水位掃relay log,對于每個事務,如果?`rli->recovery_groups`?對應bit為1,說明崩潰前已經執行過,就跳過;反之,就對事務中的每個event調用?`do_apply_event()`執行。掃描到高水位后整個恢復邏輯結束,后面SQL線程就進入正常的執行邏輯,執行(串行)或者分發(并行)event。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看