溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

從庫的SQL線程和sql_slave_skip_counter參數(shù)分析

發(fā)布時間:2021-11-08 11:00:04 來源:億速云 閱讀:222 作者:iii 欄目:MySQL數(shù)據(jù)庫

本篇內(nèi)容主要講解“從庫的SQL線程和sql_slave_skip_counter參數(shù)分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“從庫的SQL線程和sql_slave_skip_counter參數(shù)分析”吧!

一、調(diào)用流程大概如下

handle_slave_sql
 ->是否開啟了slave_preserve_commit_order和log_slave_updates參數(shù),開啟的話需要設(shè)置提交順序管理器
   if (opt_slave_preserve_commit_order && rli->opt_slave_parallel_workers > 0 &&
       opt_bin_log && opt_log_slave_updates)
     commit_order_mngr= new Commit_order_manager(rli->opt_slave_parallel_workers); //order commit 管理器
   rli->set_commit_order_manager(commit_order_mngr);
 ->如果是MTS則需要啟動worker線程
   if (slave_start_workers(rli, rli->opt_slave_parallel_workers, &mts_inited) != 0)//啟動worker線程
  {
    mysql_cond_broadcast(&rli->start_cond);
    mysql_mutex_unlock(&rli->run_lock);
    rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR),
                "Failed during slave workers initialization");
    goto err;
  ->檢查rep table是否是事務(wù)類型的如果不是則報警告
     if (!rli->is_transactional()) //是否是 table或者是file類型是table類型則支持事物
    rli->report(WARNING_LEVEL, 0,
    "If a crash happens this configuration does not guarantee that the relay "
    "log info will be consistent");
  -> 初始化 relay log 的訪問位置
      if (rli->init_relay_log_pos(rli->get_group_relay_log_name(),
                              rli->get_group_relay_log_pos(),
                              true/*need_data_lock=true*/, &errmsg,
                              1 /*look for a description_event*/)) //初始化 relay log 的訪問位置
     這個位置比較關(guān)鍵也就是從哪里開始讀取我們的relay log。如果出現(xiàn)錯誤將會導(dǎo)致讀取的relay log錯誤。
     因此我們需要保證rep info的安全,如果設(shè)置了recover relay log 那么將會初始化為最新一個relay log的
     開始位置,因為所有的未執(zhí)行的binlog event將會從新拉取,老的relay log 已經(jīng)不重要了。后面再說。
  -> GTID event沒有辦法使用sql_slave_skip_counter 其具體含義參考:
    Log_event::do_shall_skip
    mysql> set global sql_slave_skip_counter=1;
    ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with 
    @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an 
    empty transaction with the same GTID as the transaction
  進(jìn)入循環(huán) 知道SQL線程被殺死
  -> 進(jìn)入狀態(tài)stage_reading_event_from_the_relay_log
  -> 進(jìn)行一段skip event的判斷和日志輸出
    GTID event沒有辦法使用sql_slave_skip_counter 其具體含義參考:
    Log_event::do_shall_skip
    mysql> set global sql_slave_skip_counter=1;
    ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with 
    @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an 
    empty transaction with the same GTID as the transaction  
  -> exec_relay_log_event 讀取應(yīng)用 一個event的上層接口
    ->next_event 讀取下一個Event 完成MTS的檢查點
      ->獲取開始位置 rli->set_event_start_pos(my_b_tell(cur_log));
      ->Log_event::read_log_event
      ->如果是MTS 是否需要進(jìn)行檢查點
        1、是否超過檢查點周期
           周期檢查在函數(shù)mts_checkpoint_routine內(nèi)部
             set_timespec_nsec(&curr_clock, 0);
             ulonglong diff= diff_timespec(&curr_clock, &rli->last_clock);
              if (!force && diff < period)
              {
                /*
                  We do not need to execute the checkpoint now because
                  the time elapsed is not enough.
                */
                DBUG_RETURN(FALSE);
              }
        2、是否已經(jīng)GAQ已經(jīng)滿了 
          bool force= (rli->checkpoint_seqno > (rli->checkpoint_group - 1)); //如果達(dá)到了 GAQ的大小 設(shè)置為force 強(qiáng)制checkpoint 
      ->是否relay log 大小已經(jīng)達(dá)到最大 是否需要relay log切換
        但是需要注意如果本事物沒有結(jié)束不能進(jìn)行切換
    /*                                                                                                                                          
              If we have reached the limit of the relay space and we  如果我們達(dá)到 relay_log_space_limit 上限 需要通知IO THREAD進(jìn)行切換 清理空間```
              are going to sleep, waiting for more events:                                                                                      
              1. If outside a group, SQL thread asks the IO thread                                                                              
                 to force a rotation so that the SQL thread purges                                                                              
                 logs next time it processes an event (thus space is                                                                            
                 freed).                                                                                                                        
              2. If in a group, SQL thread asks the IO thread to                                                                                
                 ignore the limit and queues yet one more event                                                                                 
                 so that the SQL thread finishes the group and                                                                                  
                 is are able to rotate and purge sometime soon.                                                                                 
             */                                                                                                                                 
            if (rli->log_space_limit &&                                                                                                         
                rli->log_space_limit < rli->log_space_total)                                                                                    
            {                                                                                                                                   
              /* force rotation if not in an unfinished group */                                                                                
              if (!rli->is_parallel_exec())                                                                                                     
              {                                                                                                                                 
                rli->sql_force_rotate_relay= !rli->is_in_group(); //如果不是一組就需要切換                                                      
              }                                                                                                                                 
              else                                                                                                                              
              {                                                                                                                                 
                rli->sql_force_rotate_relay=                                                                                                    
                  (rli->mts_group_status != Relay_log_info::MTS_IN_GROUP);                                                                      
              }                                                                                                                                 
              /* ask for one more event */                                                                                                      
              rli->ignore_log_space_limit= true;//是一組 不能切換                                                                               
            }           
    ```                    
    ->如果讀取了當(dāng)前relay log的全部的relay log event,
     ->如果是當(dāng)前relay log
       ->空閑狀態(tài)下等待io 線程的喚醒,如果是MTS還需要定期醒來進(jìn)行檢查點,如下:
         ```  
         if (rli->is_parallel_exec() && (opt_mts_checkpoint_period != 0 ||
          DBUG_EVALUATE_IF("check_slave_debug_group", 1, 0)))
      {
        int ret= 0;
        struct timespec waittime;
        ulonglong period= static_cast<ulonglong>(opt_mts_checkpoint_period * 1000000ULL);
        ulong signal_cnt= rli->relay_log.signal_cnt;
        mysql_mutex_unlock(log_lock);
        do
        {
          /*
            At this point the coordinator has no job to delegate to workers.
            However, workers are executing their assigned jobs and as such
            the checkpoint routine must be periodically invoked.
          */
          (void) mts_checkpoint_routine(rli, period, false, true/*need_data_lock=true*/); // TODO: ALFRANIO ERROR
          mysql_mutex_lock(log_lock);
          if (DBUG_EVALUATE_IF("check_slave_debug_group", 1, 0))
            period= 10000000ULL;
          set_timespec_nsec(&waittime, period);
          ret= rli->relay_log.wait_for_update_relay_log(thd, &waittime);
        } while ((ret == ETIMEDOUT || ret == ETIME) /* todo:remove */ &&
                 signal_cnt == rli->relay_log.signal_cnt && !thd->killed);
      }
      else
      {
        rli->relay_log.wait_for_update_relay_log(thd, NULL); //等待relay log 更改的信號 SQL THREAD 會等待在這里
      }        
         ```     
     -> 如果不是當(dāng)前relay log 那么 SQL線程應(yīng)用或者分發(fā)完成完成后就可以清理了
        并且參數(shù)relay_log_purge需要設(shè)置為1     
        if (rli->relay_log.purge_first_log
        (rli,
         rli->get_group_relay_log_pos() == rli->get_event_relay_log_pos()
         && !strcmp(rli->get_group_relay_log_name(),rli->get_event_relay_log_name())))//做relay log的清理
-> 如果是單SQL現(xiàn)成 獲取event的時間
   這一步 就是獲取計算延遲的重要因素,但是注意MTS不是在這里實在檢查點里面
   last_master_timestamp
   ```
   rli->last_master_timestamp= ev->common_header->when.tv_sec + //event header 的timestamp
                              (time_t) ev->exec_time; //獲取event的 timestamp作為 計算last_master_timestamp的基礎(chǔ)數(shù)據(jù) query event才有的執(zhí)行時間
   DBUG_ASSERT(rli->last_master_timestamp >= 0);       //但是對于MTS來講應(yīng)該注意是最后一個XID EVENT的 時間不是這里設(shè)置的 在mts_checkpoint_routine里面
   ```
-> 如果GITD_MODE 且AUTO_POSITION 且是MTS需要由協(xié)調(diào)線程進(jìn)行半事物的恢復(fù) (partial transaction)    
   構(gòu)造回滾EVENT進(jìn)行恢復(fù),而對已非MTS會在gtid event做回滾。
   這種情況可能出現(xiàn)在:
   - AUTO_POSITION情況下如果重連,會重新發(fā)送已經(jīng)傳輸?shù)腅vent。
   - AUTO_POSITION情況下如果從庫異常宕機(jī)重啟,并且recovery_relay_log=0的情況下,會重新發(fā)送已經(jīng)傳輸?shù)腅vent,并且relay log pos不會重置
   因此我們前面在IO線程和DUMP線程中已經(jīng)討論了,每次sql線程的啟動都會通過GTID去重新尋找需要拉取的
   位置。
   coord_handle_partial_binlogged_transaction(rli, ev) 
-> apply_event_and_update_pos 非MTS 完成 應(yīng)用 MTS完成分發(fā)
  -> 進(jìn)行skip event操作
  -> 維護(hù)skip counter計數(shù)器
       if (reason == Log_event::EVENT_SKIP_COUNT)
          {
            --rli->slave_skip_counter;//維護(hù)skip count
            skip_event= TRUE;
          }
     我們看到slave_skip_counter是以event為單位的,但是對于最后一個event如果跨事務(wù)了
     那么整個事物都需要跳過。但是skip在GTID模式下是不能用的。       
  -> 如果不能跳過的事務(wù) 就需要應(yīng)用了。MTS則完成分發(fā)
     ->完成延遲應(yīng)用邏輯
       sql_delay_event(ev, thd, rli)
     ->ev->apply_event(rli); 這里單SQL線程應(yīng)用 MTS完成分發(fā),分發(fā)方式參考前面
       ->是否是進(jìn)行 MTS recovery if (rli->is_mts_recovery())
          根據(jù) bitmap 設(shè)置進(jìn)行跳過處理 
           if (rli->is_mts_recovery())//如果是恢復(fù) 這個地方就是前面恢復(fù)掃描出來的位置
           {
             bool skip=
               bitmap_is_set(&rli->recovery_groups, rli->mts_recovery_index) &&
               (get_mts_execution_mode(::server_id,
                                       rli->mts_group_status ==
                                       Relay_log_info::MTS_IN_GROUP,
                                       rli->current_mts_submode->get_type() ==
                                       MTS_PARALLEL_TYPE_DB_NAME)
                == EVENT_EXEC_PARALLEL);
             if (skip)
             {
               DBUG_RETURN(0);
             }
             else
             {
               DBUG_RETURN(do_apply_event(rli));
             }
           }

到此,相信大家對“從庫的SQL線程和sql_slave_skip_counter參數(shù)分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

sql
AI