一、MySQL 主從複制原理闡述
Mysql主從複制:簡單來說就是Mysql 同步,Ab 複制等,主從複制是
單向
的,隻能從 Master 複制到 Slave 上,延時基本上是毫秒級别的(排除網絡延遲等問題)。一組複制結構中可以有多個Slave,對于 Master一般場景推薦隻有一個,【根據您的業務進行調配,主主複制、延遲複制等】
Mysql 傳統複制是基于 Mysql 二進制檔案(Mysql-Bin.000001),加上對應日志檔案中每個事件的偏移量位置點(Postion)。
同步更新部落格:www.dgstack.cn
MySQL主從複制 三個線程來實作:
主庫:
Binlog Dump
從庫:
Io
和
Sql
線程
Mysql同步原理簡述:
- Master 所有資料庫變更寫進 Binary Log, 主庫線程 Binlog Dump 把 Binary Log 内容發送到從庫 Slave 上(Slave 被動接受資料,不是主動去擷取)。
- Slave Io 線程讀取 Master 上 Binary Log 日志資訊,把接受到的 Binary Log 日志寫到本地中繼日志 Relay Log
- Slave Sql 線程讀取 Ralay Log 日志内容寫入本地資料庫執行個體
二、MySQL 異步複制架構中 GTID 複制的原理闡述
2.1 GTID 的概述:
1、全局事物辨別:global transaction identifieds。
2、GTID 事物是全局唯一性的,且
一個事務對應一個 GTID
。
3、一個 GTID 在一個伺服器上隻執行一次,
避免重複執行
導緻資料混亂或者主從不一緻。
4、
GTID
用來代替
classic
的複制方法,不在使用 binlog+pos 開啟複制。而是使用 master_auto_postion=1 的方式自動比對
GTID 斷點進行複制
5、
MySQL-5.6.5
開始支援的,MySQL-5.6.10 後開始完善。
6、在傳統的 slave 端,binlog 是不用開啟的,但是在
GTID 中,slave 端的 binlog 是必須開啟的
,目的是記錄執行過的 GTID(強制);但是從 5.7.5 版本開始無需在 GTID 模式下啟用參數 log_slave_updates
2.2 GTID 的組成部分:
- GTID = source_id:transaction_id
- source_id 正常即是
,在第一次啟動時生成(函數 generate_server_uuid),并持久化到 DATADIR/auto.cnf 檔案裡。server_uuid
- transaction_id 是
(sequence number),在每台 MySQL 伺服器上都是從 1 開始自增長的序列,是事務的唯一辨別。例如:3E11FA47-71CA-11E1-9E33-C80AA9429562:23順序化的序列号
- GTID 的集合(GTIDs)可以用 source_id+transaction_id 範圍表示,例如:3E11FA47-71CA-11E1-9E33-C80AA9429562:1-18
- 複雜一點的:如果這組 GTIDs 來自不同的 source_id,各組 source_id 之間用逗号分隔;如果事務序号有多個範圍區間,各組範圍之間用冒号分隔,例如:3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5:11-18,2C256447-3F0D-431B-9A12-575BB20C1507:1-27
2.3 GTID 如何産生
- GTID 的生成受
控制。gtid_next
- 在 Master 上,gtid_next 是預設的 AUTOMATIC,即 GTID 在每次事務送出時
。它從目前已執行的 GTID 集合(即 gtid_executed)中,找一個大于 0 的未使用的最小值作為下個事務 GTID。同時将 GTID 寫入到 binlog(set gtid_next 記錄),在實際的更新事務記錄之前。自動生成
- 在 Slave 上,從 binlog 先讀取到主庫的 GTID(即 set gtid_next 記錄),而後執行的事務采用該 GTID。
2.4 GTID 相關的變量
GTID_EXECUTED
#表示已經在該執行個體上執行過的事務; 執行 RESET MASTER 會将該變量置空; 我們還可以通過設定 GTID_NEXT 在執行一個空事務,來影響 GTID_EXECUTED
GTID_PURGED
#已經被删除了 binlog 的事務,它是 GTID_EXECUTED 的子集,隻有在 GTID_EXECUTED 為空時才能設定該變量,修改 GTID_PURGED 會同時更新 GTID_EXECUTED 和 GTID_PURGED 的值。
GTID_OWNED
#表示正在執行的事務的 gtid 以及對應的線程 ID。
GTID_NEXT
#SESSION 級别變量,表示下一個将被使用的 GTID。
2.5 GTID 比傳統複制的優勢與限制:
GTID優勢
更簡單的實作 failover,不用以前那樣在需要找 log_file 和 log_Pos。
更簡單的搭建主從複制。
複制叢集有一個統一的方式識别複制位置,給叢集管理帶來了便利。
正常情況下,GTID 是連續沒有空洞的,是以主從庫出現資料沖突時,可以用添加空事物的方式進行跳過。
GTID的限制:
1、在一個事務裡面混合使用引擎,如 Innodb(支援事務)、MyISAM(不支援事務), 造成多個 GTIDs 和同一個事務相關聯出錯
2、CREATE TABLE…..SELECT 不能使用,該語句産生的兩個 event 在某一情況 會使用同一個 GTID(同一個 GTID 在 slave 隻能被使用一次)
1th event:建立表語句 create table
2th event:插入資料語句 insert
3、CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE 不能在事務内使用 (啟用了–enforce-gtid-consistency 參數)。
三、GTID 的工作原理:
master 更新資料時,會在事務前産生 GTID,`一同記錄到 binlog 日志中`。
slave 端的 i/o 線程将變更的 binlog,寫入到本地的 relay log 中,讀取值是根據`gitd_next變量`,告訴我們slave下一個執行哪個GTID。
sql 線程從 relay log 中擷取 GTID,然後對比 slave 端的 binlog 是否有記錄。
如果有記錄,說明該 GTID 的事務已經執行,slave 會忽略。
如果沒有記錄,slave 就會從 relay log 中執行該 GTID 的事務,并記錄到 binlog。
在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有二級索引就用全部掃描。
3.1 pos 與 GTID 有什麼差別?
兩者都是日志檔案裡事件的一個标志,如果将整個 mysql 叢集看作一個整體,
pos
就是局部的,
GTID
就是全局的.
上圖就是一個 mysql 節點的叢集,一主兩從,在 master,slave1,slave2 日志檔案裡的 pos,都各不相同,就是一個 event,在 master 的日志裡,pos 可能是 700,而在 slave1,slave2 裡,pos 可能就是 300,400 了,因為衆多 slave 也可能不是同時加入叢集的,不是從同一個位置進行同步.
而 GTID,在 master,slave1,slave2 各自的日志檔案裡,同一個 event 的 GTID 值都是一樣的.
3.2 為什麼要有這個區分呢?
大家都知道,這整個叢集架構的節點,通常情況下,是
master
在工作,其他兩個結點做備份,而且,各個節點的機器,性能不可能完全一緻,是以,在做備份時,備份的速度就不一樣,當 master 突然宕掉之後,馬上會啟用從節點機器,接管 master 的工作,當有多個從節點時,選擇備份日志檔案最接近 master 的那個節點;
現在就出現情況了,當 salve1 變成主節點,那slave2就應該從 slave1 去擷取日志檔案,進行同步.
大家來想想這個問題
如果使用的是pos,三者的pos不一緻,slave2 怎麼去擷取它目前要同步的事件在 slave1 裡的 pos 呢?????????
是以就有了
GTID
全局的,将所有節點對于同一個 event 的标記完全一緻,當 master 宕掉之後,slave2 根據同一個 GTID 直接去讀取 slave1 的日志檔案,繼續同步.
四、MySQL經典主從配置實戰
4.1 核心配置 my.cnf
[mysqld]
log-bin
server-id
gtid_mode=off #禁掉 gtid
4.2 添加主從複制使用者
grant replication slave on *.* to 'repl'@'%' identified by 'qiuyuetao';
flush privileges;
4.3 添加一個新的從庫
擷取主庫上一個帶 binlog 及 pos 偏移量的備份
在從庫上恢複後
>change master to
master_host='192.168.199.117',
master_user='slave',
master_port=7000,
master_password='slavepass',
master_log_file='mysql-bin.000008',
master_log_pos=896;
>start slave;
>show slave status\G;
跳過複制錯誤
stop slave;
set global sql_slave_skip_counter=1;
start slave;
show slave status\G;
如果出現錯誤代碼,那麼一般錯誤,可以跳過,具體哪些錯誤代碼可以跳,哪些不能調,後續我會在專門寫一篇文章。
五、GTID 配置
所有節點上都要進行設定
vim /etc/my.cnf
[mysqld]
#GTID:
gtid_mode=on #開啟 GTID
enforce-gtid-consistency=on
#binlog
log-bin=mysql-bin #開啟二進制檔案系統
server-id=1 #必須為 1-231 之間的一個正整數值,各個值節點不能一緻
log-slave-updates=1 # 5.7.5 版本開始無需在 GTID 模式下啟用參數 log_slave_updates
在從節點上 mysql 設定:
mysql>change master to master_host='xxxxxxx',master_user='xxxxxx',master_password='xxxxx',MASTER_AUTO_POSITION=1;
mysql> start slave;
mysql> stop slave io_thread; #重新開機 io 線程,重新整理狀态
mysql> start slave io_thread;
注意事項:
master_host
,
master_user
master_password
與經典的Mysql主從複制一緻。
唯一不一緻的是使用了
MASTER_AUTO_POSITION
參數
當使用 MASTER_AUTO_POSITION 參數的時候,MASTER_LOG_FILE,MASTER_LOG_POS 參數不能使用
如果想要從
GTID 配置回 pos
,再次執行這條語句,不過把 MASTER_AUTO_POSITION 置為 0
GTID添加從庫有兩種方法:
1.如果 master 所有的 binlog 還在,安裝 slave 後,直接 change master 到 master
原理: 直接擷取 master 所有的 gtid 并執行
優點: 簡單
缺點: 如果 binlog 太多,資料完全同步需要的時間較長,并且需要 master 一開始就啟用了 GTID
總結:适用于 master 也是建立不久的情況
2.通過 master 或者其它 slave 的備份搭建新的 slave.
原理:擷取 master 的資料和這些資料對應的 GTID 範圍,然後通過在 slave 設定@@GLOBAL.GTID_PURGED 進而跳過備份包含的 GTID
優點: 可以避免第一種方法中的不足
缺點: 操作相對複雜
總結:适用于擁有較大資料集的情況
GTID 添加從庫:
1、mysqldump
在備份的時候需要指定–master-data
導出的語句中包括:
set @@GLOBAL.GTID_PURGED=’c8d960f1-83ca-11e5-a8eb-000c29ea831c:1-745497′;
#恢複時,需要先在slave上執行一個
reset master;
#再執行
change master to
2、percona xtrabackup
xtrabackup_binlog_info 包含了 GTID 在資訊
做從庫恢複後,需要手工設定:
set@@GLOBAL.GTID_PURGED='c8d960f1-83ca-11e5-a8eb-000c29ea831c:1-745497';
恢複後,執行 change master to
>change master to
master_host='192.168.199.117',
master_user='slave',
master_port=7000,
master_password='slavepass',
master_auto_position=1;
錯誤跳過
stop slave;
set gtid_next='xxxxxxxx:N';
begin;
commit;
set gtid_next='automatic';
start slave;
GTID的限制總結:
不支援非事務引擎(從庫報錯,stop slave; start slave; 忽略)
不支援 create table … select 語句複制(主庫直接報錯)
不允許在一個 SQL 同時更新一個事務引擎和非事務引擎的表
在一個複制組中,必須要求統一開啟CTID或是關閉GTID
開啟DTID需要重新開機(5.7中可能不需要)
開啟DTID後,就不在使用原來的傳統的複制方式
對于create temporary table 和drop temporary table語句不支援
不支援sql_slave_skip_counter
六、MySQL半同步複制
MySQL 複制預設是
異步複制
,Master 将事件寫入 binlog,但并不知道 Slave 是否或何時已經接收且已處理。在異步複制的機制的情況下,如果 Master 當機,事務在 Master 上已送出,但很可能這些
事務沒有傳到任何的 Slave 上
。假設有 Master->Salve 故障轉移的機制,此時 Slave 也可能會
丢失事務
官方半同步複制的概念:
1.當 Slave 主機連接配接到 Master 時,能夠檢視其是否處于半同步複制的機制。
2.當 Master 上開啟半同步複制的功能時,至少應該有一個 Slave 開啟其功能。此時,一個線程在 Master 上送出事務将受到阻塞,直到得知一個已開啟半同步複制功能的 Slave 已收到此事務的所有事件,或等待逾時。
3.當一個事務的事件都已寫入其 relay-log 中且已重新整理到磁盤上,Slave 才會告知已收到。
4.如果等待逾時,也就是 Master 沒被告知已收到,此時 Master 會自動轉換為異步複制的機制。當至少一個半同步的 Slave 趕上了,Master 與其 Slave 自動轉換為半同步複制的機制。
5.半同步複制的功能要在
Master,Slave 都開啟
,半同步複制才會起作用;否則,隻開啟一邊,它依然為異步複制。
同步(社群增強半同步),異步,半同步複制的比較:
同步複制:Master 送出事務,直到事務在所有的 Slave 都已送出,此時才會傳回用戶端,事務執行完畢。缺點:完成一個事務可能會有很大的
延遲
異步複制:當 Slave 準備好才會向 Master 請求 binlog。
缺點:不能保證一些事件都能夠被所有的 Slave 所接收。
解決主庫不關心日志是否被從庫讀到
master
[mysqld]
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 #1s
slave
[mysqld]
rpl_semi_sync_slave_enabled=1 複制參數