從 SQL Server 到 MariaDB - [5] Log-shipping 與 MariaDB Replication

2016-09-19

需求說明

在資料庫的備援部份,採用 SQL Server Log-shipping 方式來處理,希望在移轉至 MariaDB 時也有不錯的類似 Log-shipping 方式執行。

MariaDB Replication uses GTID

接下來的文章來自於許多的文件,從官方文件MariaDB · 版本特性 · MariaDB 的 GTID 介绍,還有mariadb10 GTID 研究笔记. md實作,整理了一下。

Master 與 Slave

與 SQL Server Log-shipping 不一樣的地方如下

  1. 不管什麼樣的 Replication (樹狀或是環型串聯),發動端皆是 Slave,也就是說 Master 不存放任何有關 Replication 的資料,所有資料皆在 Slave 裡。
  2. 在 Primary Database (Master) 會是 Backup Trasnsaction 的動作,而在 Secondary Database (Slave) 是 Copy 與 Restore 的動作,在 MariaDB Replication 只會在 Slave 連到 Master 時看要複製哪一段的 binlog 加以重現。
  3. 因為 GTID 的設計,一個 Slave 的 Master 可以是多個,當然一個 Master 可以有多個 Slave。
  4. Slave 重現完資料以後就會繼續跟 Master 要進一步的 binlog 資料,所以基本上是沒有落差可言的,在 SQL Server Log-shipping 是有發動時間設定的(預設為 15 分鐘)。
  5. 系統的資料庫 (mysql 那些)也可以做 Replication。

在 4 的部份,其實對於現在的維運有些麻煩。因為這 15 分鐘的落差,可以拿來利用成正式環境的 buffer,下錯指令時如果還在 15 分鐘的時間分隔下,還可以趕快去備援區拿資料蓋回去,現在沒有時間設定的話就比較麻煩。有人發個 issue 希望能 merge MySQL 的 MASTER_DELAY 功能,不過表定是 10.2.x 的功能,希望能實作在 10.1.x。

在 5 的部份我覺得還蠻方便的就是帳號密碼的同步,在 SQL Server Log-shipping 是要分開想辦法同步的,管理就很方便了。

Slave 如何透過 GTID 做 Replication

GTID 的規格部份就不解釋了,上面的文件寫的很清楚。

在 MariaDB 有一些 Server Variables 紀錄 GTID 的資訊

Server Variables Names 意義
gtid_binlog_pos binlog 裡最後一筆紀錄的 GTID
gtid_slave_pos Slave 上紀錄的最後一個 binlog GTID
gtid_current_pos Database 上紀錄的最後一個 binlog GTID

什麼叫作 “紀錄的最後一個 binlog GTID” 呢?

gtid_slave_pos 來說,就是 master 的最後一筆 GTID。

gtid_current_pos 來說,不管當下的 Instance 之前是 Master 還是 Slave

1
2
3
4
5
if gtid_binlog_pos.ServerID = Instance.ServerID and gtid_binlog_pos.DomainID = Instance.DomainID and gtid_binlog_pos.SequenceID > gtid_slave_pos.SequenceID (同 ServerID 與 DomainID 狀態下) then
gtid_current_pos = gtid_binlog_pos
else
gtid_current_pos = gtid_slave_pos (同 ServerID 與 DomainID 狀態下)
end if

官方文件有說到一個例子,假設 A 為 Master,B 為 Slave,參數為 slave_pos,把 A 先關一會兒,讓 B 成為 Master,最後回復 A 並成為 B 的 Slave。

這個的假設前提是在那個中斷時間內沒 B 沒有任何的 binlog 紀錄交易,這樣能確保 1-1-1451-1-147 都只有在有 Slave 的狀態下 Master 產生的交易,假設 B 中間原來忽略的的 2-2-138 被產生了,那麼 binlog 的順序就會在 1-1-1451-1-147 中間夾一個 2-2-138,奇怪的交易紀錄,會導致 A 的加入失敗。

所以官方建議在 Slave 上不要有任何的 binlog 紀錄 (Session 中 @@sql_log_bin=0),避免當這個 Slave 有機會變成 Master 或是再 Slave 到其他地方時有交易紀錄異常的問題。

如果還是要修改 Slave 的資料造成 binlog 會有變動,還是要把參數設為 slave_pos,確保 Slave 會從 Master 看的 binlog 資料不會因為 Slave 的變動而異常,不能像 current_pos 那樣兩邊都有可能取值,不管是什麼角色。

在 B 當 Slave 的時候如果沒有開 binlog,那麼 current_posslave_pos 是同一件事,當然後續的切換是不會發生的 (B 沒 binlog 不能當 Master)。

最後的設計

剛剛最後有說到在 Slave 沒有開 binlog 的方式,我原來是想要用這個的,因為這蠻像現在的維運模式。

  1. 當 Master 壞了就停掉 Slave 的 Service
  2. 等到 Master 好的時候停機備份到 Master 上
  3. 在 Slave 重新建 Replication

如果 Slave 有開 binlog,第2步的停機備份似乎可以把時間縮短

  1. 當 Master 壞了就停掉 Slave 的 Service
  2. 等到 Master 好的時候倒回 Slave 的備份
  3. 同步完成後重新建立 Master / Slave 順序

測試

在同一個機器上開新的 Instance

參數 意義
master-data dump 出來的掛 master ID
events 包含 events
routines 包含 functions 與 store procedures
gtid 產生 CHANGE MASTER TO master_use_gtid 語法

觀察一下 all_db.sql 的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- MySQL dump 10.16  Distrib 10.1.17-MariaDB, for Win64 (AMD64)
(中間省略)
--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='WSTest-bin.000002', MASTER_LOG_POS=3732;

--
-- GTID to start replication from
--

CHANGE MASTER TO MASTER_USE_GTID=slave_pos;
SET GLOBAL gtid_slave_pos='0-1-770';

--
-- Current Database: 'master'
--
(後面省略)

可以看到如果要用舊的方式(兼容 MySQL Replication) 與新的方式。

觀察一下 all_db.sql 的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- MySQL dump 10.16  Distrib 10.1.17-MariaDB, for Win64 (AMD64)
(中間省略)
--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='WSTest-bin.000003', MASTER_LOG_POS=12441;

--
-- GTID to start replication from
--

CHANGE MASTER TO MASTER_USE_GTID=slave_pos;
SET GLOBAL gtid_slave_pos='0-2-976';

--
-- Current Database: 'master'
--
(後面省略)

Blog comments powered by Disqus