一、複製的意義 mysql的複製功能是構建基於MySql大規模,高性能應用的基礎,我們可以通過為伺服器配置一個或多個備庫來進行數據同步;複製功能不僅有利於構建高性能的應用,同時也是高可用性,可擴展行,災難恢復,備份以及數據倉庫等工作的基礎 二、複製的方式 Mysql支持3種方式:基於語句的複製、基於 ...
一、複製的意義
mysql的複製功能是構建基於MySql大規模,高性能應用的基礎,我們可以通過為伺服器配置一個或多個備庫來進行數據同步;複製功能不僅有利於構建高性能的應用,同時也是高可用性,可擴展行,災難恢復,備份以及數據倉庫等工作的基礎
二、複製的方式
Mysql支持3種方式:基於語句的複製、基於行的複製、混合複製。對應的binlog的格式也有三種:STATEMENT,ROW,MIXED
(1)基於語句的複製(SBR)
每一條會修改數據的sql語句會記錄到binlog中。優點是不需要記錄每一條sql語句和每一行的數據變化,減少了binlog日誌量,節約IO,提高性能。缺點是在某些情況下會導致master-slave中的數據不一致(如RAND(),UUID(),存儲過程,觸發器等)
(2)基於行的複製(RBR)
不記錄每條sql語句的上下文信息,轉而需記錄哪條數據被修改了,修改成什麼樣了。並且不會出現某些特定情況下的存儲過程、觸發器等在基於語句複製的模式下導致無法被正確複製的問題。缺點是會產生大量的日誌,尤其是alter table的時候會讓日誌暴漲,無法準確的判斷執行了那些sql,此外在備庫上改表的schema時會出現複製失敗,比如沒有在最後加列或刪除列
(3)混合複製(MRB)
以上兩種模式的混合使用,MySQL會根據執行的SQL語句選擇日誌保存方式,一般的複製使用STATEMENT模式保存binlog,對於STATEMENT模式無法複製的操作使用ROW模式保存binlog。
三、與複製相關的文件
mysql-bin.index:在伺服器上開啟二進位日誌的同時會生成一個和二進位日誌同名的但以.index作為尾碼的文件,該文件用於記錄磁碟上的二進位日誌文件,這裡的“index”並不是指表的索引,而是說這個文件的每一行包含了一個二進位的文件名,Mysql依賴於這個文件,除非在這個文件里有記錄否則mysql識別不了二進位文件
mysql-relay-bin-index:中繼日誌的索引文件和mysql-bin.index的作用類似
master.info:這個文件用於保存備庫鏈接到主庫所需要的信息,格式為存文本,不同的mysql版本,其記錄的信息也可能不同;此文件不能刪除,否則備庫在重啟後無法連接到主庫。此外這個文件以文本的形式記錄了複製用戶的密碼,所以要註意對此文件的許可權控制
relay-log.info:這個文件包含了當前備庫負責的二進位日誌和中繼日誌坐標(例如,備庫複製在主庫複製的位置),同樣也不用刪除這個文件,否則備庫在重啟後將無法獲知從哪個位置開始複製,可能導致重放已經執行的語句
四、複製的原理
1、主庫把數據更改記錄在二進位日誌中(Binary Log)中(這些記錄被稱為二進位日誌事件)
2、備庫啟動一個工作進程,稱為I/O線程,通過I/O線程向主庫建立一個普通的客戶端連接,備庫還會啟動一個SQL線程
3、在主庫上啟動一個特殊的二進位轉儲(binlog dump)線程(該線程沒有對應的SQL命令)
4、主庫上的二進位轉儲線程會讀取主庫上的二進位日誌中的事件通過socket連接發送給從庫,備庫上的I/O線程會將接收到的事件記錄到中繼日誌中;主庫上的二進位轉儲線程不會對事件進行輪詢,如果該進程追趕上了主庫,它將進入睡眠狀態,直到主庫發送信號量通知其有新的事件產生時才會被喚醒
5、備庫的SQL線程執行最後一步,該線程從中繼日誌中讀取事件併在備庫中執行,從而實現備庫數據的更新。當SQL線程追趕上I/O線程時,中繼日誌通常已經在系統緩存中,所以中繼日誌的開銷很低。SQL線程執行的事件也可以通過配置選項來決定是否寫入備庫的二進位日誌中
五、複製的場景
1、同步複製場景
MySQL Cluster(NDB)採用同步複製,保證集群內數據的強一致性。
其基於shared-nothing架構的記憶體存儲引擎,應用場景有限,業務很少採用。
2、非同步複製場景
數據寫入主庫即返回,從庫通過IO線程拉取日誌,再通過SQL線程進行非同步回放。
優點:寫入主庫即可,無數據複製代價
缺點:業務數據讀取不一致;主庫crash時,從庫數據和主庫不一致
應用場景:對數據讀取一致性要求不高的業務
3、半同步複製場景
1) 半同步
MySQL 5.5引入了半同步複製(semisync),保證至少有一個slave與master一致。
http://dev.mysql.com/doc/refman/5.6/en/replication-semisync.html
master把數據寫入後,將binlog發給slave,半同步複製不要求slave執行,slave收到日誌後就發送ack即可,master收到第一個ack後,事務才算結束。
但這種方式可能會造成主從數據不一致:當master innodb commit執行成功,再把binlog同步給slave之前crash,就會造成數據不一致。
2) 分組半同步
採用半同步複製,同機房的從庫大概率首先返回ack,這樣跨機房容災成為空談。於是發展出了分組半同步(semisync + group slave),將不同機房劃分為不同的group,每個機房(group)至少有一個從庫返回ack,事務才算完成。
優點:半同步和分組半同步最大限度的保證了數據一致性
缺點:
引入同步(等待ack)造成的性能問題
單機房故障會hang住事務,需要退化為非同步
數據仍然存在不一致可能
主庫在commit和binlog同步之間crash
半同步在超時後會退化為非同步(預設10000ms)
3) 無損半同步
由於MySQL 5.5,5.6版本的半同步存在數據不一致問題(先commit後同步),MySQL 5.7.2引入了無損(loessness)半同步,即數據寫完slave的relay log後再commit。
https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.html
http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html
六、並行複製的方式
1、基於庫級別的並行複製
mysql5.6版本
io_thread:根據binlog dump協議從主庫拉取binlog, 並將binlog轉存到本地的relaylog;
Coordinator_thread:負責讀取relay log,將讀取的binlog event以事務為單位分發到各個worker thread進行執行;在必要時自己執行binlog event
worker_thread(sql_thread):執行分配到的binlog event,各個線程之間互不影響
多線程原理:
sql_thread的分發是依據當前事務鎖操作的資料庫名稱來進行分發,如果事務是跨分片的,需要等待已分配的改資料庫的事務全部執行完畢,才會繼續分發
2、基於GroupCommit的並行複製
mysql5.7版本,並行複製的實現添加了另外一種並行的方式,即主庫在orderd_commit中的第二階段的時候,將同一批commit的binlog打上一個相同的seqno標簽,同一時間戳的事務在備庫是可以同時執行的,因此簡化了並行複製的邏輯,並打破了mysql5.6版本相同Db不能並行複製的限制。備庫在執行時,具有同一seqno的事務在備庫可以併發的執行,互補干擾,也不需要綁定信息,後一批seqno的事務需要等待前一批seqno的事務執行完後才可以執行
優點:對SRB和RBR都支持
缺點:事務越大,DML操作越多,主庫上能同時提交的幾率越小,從庫上回覆的並行度也就越小
業務正常壓力模式下,主庫同時提交的事務並不多,歷史備份或者從庫schema change後在追若幹前的數據所需要的時間無法顯著的縮短
3、基於表級別的並行複製
將原有mysql5.6的基於Db基本的分發改成db_name+table_name,不同db_name+table_name的可以分發給不同的worker_thread進行執行
4、基於行級別的並行複製
基於Mysql Row格式的binlog記錄了每一行的所有欄位信息,因此可以在從中取出每一行的primary key或者unique key,通過db_name+table_name+primary_key來進行衝突檢查,對於不衝突的事務可以並行執行,達到行級別的並行複製缺點:只支持RBR
七、其他知識點
1、server id
server_id定義在my.cnf中 server_id = xxx,必須明確指定一個唯一的伺服器ID,預設的伺服器ID通常為1(這和版本相關,一些mysql版本根本不允許使用這個值)。使用預設值可能會導致和其他伺服器的ID衝突,因此要保證它是唯一且不變的
Mysql在複製過程中為了防止環形無限複製,當SQL線程讀取中繼日誌的時候,會丟去事件中記錄的伺服器ID和該伺服器本身ID相同的事件,從而打破複製過程中的無限迴圈。在某些複製拓撲結構下打破無限迴圈非常重要,例如主-主複製結構
2、redo log 和 bin log
與oracle 不同,mysql 的主庫與備庫的同步是通過 binlog 實現的,而redo日誌只做為mysql 實例的crash recovery使用。
mysql在4.x 的時候放棄redo 的同步策略而引入 binlog的同步,一個重要原因是為了相容其它非事務存儲引擎,否則主備同步是沒有辦法進行的。
redo 日誌同步屬於物理同步方法,簡單直接,將修改的物理部分傳送到備庫執行,主備共用一致的 LSN,只要保證 LSN 相同即可,同一時刻,只能主庫或備庫一方接受寫請求; binlog的同步方法屬於邏輯複製,分為statement 或 row 模式,其中statement記錄的是SQL語句,Row 模式記錄的是修改之前的記錄與修改之後的記錄,即前鏡像與後鏡像;備庫通過binlog dump 協議拉取binlog,然後在備庫執行。如果拉取的binlog是SQL語句,備庫會走和主庫相同的邏輯,如果是row 格式,則會調用存儲引擎來執行相應的修改。
PS:推薦一個好朋友的微信公眾號,一個每天都在思考或者在思考路上的公眾號運營少女~