在 「MySQL 5.7多源複製實踐」 一文中咱們講解了 MySQL 5.7 新特性多源複製的實現方法。今天咱們來說講 MySQL 5.7 的另外一個新特性基於 GTID 的主從複製實現。html
從 MySQL 5.6.5 開始新增了一種基於 GTID 的複製方式。經過 GTID 保證了每一個在主庫上提交的事務在集羣中有一個惟一的ID。這種方式強化了數據庫的主備一致性,故障恢復以及容錯能力。node
在原來基於二進制日誌的複製中,從庫須要告知主庫要從哪一個偏移量進行增量同步,若是指定錯誤會形成數據的遺漏,從而形成數據的不一致。藉助GTID,在發生主備切換的狀況下,MySQL的其它從庫能夠自動在新主庫上找到正確的複製位置,這大大簡化了複雜複製拓撲下集羣的維護,也減小了人爲設置複製位置發生誤操做的風險。另外,基於GTID的複製能夠忽略已經執行過的事務,減小了數據發生不一致的風險。mysql
什麼是GTIDlinux
GTID (Global Transaction ID) 是對於一個已提交事務的編號,而且是一個全局惟一的編號。 GTID 實際上 是由 UUID+TID 組成的。其中 UUID 是一個 MySQL 實例的惟一標識。TID 表明了該實例上已經提交的事務數量,而且隨着事務提交單調遞增。下面是一個GTID的具體形式:git
1 |
3E11FA47-71CA-11E1-9E33-C80AA9429562:23 |
一組連續的事務能夠用 -
鏈接的事務序號範圍表示。例如:github
1 |
e6954592-8dba-11e6-af0e-fa163e1cf111:1-5 |
GTID 集合能夠包含來自多個 MySQL 實例的事務,它們之間用逗號分隔。若是來自同一 MySQL 實例的事務序號有多個範圍區間,各組範圍之間用冒號分隔。例如:算法
1 2 |
e6954592-8dba-11e6-af0e-fa163e1cf111:1-5:11-18, e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27 |
可使用 SHOW MASTER STATUS
實時看當前的事務執行數。sql
GTID的做用數據庫
GTID 的使用不僅僅是用單獨的標識符替換舊的二進制日誌文件和位置,它也採用了新的複製協議。舊的協議每每簡單直接,即:首先從服務器上在一個特定的偏移量位置鏈接到主服務器上一個給定的二進制日誌文件,而後主服務器再從給定的鏈接點開始發送全部的事件。vim
新協議稍有不一樣:支持以全局統一事務 ID (GTID) 爲基礎的複製。當在主庫上提交事務或者被從庫應用時,能夠定位和追蹤每個事務。GTID 複製是所有以事務爲基礎,使得檢查主從一致性變得很是簡單。若是全部主庫上提交的事務也一樣提交到從庫上,一致性就獲得了保證。
GTID 相關操做:默認狀況下將一個事務記錄進二進制文件時,首先記錄它的 GTID,並且 GTID 和事務相關信息一併要發送給從服務器,由從服務器在本地應用認證,可是絕對不會改變原來的事務 ID 號。所以在 GTID 的架構上就算有了N層架構,複製是N級架構,事務 ID 依然不會改變,有效的保證了數據的完整和安全性。
你可使用基於語句的或基於行的複製與 GTID ,可是,爲了得到最佳效果,咱們建議你使用基於行(ROW)的格式。
GTID功能的具體概括主要有如下兩點:
咱們能夠看下在 MySQL 5.6 的 GTID 出現之前 Replication Failover 的操做過程。假設咱們有一個以下圖的環境:
此時,Server A 的服務器宕機,須要將業務切換到 Server B 上。同時,咱們又須要將 Server C 的複製源改爲 Server B。複製源修改的命令語法很簡單即:
1 |
CHANGE MASTER TO MASTER_HOST='xxx', MASTER_LOG_FILE='xxx', MASTER_LOG_POS=nnnn |
而這種方式的難點在於,因爲同一個事務在每臺機器上所在的 binlog 名字和位置都不同,那麼怎麼找到 Server C 當前同步中止點對應 Server B 上 master_log_file
和 master_log_pos
的位置就成爲了難題。
這也就是爲何 M-S
複製集羣須要使用 MMM
、MHA
這樣的額外管理工具的一個重要緣由。 這個問題在 5.6 的 GTID 出現後,就顯得很是的簡單。
因爲同一事務的 GTID 在全部節點上的值一致,那麼根據 Server C 當前中止點的 GTID 就能惟必定位到 Server B 上的GTID。甚至因爲 MASTER_AUTO_POSITION
功能的出現,咱們都不須要知道 GTID 的具體值。直接使用如下命令就能夠直接完成 Failover 的工做。
1 |
CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION=='xxx' |
如何產生GTID
GTID 的生成受 gtid_next
控制。 在主服務器上gtid_next
是默認的 AUTOMATIC
,即在每次事務提交時自動生成新的 GTID 。它從當前已執行的 GTID 集合(即 gtid_executed
)中,找一個大於 0 的未使用的最小值做爲下個事務 GTID 。同時在 Binlog 的實際的更新事務事件前面插入一條 set gtid_next
事件。
這裏以一條 insert 語句來看看 GTID 的生成過程:
在從庫上回放主庫的 Binlog 時,先執行 SET @@SESSION.GTID_NEXT
語句,而後再執行 insert 語句,確保在主和備上這條 insert 對應於相同的 GTID。
通常狀況下,GTID集合是連續的,但使用多線程複製(MTS)以及經過 gtid_next
進行人工干預時會致使 GTID 空洞。
繼續執行事務,MySQL 會分配一個最小的未使用 GTID,也就是從出現空洞的地方分配 GTID,最終會把空洞填上。
這意味着嚴格來講咱們即不能假設 GTID 集合是連續的,也不能假定 GTID 序號大的事務在GTID序號小的事務以後執行,事務的順序應由事務記錄在 Binlog 中的前後順序決定。
什麼是server-uuid
MySQL 5.6 之後用 128 位的 server-uuid
代替了本來的 32 位 server-id
的大部分功能。緣由很簡單,server-id
依賴於 my.cnf 的手工配置,有可能產生衝突。而自動產生 128 位 UUID 的算法能夠保證全部的 MySQL UUID 都不會衝突。
MySQL 在數據目錄下有一個 auto.cnf 文件就是用來保存 server-uuid
的,以下:
1 2 3 |
$ cat /var/lib/mysql/auto.cnf [auto] server-uuid=f75ae43f-3f5e-11e7-9b98-001c4297532a |
在 MySQL 再次啓動時會讀取 auto.cnf 文件,繼續使用上次生成的 server_uuid
。使用 SHOW
命令能夠查看 MySQL 實例當前使用的 server-uuid
,它是一個 MySQL Global Variables。
1 |
SHOW GLOBAL VARIABLES LIKE ‘server_uuid'; |
這裏一共使用了二臺機器,MySQL 版本都爲 5.7.18。
機器名 | IP地址 | MySQL角色 |
---|---|---|
dev-master-01 | 192.168.2.210 | MySQL 主庫 |
dev-node-02 | 192.168.2.212 | MySQL 從庫 |
MySQL 安裝比較簡單,在 「MySQL 5.7多源複製實踐」一文中咱們也講了,這裏就不在重複講了。若是你還不會安裝,能夠先參考此文安裝好 MySQL 。
GTID主從複製的配置思路
配置 MySQL 基於GTID的複製,主要是須要在 MySQL 服務器的主配置文件 [mysqld]
段中添加如下內容:
1 2 3 |
gtid-mode = ON enforce-gtid-consistency = ON log-slave-updates = ON |
在 MySQL 5.6 版本時,基於 GTID 的複製中 log-slave-updates
選項是必須的。可是其增大了從服務器的IO負載, 而在 MySQL 5.7 中該選項已經不是必須項。
MySQL主服務器配置片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ vim /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] server-id = 1 binlog_format = row expire_logs_days = 30 max_binlog_size = 100M gtid_mode = ON enforce_gtid_consistency = ON binlog-checksum = CRC32 master-verify-checksum = 1 log-bin = /var/log/mysql/mysql-bin log_bin_index = /var/log/mysql/mysql-bin.index log-slave-updates = ON |
MySQL從服務器配置片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$ vim /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] server-id = 3 gtid_mode = ON enforce_gtid_consistency = ON log-slave-updates = ON skip-slave-start = true expire_logs_days = 30 max_binlog_size = 100M read_only = ON slave-sql-verify-checksum = 1 log-bin = /var/log/mysql/mysql-bin log_bin_index = /var/log/mysql/mysql-bin.index relay-log = /var/log/mysql/relay-log relay-log-index = /var/log/mysql/relay-log-index relay-log-info-file = /var/log/mysql/relay-log.info master-info-repository = table relay-log-info-repository = table relay-log-recovery = ON report-port = 3306 report-host = 192.168.2.212 replicate-do-db = master1 replicate_wild_do_table=master1.% |
注:server-id
每臺必須配置爲不同,好比 dev-master-01 爲1,dev-node-02 爲3。這裏沒有給出所有配置,其它請根據實際狀況自行配置。
1 |
$ service mysql restart |
基於 GTID 的複製會自動地將沒有在從庫執行過的事務重放,因此不要在其它從庫上創建相同的帳號。 若是創建了相同的帳戶,有可能形成複製鏈路的錯誤。
1 2 3 |
# 在MySQL主服務器上建立 mysql> grant replication slave on *.* to 'repl'@'192.168.2.%' identified by '000000'; mysql> flush privileges; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
mysql> show variables like "%gtid%"; +----------------------------------+-----------+ | Variable_name | Value | +----------------------------------+-----------+ | binlog_gtid_simple_recovery | ON | | enforce_gtid_consistency | ON | | gtid_executed_compression_period | 1000 | | gtid_mode | ON | | gtid_next | AUTOMATIC | | gtid_owned | | | gtid_purged | | | session_track_gtids | OFF | +----------------------------------+-----------+ 8 rows in set (0.00 sec) mysql> show variables like '%gtid_next%'; +---------------+-----------+ | Variable_name | Value | +---------------+-----------+ | gtid_next | AUTOMATIC | +---------------+-----------+ 1 row in set (0.00 sec) |
簡單說下幾個經常使用參數的做用:
a) gtid_executed
在當前實例上執行過的 GTID 集合,實際上包含了全部記錄到 binlog 中的事務。設置 set sql_log_bin=0
後執行的事務不會生成 binlog 事件,也不會被記錄到 gtid_executed
中。執行 RESET MASTER
能夠將該變量置空。
b) gtid_purged
binlog 不可能永遠駐留在服務上,須要按期進行清理(經過 expire_logs_days
能夠控制按期清理間隔),不然早晚它會把磁盤用盡。
gtid_purged
用於記錄本機上已經執行過,可是已經被清除了的 binlog 事務集合。它是 gtid_executed
的子集。只有 gtid_executed
爲空時才能手動設置該變量,此時會同時更新 gtid_executed
爲和 gtid_purged
相同的值。
gtid_executed
爲空意味着要麼以前沒有啓動過基於 GTID 的複製,要麼執行過 RESET MASTER
。執行 RESET MASTER
時一樣也會把 gtid_purged
置空,即始終保持 gtid_purged
是 gtid_executed
的子集。
c) gtid_next
會話級變量,指示如何產生下一個GTID。可能的取值以下:
第一個:AUTOMATIC
自動生成下一個 GTID,實現上是分配一個當前實例上還沒有執行過的序號最小的 GTID。
第二個:ANONYMOUS
設置後執行事務不會產生GTID。
第三個:顯式指定的GTID
能夠指定任意形式合法的 GTID 值,但不能是當前 gtid_executed
中的已經包含的 GTID,不然下次執行事務時會報錯。
1 2 3 4 5 6 7 8 |
mysql> show global variables like '%uuid%'; +---------------+--------------------------------------+ | Variable_name | Value | +---------------+--------------------------------------+ | server_uuid | f75ae43f-3f5e-11e7-9b98-001c4297532a | +---------------+--------------------------------------+ 1 row in set (0.00 sec) |
1 |
mysql> CHANGE MASTER TO MASTER_HOST='192.168.2.210',MASTER_USER='repl',MASTER_PASSWORD='000000',MASTER_AUTO_POSITION=1; |
1 |
mysql> START SLAVE; |
啓動成功後查看SLAVE的狀態
1 2 3 4 5 6 |
mysql> SHOW SLAVE STATUS\G ... Slave_IO_Running: Yes Slave_SQL_Running: Yes ... |
確認 Slave_IO_Running
和 Slave_SQL_Running
兩個參數都爲 Yes 狀態。
在主服務器查看從庫鏈接的主機信息
1 2 3 4 |
mysql> create database master1; mysql> use master1; mysql> CREATE TABLE `test1` (`id` int(11) DEFAULT NULL,`count` int(11) DEFAULT NULL); mysql> insert into test1 values(1,1); |
1 2 3 4 5 6 7 |
mysql> select * from master1.test1; +------+-------+ | id | count | +------+-------+ | 1 | 1 | +------+-------+ 1 row in set (0.00 sec) |
檢查從服務器狀態
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.2.210 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 977 Relay_Log_File: relay-log.000002 Relay_Log_Pos: 1190 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: master1 Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: master1.% Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 977 Relay_Log_Space: 1391 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: f75ae43f-3f5e-11e7-9b98-001c4297532a Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: f75ae43f-3f5e-11e7-9b98-001c4297532a:1-4 Executed_Gtid_Set: 2c55f623-4fea-11e7-82c7-001c4283459b:1, f75ae43f-3f5e-11e7-9b98-001c4297532a:1-4 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec) |
能夠看到 IO 和 SQL 線程都爲 YES ,另外 retrieved_Gtid_Set
接收了4個事務,Executed_Gtid_Set
執行了4個事務。
在基於 GTID 的複製拓撲中,要想修復從庫的 SQL 線程錯誤,過去的 SQL_SLAVE_SKIP_COUNTER
方式再也不適用。須要經過設置 gtid_next
或 gtid_purged
來完成,固然前提是已經確保主從數據一致,僅僅須要跳過複製錯誤讓複製繼續下去。
在從庫上執行如下SQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
mysql> stop slave; Query OK, 0 rows affected (0.00 sec) mysql> set gtid_next='f75ae43f-3f5e-11e7-9b98-001c4297532a:20'; Query OK, 0 rows affected (0.00 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> set gtid_next='AUTOMATIC'; Query OK, 0 rows affected (0.00 sec) mysql> start slave; Query OK, 0 rows affected (0.02 sec) |
其中 gtid_next
就是跳過某個執行事務,設置 gtid_next
的方法一次只能跳過一個事務,要批量的跳過事務能夠經過設置 gtid_purged
完成。假設下面的場景:
此時從庫的 Executed_Gtid_Set
已經包含了主庫上 1-13 和 20 的事務,再開啓複製會從後面的事務開始執行,就不會出錯了。在從庫上驗證剛纔插入的數據:
注意,使用 gtid_next
和 gtid_purged
修復複製錯誤的前提是跳過那些事務後仍能夠確保主備數據一致。若是作不到,就要考慮 pt-table-sync 或者從新導入備份的方式了。
在作備份恢復的時候,有時須要恢復出來的 MySQL 實例能夠做爲從庫連上原來的主庫繼續複製,這就要求從備份恢復出來的 MySQL 實例擁有和主數據庫數據一致的 gtid_executed
值。這也是經過設置 gtid_purged
實現的,下面看下 mysqldump 作備份的例子。
這裏使用 --all-databases
選項是由於基於 GTID 的複製會記錄所有的事務, 因此要構建一個完整的dump。
1 |
$ mysqldump --all-databases --single-transaction --triggers --routines --events --host=127.0.0.1 --port=3306 --user=root -p000000 > dump.sql |
生成的 dump.sql 文件裏包含了設置 gtid_purged
的語句
1 2 3 4 5 6 |
SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN= 0; ... SET @@GLOBAL.GTID_PURGED='f75ae43f-3f5e-11e7-9b98-001c4297532a:1-14:20'; ... SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN; |
在從庫恢復數據前須要先經過 reset master
清空 gtid_executed
變量
1 2 |
$ mysql -h127.0.0.1 --user=root -p000000 -e 'reset master' $ mysql -h127.0.0.1 --user=root -p000000<dump.sql |
不然執行設置 GTID_PURGED
的 SQL 時會報下面的錯誤:
1 |
ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty. |
此時恢復出的 MySQL 實例的 GTID_EXECUTED
和在主庫備份時的一致:
因爲恢復出 MySQL 實例已經被設置了正確的 GTID_EXECUTED
,下面以 master_auto_postion = 1
的方式 CHANGE MASTER
到原來的主節點便可開始複製。
1 |
mysql> CHANGE MASTER TO MASTER_HOST='192.168.2.210', MASTER_USER='repl', MASTER_PASSWORD='000000', MASTER_AUTO_POSITION = 1; |
若是不但願備份文件中生成設置 GTID_PURGED
的 SQL,能夠給 mysqldump
傳入 --set-gtid-purged=OFF
關閉。
enforce_gtid_consistency
強制 GTID 一致性, 啓用後如下命令沒法再使用。
1 2 |
mysql> create table test2 select * from test1; ERROR 1786 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT. |
由於其實是兩個獨立事件,因此只能將其拆分。先創建表,而後再把數據插入到表中。
1 2 3 4 5 |
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> create temporary table test2(id int); ERROR 1787 (HY000): Statement violates GTID consistency: CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can only be executed outside transactional context. These statements are also not allowed in a function or trigger because functions and triggers are also considered to be multi-statement transactions. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
mysql> CREATE TABLE `test_innodb` (id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT); Query OK, 0 rows affected (0.04 sec) mysql> CREATE TABLE `test_myisam` (id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE = `MyISAM`; Query OK, 0 rows affected (0.03 sec) mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into test_innodb(id) value(1); Query OK, 1 row affected (0.00 sec) mysql> insert into test_myisam(id) value(1); ERROR 1785 (HY000): Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables. |
http://www.google.com
http://www.ywnds.com/?p=3898
http://dbaplus.cn/news-11-857-1.html
http://www.jianshu.com/p/3675fa74bc72
http://www.cnblogs.com/abobo/p/4242417.html
http://cenalulu.github.io/mysql/mysql-5-6-gtid-basic/