MYSQL 5.6 GTID模式下手工刪除日誌導致備庫數據丟失

我們在測試 5.6 GTID 的時候,發現了一個導致主備數據丟失的場景。特別提醒一下使用 MySQL 5.6 並啓用了 GTID 的各位,以避免這種情況發生。同時也簡單介紹了 GTID 的實現原理。

場景描述

有一臺 MySQL 實例 A ,啓用了 GTID 。由於 MySQL 服務器空間吃緊,我們手工將主庫的所有的 binlog以及 binlog index 文件都刪除掉,並啓動了這個 MySQL 實例。此後,本來連在該 MySQL 實例上的備庫雖然 show slave status 狀態都是正常的,但是卻無法獲得主庫上的任何更新了。備庫上的 show slave status如下:

[5.6.14-log instance1 root@test1 /root [email protected]:test 17:42:32 ]

>show slave status\G

*************************** 1. row ***************************

              Slave_IO_State: Waiting for master to send event

                 Master_Host: 192.168.1.152

                 Master_User: repl

                 Master_Port: 9309

               Connect_Retry: 60

             Master_Log_File: mysql-bin.000001

         Read_Master_Log_Pos: 10328743

              Relay_Log_File: mysql-relay-bin.000002

               Relay_Log_Pos: 7805047

       Relay_Master_Log_File: mysql-bin.000001

Slave_IO_Running: Yes

           Slave_SQL_Running: Yes

             Replicate_Do_DB:

         Replicate_Ignore_DB:

          Replicate_Do_Table:

      Replicate_Ignore_Table:

     Replicate_Wild_Do_Table:

 Replicate_Wild_Ignore_Table:

                  Last_Errno: 0

                  Last_Error:

                Skip_Counter: 0

         Exec_Master_Log_Pos: 10328743

             Relay_Log_Space: 7805251

             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: 152

                 Master_UUID: 9300dd57-51da-11e3-989d-3cd92bee36a8

            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 the slave I/O thread to update it

          Master_Retry_Count: 86400

                 Master_Bind:

     Last_IO_Error_Timestamp:

    Last_SQL_Error_Timestamp:

              Master_SSL_Crl:

          Master_SSL_Crlpath:

          Retrieved_Gtid_Set: 9300dd57-51da-11e3-989d-3cd92bee36a8:1-1986

           Executed_Gtid_Set: 9300dd57-51da-11e3-989d-3cd92bee36a8: 1 - 28123

               Auto_Position: 0

1 row in set (0.00 sec)

GTID 是什麼?

首先,簡單描述一下 GTID

GTID MySQL 5.6 新引入了的概念,它是由 UUID 和事務 ID 組成。這樣全球所有的服務器的事務都可以用 GTID 來表示。下面就是一個 GTID

9300dd57-51da-11e3-989d-3cd92bee36a8:1

9300dd57-51da-11e3-989d-3cd92bee36a8 表示的是一臺服務器 A UUID ,這個是全球唯一的。然後 1 表示這個服務器 A 的第一個事務。

引入 GTID 以後, MySQL 就更靈活了:

l  Slave 在搭建複製的時候,簡單的設置 auto_position=1 ,不用去找 MASTER_LOG_FILE MASTER_LOG_POS 這種坑爹的“二進制文件的字節偏移位置”了。

l  在環形複製或者一主多從的架構下,節點一旦損壞,其他節點之間複製的架構重新搭建起來能夠非常簡單的完成。

GTID 實現原理

我們這裏也簡單描述一下 GTID 的實現原理。 MySQL 在主庫上會把它自己執行過的所有事務的 GTID 都記錄下來: gtid_executed gtid_executed 是一個 GTID 的集合,不管是客戶自己提交的還是 SQL 線程執行過的數據變更事務,都會被記錄在這裏。你可以通過 show global variables 查看:

[5.6.14-log instance1 root@test1 /root [email protected]:test 1 8 : 22 :32 ]

>show global variables like 'gtid_executed';

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

| Variable_name | Value                                          |

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

| gtid_executed | 9300dd57-51da-11e3-989d-3cd92bee36a8:1-8454,

a01193d9-51da-11e3-989d-94de80b47c01:328691-335254 |

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

1 row in set (0.00 sec)

然後,當備庫需要搭建複製的時候,有兩種方式:

l  auto_position=0 ,需要指定 MASTER_LOG_FILE MASTER_LOG_POS

l  auto_position=1

auto_position=0 時,很簡單根據指定的 binlog 文件和偏移量, Master binlog 中的各個數據變更事務發送給備庫就好了。備庫接收到對應的事務,判斷是否本機發起的 ( 通過數據變更事務記錄的 server_id來判斷 ) ,如果不是本機發起的,就直接執行。

auto_position=1 時,備庫將自己的 gtid_executed 的事務集合傳給 Master Master 找到第一個包含有非備庫 gtid_executed 事務集中數據變更的 binlog ,將 binlog 中的各個 event 發送給備庫。這樣備庫首先判斷是否本機發起的,然後判斷髮送過來的各個數據變更事務是否在本機執行過。沒有執行過的事務都需要在本地執行一遍。

問題分析

我們還是看備庫上的 slave 狀態:

[5.6.14-log instance1 root@test1 /root [email protected]:test 17:42:32 ]

>show slave status\G

*************************** 1. row ***************************

              Slave_IO_State: Waiting for master to send event

                 Master_Host: 192.168.1.152

                 Master_User: repl

                 Master_Port: 9309

               Connect_Retry: 60

             Master_Log_File: mysql-bin.000001

         Read_Master_Log_Pos: 10328743

              Relay_Log_File: mysql-relay-bin.000002

               Relay_Log_Pos: 7805047

       Relay_Master_Log_File: mysql-bin.000001

    Slave_IO_Running: Yes

           Slave_SQL_Running: Yes

             Replicate_Do_DB:

         Replicate_Ignore_DB:

          Replicate_Do_Table:

      Replicate_Ignore_Table:

     Replicate_Wild_Do_Table:

 Replicate_Wild_Ignore_Table:

                  Last_Errno: 0

                  Last_Error:

                Skip_Counter: 0

         Exec_Master_Log_Pos: 10328743

             Relay_Log_Space: 7805251

             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: 152

                 Master_UUID: 9300dd57-51da-11e3-989d-3cd92bee36a8

            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 the slave I/O thread to update it

          Master_Retry_Count: 86400

                 Master_Bind:

     Last_IO_Error_Timestamp:

    Last_SQL_Error_Timestamp:

              Master_SSL_Crl:

          Master_SSL_Crlpath:

   Retrieved_Gtid_Set: 9300dd57-51da-11e3-989d-3cd92bee36a8:1-1986

           Executed_Gtid_Set: 9300dd57-51da-11e3-989d-3cd92bee36a8: 1 - 28123

               Auto_Position: 0

1 row in set (0.00 sec)

我們注意到 Retrieved_Gtid_Set Executed_Gtid_Set 還要少。 Retrieved_Gtid_Set 記錄的是 IO thread 從主庫拿到的事務集合, Executed_Gtid_Set 記錄的是本機已經執行的事務集合。這裏有一個很奇怪的想象,本機已經執行的事務集合比 IO thread 從主庫拿到的事務集合還要大,而且它只是備庫。

回想一下,我們之前做的操作:手工刪除了 binlog 日誌和 binlog index 文件,然後啓動了 MySQL 。這個操作在沒有啓動 GTID MySQL 上,我們也做過類似的事情,備庫並沒有複製丟失的問題。

這裏之所以數據丟失的原因就很清晰了:

我們手工刪除了 binlog 以後,數據庫會自動生成 binlog 。但是在 Master 中,它重新從 9300dd57-51da-11e3-989d-3cd92bee36a8:1 第一個事務開始計數。從另外一個側面來說,已經執行完的 GTID 集合,MySQL 並沒有單獨保存,而是通過 Binlog 來獲得的。

備庫已經執行完了 9300dd57-51da-11e3-989d-3cd92bee36a8 :1 - 28123 這些事務。所以當 SQL thread 拿到從主庫複製過來的變更事務時,發現這些事務都是它已經執行過的事務,那麼它就不會再重複執行,從而導致對應的這些事務都會被丟失掉。

從上面來說,我們儘量不要在數據庫之外去做一些事情。刪除 binlog ,我們其實可以用更好的辦法:“purge master logs ”。如果在某種情況下,我們確實需要在手工刪除,並啓動數據庫,建議同時將auto.cnf 清理掉。這樣主庫會生成新的 UUID ,備庫發現是新的事務就會執行這個變更了。

出自:http://www.woqutech.com/?p=1108

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章