mysql GTID 主從複製概述

賀子_DBA時代發表於2018-01-03
一、GTID的概述:
1、全域性事物標識:global transaction identifieds。
2、GTID事物是全域性唯一性的,且一個事務對應一個GTID。
3、一個GTID在一個伺服器上只執行一次,避免重複執行導致資料混亂或者主從不一致。
4、GTID用來代替classic的複製方法,不在使用binlog+pos開啟複製。而是使用master_auto_postion=1的方式自動匹配GTID斷點進行復制。
5、MySQL-5.6.5開始支援的,MySQL-5.6.10後開始完善。
6、在傳統的slave端,binlog是不用開啟的,但是在GTID中,slave端的binlog是必須開啟的,目的是記錄執行過的GTID(強制)。

二、GTID的組成部分:
前面是server_uuid:後面是一個序列號
例如:server_uuid:sequence number
7800a22c-95ae-11e4-983d-080027de205a:10
UUID:每個mysql例項的唯一ID,由於會傳遞到slave,所以也可以理解為源ID。
Sequence number:在每臺MySQL伺服器上都是從1開始自增長的序列,一個數值對應一個事務。
三、GTID比傳統複製的優勢:
1、更簡單的實現failover,不用以前那樣在需要找log_file和log_Pos。
2、更簡單的搭建主從複製。
3、比傳統複製更加安全。
4、GTID是連續沒有空洞的,因此主從庫出現資料衝突時,可以用新增空事物的方式進行跳過。


四、GTID的工作原理:

1、master更新資料時,會在事務前產生GTID,一同記錄到binlog日誌中。
2、slave端的i/o 執行緒將變更的binlog,寫入到本地的relay log中。
3、sql執行緒從relay log中獲取GTID,然後對比slave端的binlog是否有記錄。
4、如果有記錄,說明該GTID的事務已經執行,slave會忽略。
5、如果沒有記錄,slave就會從relay log中執行該GTID的事務,並記錄到binlog。
6、在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有就用全部掃描。

要點:
1、slave在接受master的binlog時,會校驗master的GTID是否已經執行過(一個伺服器只能執行一次)。
2、為了保證主從資料的一致性,多執行緒只能同時執行一個GTID。
六、使用GTID搭建mysql的主從複製的主要引數:
[mysqld]
#GTID:
gtid_mode=on
enforce_gtid_consistency=on
server_id=2003306 #每天例項的server_id都要不一樣
#binlog
log-bin=mysqlbin
log-slave-updates=1 #允許下端接入slave
binlog_format=row #強烈建議,其他格式可能造成資料不一致
#relay log
skip_slave_start=1
注意:建議使用mysql-5.6.5以上的最新版本。
(二)、啟動GTID的兩種方法:
方法一、
1、如果是在已經跑的伺服器,你需要重啟一下mysql server。
2、啟動之前,一定要先關閉master的寫入,保證所有slave端都已經和master端資料保持同步。
3、所有slave需要加上skip_slave_start=1的配置引數,避免啟動後還是使用老的複製協議。

方法二、
1、如果是新搭建的伺服器,直接啟動就行了。
七、master-slave搭建的注意事項:
(一)、使用GTID的方式,把salve端掛載master端:
1、啟動以後最好不要立即執行事務,而是先change master上。
2、然後在執行事務,當然知不是必須的。
3、使用下面的sql切換slave到新的master。
stop slave;
change master to
master_host = 192.168.100.200,
master_port = 3306,
master_user = abobo,
master_password=123,
master_auto_position = 1;
(二)、如果給已經執行的GTID的master端新增一個新的slave
有兩種方法:

方法一、適用於master也是新建不久的情況。
1、如果你的master所有的binlog還在。可以選擇類似於上面的方法,安裝slave,直接change master to到master端。
2、原理是直接獲取master所有的GTID並執行。
3、優點:簡單方便。
4、缺點:如果binlog太多,資料完全同步需要時間較長,並且master一開始就啟用了GTUD。

方法二、適用於擁有較大資料的情況。(推薦)
1、透過master或者其他slave的備份搭建新的slave。(看第三部分)
2、原理:獲取master的資料和這些資料對應的GTID範圍,然後透過slave設定@@global.gtid_purged跳過備份包含的gtid。
3、優點:是可以避免第一種方法的不足。
4、缺點:相對來說有點複雜。

(三)、透過備份搭建新的slave:(方法二的擴充套件)
兩種方法:
方法一、mysqldump的方式:
1、在備份的時候指定--master-data=2(來儲存binlog的檔案號和位置的命令)。
2、使用mysqldump的命令在dump檔案裡可以看到下面兩個資訊:
SET @@SESSION.SQL_LOG_BIN=0;
SET @@GLOBAL.GTID_PURGED='7800a22c-95ae-11e4-983d-080027de205a:1-8';
3、將備份還原到slave後,使用change master to命令掛載master端。
注意:在mysql5.6.9以後的命令才支援這個功能。
方法二、percona Xtrabackup
1、Xtrabackup_binlog_info檔案中,包含global.gtid_purged='XXXXXX:XXXX'的資訊。
2、然後到slave去手工的 SET GLOBAL.GTID_PURGED='XXXXXX:XXXX'。
3、恢復備份,開啟change master to 命令。
注意:如果系統執行了很久,無法找到GTID的編號了,可以透過上面的方式進行查詢。
八、GTID如何跳過事務衝突:
1、這個功能主要跳過事務,代替原來的set global sql_slave_skip_counter = 1。
2、由於在這個GTID必須是連續的,正常情況同一個伺服器產生的GTID是不會存在空缺的。所以不能簡單的skip掉一個事務,只能透過注入空事物的方法替換掉一個實際操作事務。
3、注入空事物的方法:
stop slave;
set gtid_next='xxxxxxx:N';
begin;commit;
set gtid_next='AUTOMAIC';
start slave;
4、這裡的xxxxx:N 也就是你的slave sql thread報錯的GTID,或者說是你想要跳過的GTID。
九、GTID的引數註釋:
[master]>show global variables like '%gtid%';
1、enforce_gtid_consistency:開啟gtid的一些安全限制(介意開啟)。
2、gtid_executed:全域性和seeeion級別都可以用。用來儲存已經執行過的GTIDs。
貼士:show master status\G;輸出結果中的Executed_Gtid_Set和gitd_executed一致。reset master時,此值會被清空。
3、gtid_owned:全域性和session級別都可用,全域性表示所有伺服器擁有GTIDs,session級別表示當前client擁有所有GTIDs。(此功能用的少)
4、gtid_mode:是否開啟GTID功能。
5、gtid_purged:全域性引數,設定在binlog中,已經purged的GTIDs,並且purged掉的GTIDs會包含到gtid_executed中。
貼士:從而導致slave不會再去master請求這些GTIDs,並且Executed_Gtid_Set為空時,才可以設定此值。
6、gtid_next:這個時session級別的引數:
[master]>show session variables like '%gtid_next%';

十、關於GTID的一些功能限制:
(一)、更新非事務引擎:
1、Case重現:
master:對一個innodb表做一個多sql更新的事物,效果是產生一個GTID。
slave:對應的表是MYISAM引擎,執行這個GTID的第一個語句後就會報錯,因為非事務引擎一個sql就是一個事務。
2、錯誤編號:
last_Errno:1756
3、異常恢復方案:
(1)、簡單的stop slave; start slave;就能夠忽略錯誤。但是這個時候主從的一致性已經出現問題。需要手工的把slave差的資料補上。
(2)、首先將引擎調整為一樣的,slave也改為事務引擎。
(二)、不支援 create table ....select statements
1、case重現:
master:直接執行一個create table select * from table;的sql
2、報錯:
error 1786
3、原理:
由於create table ...select語句會生成兩個sql,一個是DDL建立表SQL,一個是insert into 插入資料的sql。由於DDL會導致自動提交,所以這個sql至少需要兩個GTID,但是GTID模式下,只能給這個sql生成一個GTID,如果強制執行會導致和上面更新非事務引擎一樣的結果。
(三)、一個sql同事操作innodb引擎和myisam引擎:
case重現:t1表是innodb,t2表是myisam
1、update t1,t2 set t1.id=1000,t2.id=1000 where t1.id=t2.id;
2、報錯:1785
3、原理和第二個相同。
(四)、在一個replication grouop 中,所有的mysql必須要統一開啟或者關閉GTID功能。
1、case重現:
將一個未開啟gtid的slave透過原始的binlog和pos方式連線到開啟GTID的master。
2、報錯:
The slave IO thread stops because the master has @@GLOBAL.GTID_MODE ON and this server has @@GLOBAL.GTID_MODE OFF。
(五)、在一個replication group中,如果開啟GTID以後,就不再允許使用classic的複製方式:
1、case重現:
將一個開啟gtid的slave透過原始的binlog和pos方式連線到開啟GTID的master。
2、報錯:
ERROR 1776(HY000):Parameters MASTER_LOG_FILE,MASTER_LOG_POS,RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active。
(六)、GTID_MODE是not online的:
需要重啟才能生效,官方暫時不支援平滑的從classic replication切換到GTID replication。
貼士:
由於GTID開啟需要重啟系統,一個複製組中所有的例項必須統一開啟或者關閉GTID,開啟GTID以後不能在使用classic複製。
問題:
也就是說線上業務必須統一關閉,然後再啟動,會導致服務中斷。
解決方案:
1、針對這種情況,社群有兩種對應的平滑升級的方案:
一種是booking.com出品,這兩個差別在淘寶9月份資料庫月報裡有說明,加了一個橋接的伺服器,既可以執行GTID模式下,也可以執行classic模式下。
另外一種是facebook.com出品。所有的slave可以在開啟GTID模式的情況下,可以連線到沒有開啟GTID模式的master。
2、可以關閉一個部分,停止寫操作,但是讀不用,將另一部分改成GTID模式。
(七)、Temporary tables。
1、create temporary table和drop temporary table語句一樣在GTID環境下不支援。
如果--enforce_gtid_consistency引數開啟,並且autocommit=1,那麼可以使用。
(八)、關於Errant transaction
1、Errant transaction:所謂的errant transaction也就是沒有規範的從master執行,而是直接從slave執行的事務。
2、由於GTID協議的原因,最開始已經提過(參見GTID architecture)。
3、如果slave有errant transaction產生,由於GTID協議中的規則,很容易導致failover失敗。主要有兩種情況:
a、在slave上做了無用的或者臨時的errant transaction操作,如果該slave升級成為master的話,連線到它的所有資料庫都會獲取到這個事務。如果一樣就會產生衝突。
b、由於做了這個errant transaction這個事務以後,其他的slave還沒有獲取這個errant transaction的GTID,需要從master上發同步給其他的slave,但是主的binlog又被刪掉了,這時將會報錯。
4、總之:儘量避免產生errant transaction。可以透過:set sql_log_bin=off的方式在slave執行sql,但是也要考慮到資料一致性。

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

相關文章