mysql replication之GTID

myownstars發表於2015-02-10
 原理

5.6引入,在整個複製拓撲結構內,每個事務擁有自己全域性唯一標識,,每個GTID對應一個事務;

GTID包含兩個部分,一部分是例項的UUID,另一部分是例項內遞增的整數,GTID = server_uuid:transaction_id,

注:第一次啟動mysql會生成server_uuid並存於資料目錄的auto.cnf

一旦GTID對應的事務被執行,會加入當前例項的gtid_executed集合,後續相同GTID的事務則直接被忽略;

流程

主庫執行事務,分配GTID並記入binlog

2 slave解析binlog,將GTID賦給gtid_next並於下一個事務使用此GTIDgtid_next位於session context

3 slave使用GTID前先做檢查,確保其既沒有被使用過(gtid_executed) 也沒有被其他session正在使用(gtid_owned),多個客戶端不可併發執行同一個事務;


配置引數

主備都要宣告 gtid-mode = ON & enforce-gtid-consistency = ON

Gtid_executed例項已執行的所有GTID集合;伺服器啟動時,讀取最新的binlog,賦予union(previous_gtids_log_event, gtid_log_event)

Gtid_purged:已清除的binlog中包含的GTID集合;伺服器啟動時,讀取最舊binlogprevious_gtid_log_event並將其賦值;每purge一個binlog,則重置一次;reset master會將其重置為空;

注:任意時刻,例項當前binlog中可以找到的事務範圍為GTID_SUBTRACT(@@global.gtid_executed, @@global.gtid_purged)

Gtid_owned:只讀變數,分別描述sessionglobal當前擁有的gtid集合;

Gtid_next

指定下一個GTID獲取的方式,有3種選項

1 automatic,使用下一個自動產生的GTID

2 anonymous,不使用GTID

3 GTID(格式為UUID:NUMBER),即下一個事務採用此GTID



轉化replication

對已有的replication,將其轉化為GTID模式;

將主備都設定為read-only,並確保slavemaster同步;

2 masterslave,開啟GTIDbinloglog-slave-updates(備庫)

slaveauto-positioning方式連線到master

去除masterslaveread-only限制;


具體步驟

1

主備均執行SET @@global.read_only = ON;

2

重啟master

mysqld_safe --gtid_mode=ON --log-bin --log-slave-updates --enforce-gtid-consistency &

重啟slave,禁止啟動IOSQL thread

mysqld_safe --gtid_mode=ON --log-bin --log-slave-updates  --skip-slave-start --enforce-gtid-consistency &

: --gtid_mode為列舉而非布林型別故只能宣告on或者off,賦值01會出現問題;--enforce-gtid-consistency則是禁止gtid unsafe語句執行;

3 slave指向master

mysql> CHANGE MASTER TO

     >     MASTER_HOST = host,

     >     MASTER_PORT = port,

     >     MASTER_USER = user,

     >     MASTER_PASSWORD = password,

     >     MASTER_AUTO_POSITION = 1;

注:若要使用file+position模式,須將MASTER_AUTO_POSITION = 0

開啟slave IOSQL執行緒,

mysql> START SLAVE;

去除masterslaveread-only限制

SET @@global.read_only = OFF;


除此之外還有其他方法http://dev.mysql.com/doc/refman/5.6/en/replication-gtids-failover.html



限制

GTID依附於事務,

更新非事務引擎

非事務引擎單條DML被視為一個事務,倘若在一個事務中和innodb混合使用,可能導致單條事務生成多個GTID;

2 create table … select

若採用row-based,該sql常常被分解成兩個獨立事件:建立表;插入資料;

事務可能會為這兩個事件分配同一個GTID,導致slave把後面的insert忽略;

臨時表

手工建立的臨時表應該獨立於任何事務之外(autocommit = 1)

不支援sql_slave_skip_counter

slave若要跳過事務,需設定gtid_executed或gtid_next;

5 mysql_upgrade

其會更新系統表(MyISAM),因此使用時須設定—gtid-mode=off,或者—write-binlog=off(5.6.7預設開啟);



GTID的開銷

 previous_gtid_log_event

位於binlog開頭,記錄此binlog之前的GTID列表(對應@@gtid_purged ),外加40位元組 * number_of_masters

 Gtid_log_event

44位元組,位於每個事務之前;

https://blogs.oracle.com/MySQL/entry/deep_dive_into_gtids_and

 


以下內容來源於  淘寶內部分享:怎麼跳出MySQL的10個大坑  
GTID mutex的改進
GTID的分配包含兩種方式,一種是自動分配,另外一種是顯式設定session.gtid_next,下面簡單介紹下這兩種方式:
自動分配
如果沒有設定session級別的變數gtid_next,所有事務都走自動分配邏輯。分配GTID發生在GROUP COMMIT的第一個階段,也就是flush stage,大概可以描述為:
Step 1:事務過程中,碰到第一條DML語句需要記錄Binlog時,分配一段Gtid事件的cache,但不分配實際的GTID
Step 2:事務完成後,進入commit階段,分配一個GTID並寫入Step1預留的Gtid事件中,該GTID必須保證不在gtid_owned集合和gtid_executed集合中, 分配的GTID隨後被加入到gtid_owned集合中。
Step 3:將Binlog 從執行緒cache中刷到Binlog檔案中。
Step 4:將GTID加入到gtid_executed集合中。
Step 5:在完成sync stage 和commit stage後,各個會話將其使用的GTID從gtid_owned中移除。

問題
由於在例項內,GTID需要保證唯一性,因此不管是操作gtid_executed集合和gtid_owned集合,還是分配GTID,都需要加上一個大鎖。我們的最佳化主要集中在第一種GTID分配方式。
對於GTID的分配,由於處於Group Commit的第一個階段,由該階段的leader執行緒為其follower執行緒分配GTID及刷Binlog,因此不會產生競爭。
而在Step 5,各個執行緒在完成事務提交後,各自去從gtid_owned集合中刪除其使用的gtid。這時候每個執行緒都需要獲取互斥鎖,很顯然,併發越高,這種競爭就越明顯,我們很容易從pt-pmp輸出中看到如下類似的
trace: ha_commit_trans—>MySQL_BIN_LOG::commit—>MySQL_BIN_LOG::ordered_commit—>MySQL_BIN_LOG::finish_commit—>Gtid_state::update_owned_gtids_impl—>lock_sidno
這同時也會影響到GTID的分配階段,導致TPS在高併發場景下的急劇下降。

解決
實際上對於自動分配GTID的場景,並沒有必要維護gtid_owned集合。我們的修改也非常簡單,在自動分配一個GTID後,直接加入到gtid_executed集合中,避免維護gtid_owned,這樣事務提交時就無需去清理gtid_owned集合了,從而可以完全避免鎖競爭。
當然為了保證一致性,如果分配GTID後,寫入Binlog檔案失敗,也需要從gtid_executed集合中刪除。不過這種場景非常罕見。
效能資料
使用sysbench,100張表,每張10w行記錄,update_non_index.lua,純記憶體操作,innodb_flush_log_at_trx_commit = 2,sync_binlog = 1000
併發執行緒       原生               修改後
32           24500              25000
64           27900              29000
128          30800              31500
256          29700              32000
512          29300              31700
1024         27000              31000
從測試結果可以看到,最佳化前隨著併發上升,效能出現下降,而最佳化後則能保持TPS穩定。


顯式設定
使用者透過設定session級別變數gtid_next可以顯式指定一個GTID,流程如下:
Step 1:設定變數gtid_next,指定的GTID被加入到gtid_owned集合中。
Step 2:執行任意事務SQL,在將binlog從執行緒cache刷到binlog檔案後,將GTID加入到gtid_executed集合中。
Step 3:在完成事務COMMIT後,從gtid_owned中移除。

備庫SQL執行緒使用的就是第二種方式,因為備庫在apply主庫的日誌時,要保證GTID是一致的,SQL執行緒讀取到GTID事件後,就根據其中記錄的GTID來設定其gtid_next變數。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-1432655/,如需轉載,請註明出處,否則將追究法律責任。

相關文章