1. 程式人生 > 其它 >淺談GTID及簡單測試

淺談GTID及簡單測試

今天簡單介紹一下GTID,並有部分相關實驗。

GTID相信大家都不陌生,GTID的英文全稱為Global Transaction Identifier,在MySQL主從架構中應用廣泛。

GTID是由“UUID:事務號“組成的,GTID是基於事務的,在主從架構中,在主庫每提交一個事務都會對應生成一個GTID號,GTID支援語句和行格式的複製,而且在主庫提交的事務只會在從庫應用一次,保證了一致性。

一、下面說一下GTID的優缺點

1、GTID優點:

①複製安全性高,搭建主從相比與傳統的(基於binlog和position號)複製要簡單;

②GTID在主庫是連續的,保證了資料的一致性;

③故障切換時間更短,降低服務故障時間等。

2、GTID缺點:

①主從表儲存引擎必須一致,不能一個innodb一個myisam,這樣會導致主備資料不一致,因為GTID是和事務之間一一對應的;

②不允許一個sql同時更新事務引擎表和非事務引擎表;

③在主從模式下,需要主庫從庫同時開啟和關閉gtid;

④不支援create table .... as select 語句,在主庫會直接報錯的;

⑤不支援create/drop temporary table語句;

⑥不支援傳統的複製模式,跳過錯誤的語句(sql_slave_skip_counter)。

二、GTID部分相關引數介紹

①gtid_mode:控制是否啟用gtid, on/on_permissiv/off_perissiv/off;

②enforce_gtid_consistency:用於保證GTID一致性的,有on/off/warn三個值

 on:不允許任何事務違反gtid一致性;

 off:允許所有事務違反gtid一致性;

 warn:允許所有事務違反gtid一致性,但是這種情況下會生成告警,mysql5.7.6新增。

③gtid_executed:它是一個gtid的範圍,表示的是已經執行過的所有的gtid事務集或者是由於語句人為設定的gtid。

④gtid_purged:表示的是已經執行過但是已經被purge掉的gtid,也是一個範圍,它是executed的一個子集。在以下幾種情況下,gtid_purged會有值:

 禁用二進位制日誌的情況下,提交事務的GTID;

 寫入二進位制日誌的GTID已經被刪除了;

 使用語句顯示的指定gtid purged:set @@global.gtid_purged=...。

⑤gtid_next:是會話級別的變數,對於提交的事務,會自動分配新的gtid,預設值為automatic,也可以顯示指定gtid_next,來指定下一個事務的GTID號。

⑥gtid_owned:該變數主要提供內部使用,儲存的是伺服器上當前正在使用的所有GTID列表,以及擁有它的執行緒的ID。

三、進行幾個GTID的測試

 測試環境,已經開啟GTID複製模式,搭建過程省略。

1

1236報錯的情況

情況1:

主庫真正的purge了正在使用的binlog,或者執行了reset master;,這種情況不用過多解釋,從庫肯定不能找到主庫的binlog而報錯了。

情況2:

從庫gtid不連續,出現了空洞。

1) 如何模擬空洞的出現:

①在從庫引數檔案中指定slave_skip_errors=1050;

②在從庫test庫中建立一張表;

③在主庫test庫中建立同樣的一張表,此時如果沒有skip引數的話,從庫肯定會報錯;

④此時在主庫向該表中插入一條資料,此時從庫gtid就出現了空洞;

⑤檢視show slave status\G;

Executed_Gtid_Set: 9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1737:1739-1740,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14

對於gtid空洞的情況,假如在需要進行維護的時候,需要重新進行執行change master to語句,可能會出現1236報錯,模擬如下:

2)前期準備:

①切換和刪除binlog:

由於測試庫沒有什麼業務,binlog沒有切換,為了模擬生產上binlog的情況,我們手動進行binlog切換和刪除。

mysql> flush logs;   //首先重新整理一個日誌,這樣從庫也就指向了新的binlog

Query OK, 0 rows affected (0.04 sec)

mysql> show binary logs;  //檢視存在的binlog

+------------------+-----------+

| Log_name         | File_size |

+------------------+-----------+

| mysql-bin.000022 |      2216 |

| mysql-bin.000023 |       194 |

+------------------+-----------+

2 rows in set (0.01 sec)

mysql> purge binary logs to 'mysql-bin.000023';  //將該binlog之前的binlog全部purge掉,這樣也就     模擬了主庫binlog超過了expire log days時間後對binlog進行的清理,這樣binlog裡面之前的gtid對應的操作都不存在了。

mysql> show binary logs;  //再次檢視只留下了最新的binlog

+------------------+-----------+

| Log_name         | File_size |

+------------------+-----------+

| mysql-bin.000023 |       194 |

+------------------+-----------+

1 row in set (0.00 sec)

3) 復現1236報錯:

此時主庫老的gtid對應的操作,在binlog中已經被purge了,而從庫由於skip引數已經出現了gtid空洞,此時簡單的stop slave;start slave;是不會有什麼問題的。

檢視gtid相關資訊:

mysql> show global variables like '%gtid%';

gtid_executed:9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1737:1739-1740,

b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14 //這個值是和show slave status對應的。

gtid_purged :9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1732,

b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-11  //由於從庫的binlog沒有purge,所以它的值沒有變化。

如果此時由於某種原因,我們需要重新執行change master to...操作,此時就會報錯1236:

mysql> stop slave;

mysql> change master to .....

mysql> start slave;

mysql> show slave status\G;

Last_IO_Errno: 1236

Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.

這是因為:當MASTER_AUTO_POSITION = 1時,在初始連線握手時也就是執行change的時候,從庫傳送一個GTID集,其中包含它已經接收或提交的事務,或同時包含這兩種事務(也就是executed的值)。主庫的響應方式是傳送所有記錄在二進位制日誌中的事務,這些事務的GTID不在副本傳送的GTID集合中。這種交換確保主庫只發送從庫還沒有記錄或提交的GTID的事務。

有了上面的理解就不難明白:從庫傳送的gtid集為:9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1737:1739-1740,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14,是不連續的,缺少9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1738,而主庫會發送到從庫的是不包含在從庫傳送出來的gtid的,所以從庫還會去主庫找9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1738這個gtid,而該gtid所在的binlog已經被purge掉了,所以會報1236的錯誤。

3) 修復:

我們需要在從庫執行purge,將gtid空洞補齊:

mysql> stop slave;

mysql> reset master;

mysql> set global gtid_purged='9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1740,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14';

mysql> reset slave all;

mysql> change master to ....;

mysql> start slave;

mysql> show slave status\G;

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

注意:在執行set global gtid_purged的時候,gtid_purged需要為空,我們需要執行reset master將從庫的gtid_purged清空,這樣gtid就會連續了,從庫傳送gtid集的時候就包含了空洞這部分gtid,所以主庫也就不會向從庫傳送該gtid,從庫也就不會去主庫找該gtid,也就會從最新的gtid去拉取,這樣就不會出現1236報錯了。

當然還有其他情況會出現1236的報錯,比如主庫中包含其他例項的gtid(可能是該主庫是從其他例項上摘下來的從庫),也就是說主庫的gtid集包含沒在該架構中的例項的gtid,而從庫不包含該例項的gtid,當從庫執行change的時候大概率也會有1236報錯。大概就是這一個意思,這裡就不做測試了,想要說明的就是從庫在執行change的時候它包含的gtid集要和主庫的一致,也就是說主庫gtid集中有的uuid,從庫中也得有,不管該gtid有沒有變化有沒有用到。

2

從庫gtid_purged值什麼時候會變化

情況1:

從庫手動執行set global gtid_purged=....;這個上面已經執行過了。

情況2:

禁用二進位制日誌的情況下或者關閉log_slave_updates引數,提交事務的GTID;這種情況說的是當從庫在禁用binlog的情況下,gtid purged該值是都在變化的,因為從庫沒有binlog了,也就沒有purge binlog這一說了。

情況3:

當從庫binlog被purge掉的時候。

此時的gtid executed和gtid purged分別為:

gtid_executed :9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1748,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14

gtid_purged:9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1740,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14

需要注意的是這兩個值平時是不一樣的,executed表示已經接收執行到的gtid,而purged表示已經被purge掉的gtid。

我們先flush一個binlog,然後再purge:

mysql> flush logs;

Query OK, 0 rows affected (0.09 sec)

mysql> show binary logs;

+------------------+-----------+

| Log_name         | File_size |

+------------------+-----------+

| mysql-bin.000001 |      2217 |

| mysql-bin.000002 |       194 |

+------------------+-----------+

2 rows in set (0.00 sec)

mysql> purge binary logs to 'mysql-bin.000002';

Query OK, 0 rows affected (0.11 sec)

mysql> show global variables like '%gtid%';

gtid_executed:9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1748,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14

gtid_purged :9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1-1748,b30eac1b-7398-11ec-befa-fa7f4a6c5000:1-14

可以看到gtid_purged的值發生了變化,包含的是我們已經purge掉的gtid,因為測試庫沒有業務,所以executed和purged在這裡顯示出來是一樣的。

3

跳過gtid

當從庫出現主鍵衝突,或者其他報錯的時候,我們根據實際情況來進行手動跳過gtid,步驟如下:

mysql> stop slave;

mysql> set gtid_next=...; //該gtid號可以根據報錯提示去找。

mysql> begin;commit;

mysql> set gtid_next='automatic';

mysql> start slave;

這樣也就解釋了gtid_next可以顯示的指定,預設情況下是automatic的。

需要注意的是,在生產上從庫出現gtid問題的時候,不要一昧的去跳過,需要找到問題的原因,是因為從庫沒有開啟read only還是什麼原因,要搞清楚,否則可能造成主從資料不一致。

4

gtid_owned什麼時候顯示

在平時使用過程中該值是不容易被發現,被捕捉的。這裡我們模擬一個大事務來看看:

在主庫執行,模擬大事務:

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update d set id=sleep(20) + 111 where id=1;  //這裡d是一個測試表,可以自行建立。

Query OK, 1 row affected, 1 warning (20.09 sec)

Rows matched: 1  Changed: 1  Warnings: 1

mysql> commit;

Query OK, 0 rows affected (0.02 sec)

在從庫檢視:

mysql> show global variables like '%gtid%';

gtid_owned :9c94ddb7-7397-11ec-a69f-fa37ddbb3200:1752#5

它是由gtid和執行緒組成,我們可以使用show processlist,檢視執行緒5:

mysql> show processlist;

| 5 | system user |   | test | Connect |   34 | User sleep | update d set id=sleep(20) + 111 where id=1 |

正式主庫同步過來的操作。

5

gtid_executed表簡介

僅當gtid_mode設定為ON或ON_PERMISSIVE時,GTID才儲存在gtid_executed表中。如果從庫禁用了binlog或者log_slave_updates=0的時候,該表中儲存所有的gtid。

當啟用二進位制日誌的時候,該表並不儲存所有的gtid,而是通過show global variables查出來的gtid_executed儲存,重新整理二進位制日誌或者重啟時,將會把當前binlog中所有的gtid寫入到該表中。因此該表有時候可能並不是完整的gtid,所以通過show查看出來的才是完整的。

END   

                                                                                    

好了,就先說這麼多,說的比較淺顯,gtid還是很奇妙很有意思的,裡面其實還有好多東西需要學習,要帶著興趣帶著思考去學習去測試,這樣會事半功倍。