1. 程式人生 > >MySQL半同步複製--transmit_start

MySQL半同步複製--transmit_start

介紹

半同步複製binlog dump執行緒需要做的事情。相關結構:

Binlog_transmit_observer transmit_observer = {
  sizeof(Binlog_transmit_observer), // len

  repl_semi_binlog_dump_start,	// start
  repl_semi_binlog_dump_end,	// stop
  repl_semi_reserve_header,	// reserve_header
  repl_semi_before_send_event,	// before_send_event
  repl_semi_after_send_event,	// after_send_event
  repl_semi_reset_master,	// reset
};
/**
   Observe and extends the binlog dumping thread.
*/
typedef struct Binlog_transmit_observer {
  uint32 len;
  
  /**
     This callback is called when binlog dumping starts


     @param param Observer common parameter
     @param log_file Binlog file name to transmit from
     @param log_pos Binlog position to transmit from

     @retval 0 Sucess
     @retval 1 Failure
  */
  int (*transmit_start)(Binlog_transmit_param *param,
                        const char *log_file, my_off_t log_pos);

  /**
     This callback is called when binlog dumping stops

     @param param Observer common parameter
     
     @retval 0 Sucess
     @retval 1 Failure
  */
  int (*transmit_stop)(Binlog_transmit_param *param);

  /**
     This callback is called to reserve bytes in packet header for event transmission

     This callback is called when resetting transmit packet header to
     reserve bytes for this observer in packet header.

     The @a header buffer is allocated by the server code, and @a size
     is the size of the header buffer. Each observer can only reserve
     a maximum size of @a size in the header.

     @param param Observer common parameter
     @param header Pointer of the header buffer
     @param size Size of the header buffer
     @param len Header length reserved by this observer

     @retval 0 Sucess
     @retval 1 Failure
  */
  int (*reserve_header)(Binlog_transmit_param *param,
                        unsigned char *header,
                        unsigned long size,
                        unsigned long *len);

  /**
     This callback is called before sending an event packet to slave

     @param param Observer common parameter
     @param packet Binlog event packet to send
     @param len Length of the event packet
     @param log_file Binlog file name of the event packet to send
     @param log_pos Binlog position of the event packet to send

     @retval 0 Sucess
     @retval 1 Failure
  */
  int (*before_send_event)(Binlog_transmit_param *param,
                           unsigned char *packet, unsigned long len,
                           const char *log_file, my_off_t log_pos );

  /**
     This callback is called after an event packet is sent to the
     slave or is skipped.

     @param param             Observer common parameter
     @param event_buf         Binlog event packet buffer sent
     @param len               length of the event packet buffer
     @param skipped_log_file  Binlog file name of the event that
                              was skipped in the master. This is
                              null if the position was not skipped
     @param skipped_log_pos   Binlog position of the event that
                              was skipped in the master. 0 if not
                              skipped
     @retval 0 Sucess
     @retval 1 Failure
   */
  int (*after_send_event)(Binlog_transmit_param *param,
                          const char *event_buf, unsigned long len,
                          const char *skipped_log_file, my_off_t skipped_log_pos);

  /**
     This callback is called after resetting master status

     This is called when executing the command RESET MASTER, and is
     used to reset status variables added by observers.

     @param param Observer common parameter

     @retval 0 Sucess
     @retval 1 Failure
  */
  int (*after_reset_master)(Binlog_transmit_param *param);
} Binlog_transmit_observer;

transmit_start

本節主要介紹transmit_start這個函式。

com_binlog_dump->mysql_binlog_send->RUN_HOOK(binlog_transmit, transmit_start,
               (thd, flags, log_ident, pos, &observe_transmission))
每次申請dump請求,都會執行transmit_start這個函式。該函式呼叫repl_semi_binlog_dump_start。

程式碼分析

int repl_semi_binlog_dump_start(Binlog_transmit_param *param,
				 const char *log_file,
				 my_off_t log_pos)
{
  //檢查slave的semi-sync是否開啟
  bool semi_sync_slave= repl_semisync.is_semi_sync_slave();
  
  if (semi_sync_slave)
  {
    /* 至少有一個slave開啟了半同步,將rpl_semi_sync_master_clients++*/
	//是在LOCK_binlog_鎖內進行增加
    repl_semisync.add_slave();//rpl_semi_sync_master_clients++;
    /* Tell server it will observe the transmission.*/
    param->set_observe_flag();

    /*
      Let's assume this semi-sync slave has already received all
      binlog events before the filename and position it requests.
    */
    repl_semisync.reportReplyBinlog(param->server_id, log_file, log_pos);
  }
  else
    param->set_dont_observe_flag();
  
  return 0;
}
1、is_semi_sync_slave()->get_user_var_int("rpl_semi_sync_slave", &val, &null_value);
      檢查slave是否是半同步。注:
      slave的before_request_transmit傳送SET @rpl_semi_sync_slave= 1將master的rpl_semi_sync_slave變數置成1

2、在binlog dump執行緒中做transmit_start操作。對於每次DUMP請求,都會首先做這個動作。
     該函式的主要動作:
int ReplSemiSyncMaster::reportReplyBinlog(uint32 server_id,
                                          const char *log_file_name,
                                          my_off_t log_file_pos,
                                          bool skipped_event)
{
  const char *kWho = "ReplSemiSyncMaster::reportReplyBinlog";
  int   cmp;
  bool  can_release_threads = false;
  bool  need_copy_send_pos = true;
  //判斷master半同步是否開啟:master_enabled_,這個變數和state_關係?
  if (!(getMasterEnabled()))
    return 0;

  function_enter(kWho);
  //LOCK_binlog_鎖
  lock();

  /* 鎖內再次判斷是否已經開啟半同步 */
  if (!getMasterEnabled())
    goto l_end;
  //state_如果半同步由於超時暫時關閉,嘗試重新開啟
  if (!is_on())
    try_switch_on(server_id, log_file_name, log_file_pos);

  //第一個slave請求dump,需要複製響應位置,並將reply_file_name_inited_置1
  //否則,比較當前的位置,如果比已響應的最大位置小,則不需要調整已響應最大位置
  //如果大,則需要調整已響應最大位置
  if (reply_file_name_inited_)
  {
    cmp = ActiveTranx::compare(log_file_name, log_file_pos,
                               reply_file_name_, reply_file_pos_);
    if (cmp < 0)
    {
      /* If the position is behind, do not copy it. */
      need_copy_send_pos = false;
    }
  }

  if (need_copy_send_pos)
  {
    strncpy(reply_file_name_, log_file_name, sizeof(reply_file_name_) - 1);
    reply_file_name_[sizeof(reply_file_name_) - 1]= '\0';
    reply_file_pos_ = log_file_pos;
    reply_file_name_inited_ = true;
  }
  //等待slave回覆ACK的事務有多個
  if (rpl_semi_sync_master_wait_sessions > 0)
  {
    //比較接收到的slave的ACK,reply的值比當前wait_file_name的大
    cmp = ActiveTranx::compare(reply_file_name_, reply_file_pos_,
                               wait_file_name_, wait_file_pos_);
    if (cmp >= 0)
    {
	  //可以釋放等待slave的ACK的事務了
	  //這意味著新的等待事務,需要重新設定等待位點資訊(一組?)
      can_release_threads = true;
      wait_file_name_inited_ = false;
    }
  }

 l_end:

  if (can_release_threads)
  {
    active_tranxs_->signal_waiting_sessions_up_to(reply_file_name_, reply_file_pos_);
  }
  unlock();
  return function_exit(kWho, 0);
}
int ReplSemiSyncMaster::try_switch_on(int server_id,
				      const char *log_file_name,
				      my_off_t log_file_pos)
{
  const char *kWho = "ReplSemiSyncMaster::try_switch_on";
  bool semi_sync_on = false;

  function_enter(kWho);

  //如果當前傳送event的位置比最大提交的事務binlog位置還要大,slave已經跟上master,即
  //slave接收的event已經跟上master,將半同步重新開啟。
  //如果commit_file_name_inited_為FALSE,表示沒有事務,可以立即開啟半同步
  if (commit_file_name_inited_)
  {
    int cmp = ActiveTranx::compare(log_file_name, log_file_pos,
                                   commit_file_name_, commit_file_pos_);
    semi_sync_on = (cmp >= 0);
  }
  else
  {
    semi_sync_on = true;
  }

  if (semi_sync_on)
  {
    /* 將半同步開啟 */
    state_ = true;
  }
  return function_exit(kWho, 0);
}
int ActiveTranx::signal_waiting_sessions_up_to(const char *log_file_name,
                                               my_off_t log_file_pos)
{
  const char *kWho = "ActiveTranx::signal_waiting_sessions_up_to";
  function_enter(kWho);
  //掃描所有正在等待的事務。如果其位點小,表示slave已經收到其binlog,可以廣播到使用者執行緒喚醒。
  TranxNode* entry= trx_front_;
  int cmp= ActiveTranx::compare(entry->log_name_, entry->log_pos_, log_file_name, log_file_pos) ;
  while (entry && cmp <= 0)
  {
    mysql_cond_broadcast(&entry->cond);
    entry= entry->next_;
    if (entry)
      cmp= ActiveTranx::compare(entry->log_name_, entry->log_pos_, log_file_name, log_file_pos) ;
  }
  return function_exit(kWho, (entry != NULL));
}

總結

1、如果slave開啟半同步,那麼會向master註冊:repl_semisync.is_semi_sync_slave->rpl_semi_sync_slave會被置成TRUE。所以首先獲取該變數,判斷是否至少有一個slave開啟slave。

2、至少有一個slave開啟半同步下:LOCK_binlog_鎖內增加rpl_semi_sync_master_clients值(增加連線的備庫計數

3、reportReplyBinlog:

    1)判斷master是否開啟半同步。通過master_enabled_變數判斷。(注,和state_什麼關係?)

    2)master開啟半同步下。加LOCK_binlog_鎖,在這個鎖內再次判斷master是否開啟半同步,如果沒有開啟則退出。

    3)通過state_判斷半同步是否正常,如果半同步由於超時關閉,嘗試重新開啟:

           如果當前傳送的event位置比最大提交的事務binlog位置還要大,表示slave已經跟上master,即slave已經接收到master所有binlog,此時將半同步重新開啟。

           如果commit_file_name_inited_為FALSE,表示沒有事務,可以立即開啟半同步。

    4)第一個開啟slave,請求dump,需要儲存複製響應位置reply_file_name_並將reply_file_name_inited_設為TRUE

    5)否則,比較當前reply_file_name_位置。如果比reply_file_name_小,則需要將ACK的binlog位置儲存下來,需要調整最大響應位置。

    6)等待slave回覆ACK的事務有多個:

         比較接收slave的ACK,reply值比當前wait_file_name_還要打,則需要釋放等待slave ACK的事務。

    7)通過遍歷ActiveTranx連結串列,比較所有等待事務的binlog位置,如果等待事務的binlog的位點小,表示slave已經收到其binlog,可以broadcast,將等待事務的執行緒喚醒。

相關推薦

MySQL同步複製--transmit_start

介紹半同步複製binlog dump執行緒需要做的事情。相關結構:Binlog_transmit_observer transmit_observer = { sizeof(Binlog_transmit_observer), // len repl_semi_bi

《深入淺出MySQL:資料庫開發、優化與管理維護(2nd)》第31章之MySQL同步複製搭建學習筆記

MySQL的非同步複製在使用的過程中,主庫和從庫的資料之間存在一定的延遲,這樣存在一個隱患:當在主庫上寫入一個事務並提交成功,而從庫尚未得到主庫推送的Binlog日誌時,主庫宕機了,例如主庫可能因磁碟損壞、記憶體故障等造成主庫上該事務Binlog丟失,此時從庫就可能損失這個事務,從而造成主從不一致。

Mysql 同步複製和非同步複製

mysql 半同步複製和非同步複製 -- 在主庫中安裝半同步外掛,開啟半同步複製功能 install plugin rpl_semi_sync_master soname 'semisync_master.so'; set global rpl_semi_sync_master_enab

MySQL同步複製

MySQL資料庫複製的預設方式是非同步複製,但是非同步複製的不足之處就在於,當主庫把event寫入二進位制日誌之後,並不知道從庫是否已經接收並應用了。在非同步模式的複製,如果主庫崩潰,很有可能在主庫中已經提交的事務,並沒有傳到到任何一臺從庫機器上。在高可用叢集

支援MySQL同步複製的virtual_slave元件

文章目錄 一、簡介 二、新的技術架構 三、virtual_slave 一、簡介 在設計高可用架構時,為了保證主從故障切換時的資料一致性,有各種處理方式。在5.5/5.6的單機房資料庫架構中,我們採取了共享儲存的

MySQL 同步複製+MMM架構

介紹     上篇文章介紹了MMM架構的實現方法,但是上篇文章的MMM方案的複製是非同步複製,非同步複製的主要問題在於當主從存在延時時如果主機出現了故障導致了主從切換時這時將會存在資料丟失;mysql為了解決非同步複製資料丟失的問題增加了半同步複製,半同步複製存在5.5以上的版本,半同步複製的原理是客戶

Mysql同步複製模式說明 - 運維小結

  MySQL主從複製包括非同步模式、半同步模式、GTID模式以及多源複製模式,預設是非同步模式 (如之前詳細介紹的mysql主從複製)。所謂非同步模式指的是MySQL 主伺服器上I/O thread 執行緒將二進位制日誌寫入binlog檔案之後就返回客戶端結果,不會考慮二進位制日誌是否完整傳輸到

Mysql同步複製模式

MySQL主從複製包括非同步模式、半同步模式、GTID模式以及多源複製模式,預設是非同步模式 (如之前詳細介紹的mysql主從複製)。所謂非同步模式指的是MySQL 主伺服器上I/O thread 執行緒將二進位制日誌寫入binlog檔案之後就返回客戶端結果,不會考慮二進位制日誌是否完整傳輸

MySQL同步複製--handle_slave_io--5

handle_slave_io函式呼叫read_event函式讀取event後,然後呼叫queue_event將讀取的event寫入relay log檔案中。程式碼如下:static int queue_event(Master_info* mi,const char* bu

Mysql同步複製、資料一致性檢查

1:配置非同步複製 scripts/mysql_install_db --user=mysql --datadir=/mysql/data bin/mysqld_safe --user=mysql & 在master上建立複製使用者: mysql> GRANT

mysql 同步複製(semi_sync_replication)搭建及使用

      google為mysql開發了一個補丁一個基於半同步的補丁,應用與mysql5.0。回來mysql打上了該補丁,並在5.5版本中使用。半同步複製的理念是什麼呢?在資料庫更改操作執行前,確保更改操作至少被寫入一臺slave磁碟中,意味著著對於每一個連線,最多隻有一

MySQL同步複製原理配置與介紹

環境介紹: Ubuntu Server 16.04.2+MySQL 5.7.17 Community Server (GPL) MySQL安裝 1、下載mysql-apt-config_0.8.3-1_all.deb 2、安裝deb

mysql同步複製&組複製&全同步機制

先配置好主從 配置主從詳見上一篇部落格,這裡只是簡單過一邊 mysql> grant replication slave on *.* to 'haha'@'172.25.53.%' identified by '[email protect

mysql同步複製搭建及驗證測試

非同步複製:客戶端提交日誌後,主庫寫入日誌到binlog,即可成功返回給客戶端。 半同步複製:客戶端提交日誌後,主庫寫入日誌到binlog,需等待其中一個slave也接收到binlog事務併成功寫入relay log後,主庫才返回commmit操作成功給客戶端。 如果主庫與

mysql GTID 同步複製

1)什麼是GTID GTID(Global Transaction ID)是對於一個已提交事務的編號,並且是一個全域性唯一的編號。GTID實際上是由UUID+TID組成的。其中UUID是一個MySQL例項的唯一標 識,儲存在mysql資料目錄下的auto.cnf檔案裡。TID代表了該例項上已經提交的事務數量

MySQL主從複製,並行複製同步複製和組複製

主從複製 主從複製過程存在三個執行緒,Master端的I/O執行緒,Slave的I/O執行緒與SQL執行緒。Master端需要開啟binlog日誌,Slave端需要開啟relaylog。 1、Slave端的I/O讀取master.info檔案,獲取binlog檔名和位置點,然後向Mast

MySQL高可用方案 MHA之四 keepalived 同步複製

    [[email protected] ~]# cat /etc/mysql_mha/app1.cnf [server default]manager_log=/data/mysql_mha/app1-manager.logmanager_workdir=/data/m

mysql 主從複製 基於gtid的同步複製,並行複製同步複製

一、mysql 主從複製 1.主從形式 mysql主從複製 靈活 一主一從 主主複製 一主多從---擴充套件系統讀取的效能,因為讀是在從庫讀取的; 多主一從---5.7開始支援 聯級複製--- 2.主從複製的用途及部署條件 mysql主從複製用途 實時災備,

mysql 架構篇系列 4 複製架構一主一從搭建(同步複製)

一.概述   在mysql 5.5之前,mysql 的複製是非同步操作,主庫和從庫的資料之間存在一定的延時,這樣存在一個隱患:當主庫上寫入一個事務並提交成功,而從庫尚未得到主庫推送的Binlog日誌時,主庫down機了,事務Binlog丟失了,此時從庫就缺失了這個事務,從而造成主從不一致。    為了解決這個

MySQL主從複製同步複製原理及搭建

在MySQL5.5之前的版本中,MySQL的複製是非同步複製,主庫和從庫的資料之間存在一定的延遲,比如網路故障等各種原因,這樣子容易存在隱患就是:當在主庫寫入一個事務成功後並提交了,但是由於從庫延遲沒有及時得到主庫推送的Binlog日誌時,主庫突然宕機了,那麼此時從庫就可能損失這個事務,從而造成主從不一致的狀