要知道,Mysql 的主從使用的是 binlog 那樣簡單的 日志傳輸方式,來完成從庫對主庫的復制,雖然提高了效率,但是主庫和從庫之間並沒有 raft 那樣的協議來保證 主從一致。
有時候主庫宕機,但是 binlog 還沒有發出去,如果直接將從庫切換為主庫,那么將會主備不一致。
並且從庫是單純告訴主庫,需要從主庫的 binlog 的哪個偏移量開始 ,讓主庫發送 binlog 過來,所以很有可能因為偏移量不正確,導致從庫得到的 binlog 是以前獲得過的,導致重復的插入或者刪除,使得從庫因為錯誤終止運行。
Mysql 5.6 引入了 GTID ,啟動這個模式:啟動參數 + gtid_mode=on和enforce_gtid_consistency=on
Global Transaction ID , 可以唯一表示 一個集群中的事務(前提是這些集群中的機器的id 要相互不同),格式是 server_id : gno (數據庫實例 id : 事務 id)
前者用來標識機器,后者用來在一台機器中唯一標識事務。
數據庫會維護 一個 GTID 的集合,標識所有該數據庫實例執行過的事務
於是有一種新的模式,從庫把自己的 GTID 集合發送給 主庫,主庫檢查 從庫的 GTID 集合 和 自己 GTID 集合的差集,這個差集就是 主庫需要給從庫的 數據。
倘若從庫要求的 GTID 集合在 主庫上沒有,那么可能是 主庫刪除了對應的 binlog,主庫會報錯(但是如果是從庫自己在 成為從庫前 執行了一些事務,這些事務的 GTID 也會被發給主庫嗎?如果發的話不就必然報錯?)(其實可以 在從庫上使用命令 : set global gtid_purged = '24024e52-bd95-11e4-9c6d-926853670d0b:1 - N'; 來告訴從庫,哪些binlog 已經被 purge 了,不要再發給主庫了,不要再向主庫索要了 ,注意上面的 gno 是 1 - N 這種格式 ,只有事務提交了,才能使 gno + 1)
一般的 gtid 是遞增的,gtid_next=automatic 可以設置遞增。
也可以自己設置 set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
這樣的話,被設置的機器 若執行下一個事務,這個事務 使用的 gtid 就是上面手工設置的 gtid。
假如有 互為 主從的 兩個庫 A B ,現在的寫請求都是打到 A 上的,假如要加索引,又不想影響A 上的效率
1.先 停掉 B 向 A 發送 binlog
2.在 從庫 B 上,加索引,假設這條 加索引的 DDL 的 GID 是 B:X
3.在 主庫 A 上,設置 gid_next = A:X , 並且執行一個空事務(在設置 gid_next = A:X 到執行空事務期間,可不可能有其他的事務搶占,哦,好像就算被搶了也無所謂,反正我們要做的只是讓這個gid 對應的 DDL 不被執行)
4. A 重新認 B 為 主庫,會發送 A 的 GID 集合(這期間沒有停止 A 向 B 發送 binlog,所以 B 的 gid 集合應該和 A 一致,所以不會報錯),A 從 B 那里獲取 B 的 gid 集合,因為之前已經執行過那條 DDL 語句對應事務的 gtid 代表的假事務,所以不會執行 這條 DDL。
把寫請求打到 庫 B 上,並且再來一次剛才的過程,只不過這次反過來,B 是之前的 A ,A 是之前的 B。
5. 最后 A 和 B 上 都有了 索引。