最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

MySQL主從復(fù)制原理剖析與應(yīng)用實踐

2023-04-11 10:21 作者:vivo互聯(lián)網(wǎng)技術(shù)  | 我要投稿

vivo 互聯(lián)網(wǎng)服務(wù)器團(tuán)隊- Shang Yongxing

MySQL Replication(主從復(fù)制)是指數(shù)據(jù)變化可以從一個MySQL Server被復(fù)制到另一個或多個MySQL Server上,通過復(fù)制的功能,可以在單點服務(wù)的基礎(chǔ)上擴(kuò)充數(shù)據(jù)庫的高可用性、可擴(kuò)展性等。

一、背景

MySQL在生產(chǎn)環(huán)境中被廣泛地應(yīng)用,大量的應(yīng)用和服務(wù)都對MySQL服務(wù)存在重要的依賴關(guān)系,可以說如果數(shù)據(jù)層的MySQL實例發(fā)生故障,在不具備可靠降級策略的背景下就會直接引發(fā)上層業(yè)務(wù),甚至用戶使用的障礙;同時MySQL中存儲的數(shù)據(jù)也是需要盡可能地減少丟失的風(fēng)險,以避免故障時出現(xiàn)數(shù)據(jù)丟失引發(fā)的資產(chǎn)損失、客訴等影響。

在這樣對服務(wù)可用性和數(shù)據(jù)可靠性需求的背景下,MySQL在Server層提供了一種可靠的基于日志的復(fù)制能力(MySQL Replication),在這一機(jī)制的作用下,可以輕易構(gòu)建一個或者多個從庫,提高數(shù)據(jù)庫的高可用性、可擴(kuò)展性,同時實現(xiàn)負(fù)載均衡

  • 實時數(shù)據(jù)變化備份

  • 主庫的寫入數(shù)據(jù)會持續(xù)地在冗余的從庫節(jié)點上被執(zhí)行保留,減少數(shù)據(jù)丟失的風(fēng)險

  • 橫向拓展節(jié)點,支撐讀寫分離

  • 當(dāng)主庫本身承受壓力較大時,可以將讀流量分散到其它的從庫節(jié)點上,達(dá)成讀擴(kuò)展性和負(fù)載均衡

  • 高可用性保障

  • 當(dāng)主庫發(fā)生故障時,可以快速的切到其某一個從庫,并將該從庫提升為主庫,因為數(shù)據(jù)都一樣,所以不會影響系統(tǒng)的運(yùn)行

具備包括但不限于以上特性的MySQL集群就可以覆蓋絕大多數(shù)應(yīng)用和故障場景,具備較高的可用性與數(shù)據(jù)可靠性,當(dāng)前存儲組提供的生產(chǎn)環(huán)境MySQL就是基于默認(rèn)的異步主從復(fù)制的集群,向業(yè)務(wù)保證可用性99.99%,數(shù)據(jù)可靠性99.9999%的在線數(shù)據(jù)庫服務(wù)。

本文將深入探討MySQL的復(fù)制機(jī)制實現(xiàn)的方式, 同時討論如何具體地應(yīng)用復(fù)制的能力來提升數(shù)據(jù)庫的可用性,可靠性等。

二、復(fù)制的原理

2.1 Binlog 的引入

從比較寬泛的角度來探討復(fù)制的原理,MySQL的Server之間通過二進(jìn)制日志來實現(xiàn)實時數(shù)據(jù)變化的傳輸復(fù)制,這里的二進(jìn)制日志是屬于MySQL服務(wù)器的日志,記錄了所有對MySQL所做的更改。這種復(fù)制模式也可以根據(jù)具體數(shù)據(jù)的特性分為三種:

  • Statement:基于語句格式

  • Statement模式下,復(fù)制過程中向獲取數(shù)據(jù)的從庫發(fā)送的就是在主庫上執(zhí)行的SQL原句,主庫會將執(zhí)行的SQL原有發(fā)送到從庫中。

  • Row:基于行格式

  • Row模式下,主庫會將每次DML操作引發(fā)的數(shù)據(jù)具體行變化記錄在Binlog中并復(fù)制到從庫上,從庫根據(jù)行的變更記錄來對應(yīng)地修改數(shù)據(jù),但DDL類型的操作依然是以Statement的格式記錄。

  • Mixed:基于混合語句和行格式

  • MySQL 會根據(jù)執(zhí)行的每一條具體的 SQL 語句來區(qū)分對待記錄的日志形式,也就是在 statement 和 row 之間選擇一種。

最早的實現(xiàn)是基于語句格式,在3.23版本被引入MySQL,從最初起就是MySQL Server層的能力,這一點與具體使用的存儲引擎沒有關(guān)聯(lián);在5.1版本后開始支持基于行格式的復(fù)制;在5.1.8版本后開始支持混合格式的復(fù)制。

這三種模式各有優(yōu)劣,相對來說,基于Row的行格式被應(yīng)用的更廣泛,雖然這種模式下對資源的開銷會偏大,但數(shù)據(jù)變化的準(zhǔn)確性以及可靠性是要強(qiáng)于Statement格式的,同時這種模式下的Binlog提供了完整的數(shù)據(jù)變更信息,可以使其應(yīng)用不被局限在MySQL集群系統(tǒng)內(nèi),可以被例如Binlogserver,DTS數(shù)據(jù)傳輸?shù)确?wù)應(yīng)用,提供靈活的跨系統(tǒng)數(shù)據(jù)傳輸能力, 目前互聯(lián)網(wǎng)業(yè)務(wù)的在線MySQL集群全部都是基于Row行格式的Binlog

2.2? Binlog 的要點

2.2.1 Binlog事件類型

對于Binlog的定義而言,可以認(rèn)為是一個個單一的Event組成的序列,這些單獨的Event可以主要分為以下幾類:

各類Event出現(xiàn)是具有顯著的規(guī)律的:

  • XID_EVENT標(biāo)志一個事務(wù)的結(jié)尾

  • 當(dāng)發(fā)生了DDL類型的QUERY_EVENT,那么也是一次事務(wù)的結(jié)束提交點,且不會出現(xiàn)XID_EVENT

  • GTID_EVENT只有開啟了GTID_MODE(MySQL版本大于5.6)

  • TABLE_MAP_EVENT必定出現(xiàn)在某個表的變更數(shù)據(jù)前,存在一對多個ROW_EVENT的情況

除了上面和數(shù)據(jù)更貼近的事件類型外,還有ROTATE_EVENT(標(biāo)識Binlog文件發(fā)生了切分),F(xiàn)ORMAT_DESCRIPTION_EVENT(定義元數(shù)據(jù)格式)等。

2.2.2 Binlog的生命周期

Binlog和Innodb Log(redolog)的存在方式是不同的,它并不會輪轉(zhuǎn)重復(fù)覆寫文件,Server會根據(jù)配置的單個Binlog文件大小配置不斷地切分并產(chǎn)生新的Binlog,在一個.index文件記錄當(dāng)前硬盤上所有的binlog文件名,同時根據(jù)Binlog過期時間回收刪除掉過期的Binlog文件,這兩個在目前自建數(shù)據(jù)庫的配置為單個大小1G,保留7天。

所以這種機(jī)制背景下,只能在短期內(nèi)追溯歷史數(shù)據(jù)的狀態(tài),而不可能完整追溯數(shù)據(jù)庫的數(shù)據(jù)變化的,除非是還沒有發(fā)生過日志過期回收的Server。?

2.2.3?Binlog事件示例

Binlog是對Server層生效的,即使沒有從庫正在復(fù)制主庫,只要在配置中開啟了log_bin,就會在對應(yīng)的本地目錄存儲binlog文件,使用mysqlbinlog打開一個Row格式的示例binlog文件:

如上圖,可以很明顯地注意到三個操作,創(chuàng)建數(shù)據(jù)庫test, 創(chuàng)建數(shù)據(jù)表test, 一次寫入引發(fā)的行變更,可讀語句(create, alter, drop, begin, commit.....)都可以認(rèn)為是QUERY_EVENT,而Write_rows就屬于ROW_EVENT中的一種。

在復(fù)制的過程中,就是這樣的Binlog數(shù)據(jù)通過建立的連接發(fā)送到從庫,等待從庫處理并應(yīng)用。

2.2.4 復(fù)制基準(zhǔn)值

Binlog在產(chǎn)生時是嚴(yán)格有序的,但它本身只具備秒級的物理時間戳,所以依賴時間進(jìn)行定位或排序是不可靠的,同一秒可能有成百上千的事件,同時對于復(fù)制節(jié)點而言,也需要有效可靠的記錄值來定位Binlog中的水位,MySQL Binlog支持兩種形式的復(fù)制基準(zhǔn)值,分別是傳統(tǒng)的Binlog File:Binlog Position模式,以及5.6版本后可用的全局事務(wù)序號GTID。

  • FILE Position

只要開啟了log_bin,MySQL就會具有File Position的位點記錄,這一點不受GTID影響。

這個概念相對來說更直觀,可以直接理解為當(dāng)前處在File對應(yīng)編號的Binlog文件中,同時已經(jīng)產(chǎn)生了合計Position bytes的數(shù)據(jù),如例子中所示即該實例已經(jīng)產(chǎn)生了381808617 bytes的Binlog,這個值在對應(yīng)機(jī)器直接查看文件的大小也是匹配的,所以File Postion就是文件序列與大小的對應(yīng)值。

基于這種模式開啟復(fù)制,需要顯式地在復(fù)制關(guān)系中指定對應(yīng)的File和Position:

這個值必須要準(zhǔn)確,因為這種模式下從庫獲取的數(shù)據(jù)完全取決于有效的開啟點,那么如果存在偏差,就會丟失或執(zhí)行重復(fù)數(shù)據(jù)導(dǎo)致復(fù)制中斷。

  • GTID

MySQL 會在開啟GTID_MODE=ON的狀態(tài)下,為每一個事務(wù)分配唯一的全局事務(wù)ID,格式為:server_uuid:id

其中e2e0a733-3478-11eb-90fe-b4055d009f6c用于唯一地標(biāo)識產(chǎn)生該Binlog事件的實例,1-753表示已經(jīng)產(chǎn)生或接收了由e2e0a733-3478-11eb-90fe-b4055d009f6c實例產(chǎn)生的753個事務(wù);

從庫在從主庫獲取Binlog Event時,自身的執(zhí)行記錄會保持和獲取的主庫Binlog GTID記錄一致,還是以e2e0a733-3478-11eb-90fe-b4055d009f6c:1-753,如果有從庫對e2e0a733-3478-11eb-90fe-b4055d009f6c開啟了復(fù)制,那么在從庫自身執(zhí)行show master status也是會看到相同的值。

如果說從庫上可以看到和復(fù)制的主庫不一致的值,那么可以認(rèn)為是存在errant GTID,這個一般是由于主從切換或強(qiáng)制在從庫上執(zhí)行了寫操作引發(fā),正常情況下從庫的Binlog GTID應(yīng)該和主庫的保持一致;

基于這種模式開啟復(fù)制,不需要像File Position一樣指定具體的值,只需要設(shè)置:

從庫在讀取到Binlog后,會自動根據(jù)自身Executed_GTID_Set記錄比對是否存在已執(zhí)行或未執(zhí)行的Binlog事務(wù),并做對應(yīng)的忽略和執(zhí)行操作。

2.3 復(fù)制的具體流程

2.3.1?基本復(fù)制流程

當(dāng)主庫已經(jīng)開啟了binlog( log_bin = ON ),并正常地記錄binlog,如何開啟復(fù)制?

這里以MySQL默認(rèn)的異步復(fù)制模式進(jìn)行介紹:

  1. 首先從庫啟動I/O線程,跟主庫建立客戶端連接。

  2. 主庫啟動binlog dump線程,讀取主庫上的binlog event發(fā)送給從庫的I/O線程,I/O線程獲取到binlog event之后將其寫入到自己的Relay Log中。

  3. 從庫啟動SQL線程,將等待Relay中的數(shù)據(jù)進(jìn)行重放,完成從庫的數(shù)據(jù)更新。

總結(jié)來說,主庫上只會有一個線程,而從庫上則會有兩個線程。

  • 時序關(guān)系

當(dāng)集群進(jìn)入運(yùn)行的狀態(tài)時,從庫會持續(xù)地從主庫接收到Binlog事件,并做對應(yīng)的處理,那么這個過程中將會按照下述的數(shù)據(jù)流轉(zhuǎn)方式:

  1. Master將數(shù)據(jù)更改記錄在Binlog中,BinlogDump Thread接到寫入請求后,讀取對應(yīng)的Binlog

  2. Binlog信息推送給Slave的I/O Thread。

  3. Slave的I/O 線程將讀取到的Binlog信息寫入到本地Relay Log中。

  4. Slave的SQL 線程讀取Relay Log中內(nèi)容在從庫上執(zhí)行。

上述過程都是異步操作,所以在某些涉及到大的變更,例如DDL改變字段,影響行數(shù)較大的寫入、更新或刪除操作都會導(dǎo)致主從間的延遲激增,針對延遲的場景,高版本的MySQL逐步引入了一些新的特性來幫助提高事務(wù)在從庫重放的速度。

  • Relay Log的意義

Relay log在本質(zhì)上可以認(rèn)為和binlog是等同的日志文件,即使是直接在本地打開兩者也只能發(fā)現(xiàn)很少的差異;

在MySQL 4.0 之前是沒有Relay Log這部分的,整個過程中只有兩個線程。但是這樣也帶來一個問題,那就是復(fù)制的過程需要同步的進(jìn)行,很容易被影響,而且效率不高。例如主庫必須要等待從庫讀取完了才能發(fā)送下一個binlog事件。這就有點類似于一個阻塞的信道和非阻塞的信道。

在流程中新增Relay Log中繼日志后,讓原本同步的獲取事件、重放事件解耦了,兩個步驟可以異步的進(jìn)行,Relay Log充當(dāng)了緩沖區(qū)的作用。Relay Log包含一個relay-log.info的文件,用于記錄當(dāng)前復(fù)制的進(jìn)度,下一個事件從什么Pos開始寫入,該文件由SQL線程負(fù)責(zé)更新。

對于后續(xù)逐漸引入的特殊復(fù)制模式,會存在一些差異,但整體來說,是按照這個流程來完成的。

2.3.2?半同步復(fù)制

異步復(fù)制的場景下,不能確保從庫實時更新到和主庫一致的狀態(tài),那么如果在出現(xiàn)延遲的背景下發(fā)生主庫故障,那么兩者間的差異數(shù)據(jù)還是無法進(jìn)行保障,同時也無法在這種情況下進(jìn)行讀寫分離,而如果說由異步改為完全同步,那么性能開銷上又會大幅提高,很難滿足實際使用的需求。

基于這一的背景,MySQL從5.5版本開始引入了半同步復(fù)制機(jī)制來降低數(shù)據(jù)丟失的概率,在這種復(fù)制模式中,MySQL讓Master在某一個時間點等待一個Slave節(jié)點的 ACK(Acknowledge Character)消息,接收到ACK消息后才進(jìn)行事務(wù)提交,這樣既可以減少對性能的影響,還可以相對異步復(fù)制獲得更強(qiáng)的數(shù)據(jù)可靠性。

介紹半同步復(fù)制之前先快速過一下 MySQL 事務(wù)寫入碰到主從復(fù)制時的完整過程,主庫事務(wù)寫入分為 4個步驟:

  1. InnoDB Redo File Write (Prepare Write)

  2. Binlog File Flush & Sync to Binlog File

  3. InnoDB Redo File Commit(Commit Write)

  4. Send Binlog to Slave

  • 當(dāng)Master不需要關(guān)注Slave是否接受到Binlog Event時,即為異步主從復(fù)制

  • 當(dāng)Master需要在第3步Commit Write回復(fù)客戶端前等待Slave的ACK時,為半同步復(fù)制(after-commit)

  • 當(dāng)Master需要在第2步Flush&Sync,即Commit前等待Slave的ACK時,為增強(qiáng)半同步復(fù)制(after-sync)


  • 時序關(guān)系

從半同步復(fù)制的時序圖來看,實際上只是在主庫Commit的環(huán)節(jié)多了等待接收從庫ACK的階段,這里只需要收到一個從節(jié)點的ACK即可繼續(xù)正常的處理流程,這種模式下,即使主庫宕機(jī)了,也能至少保證有一個從庫節(jié)點是可以用的,此外還減少了同步時的等待時間。

2.3.3 小結(jié)

在當(dāng)前生產(chǎn)環(huán)境的在線數(shù)據(jù)庫版本背景下,由MySQL官方提供的復(fù)制方式主要如上文介紹的內(nèi)容,當(dāng)然目前有還很多基于MySQL或兼容MySQL的衍生數(shù)據(jù)庫產(chǎn)品,能在可用性和可靠性上做更大的提升,本文就不繼續(xù)展開這部分的描述。

2.4 復(fù)制的特性

目前已經(jīng)提及的復(fù)制方式,存在一個顯著的特性:無法回避數(shù)據(jù)延遲的場景,異步復(fù)制會使得從庫的數(shù)據(jù)落后,而半同步復(fù)制則會阻塞主庫的寫入,影響性能。

MySQL早期的復(fù)制模式中,從庫的IO線程和SQL線程本質(zhì)上都是串行獲取事件并讀取重放的,只有一個線程負(fù)責(zé)執(zhí)行Relaylog,但主庫本身接收請求是可以并發(fā)地,性能上限只取決于機(jī)器資源瓶頸和MySQL處理能力的上限,主庫的執(zhí)行和從庫的執(zhí)行(SQL線程應(yīng)用事件)是很難對齊的,這里引用一組測試數(shù)據(jù):

  • 機(jī)器:64核 256G,MySQL 5.7.29

  • 測試場景:常規(guī)的INSERT,UPDATE壓測場景

  • 結(jié)果:MySQL Server的IO線程速度以網(wǎng)絡(luò)上的數(shù)據(jù)量評估,每秒超過100MB,正常是可以覆蓋業(yè)務(wù)使用的,然而SQL線程的預(yù)估速度只有21~23MB/s,如果是涉及UPDATE場景,性能還會減少;

  • 需要注意的是,以上結(jié)果是在高版本的MySQL具備并行復(fù)制能力的前提下取得,如果是不具備該特性的版本,性能會更差。

期望業(yè)務(wù)層限制使用是不現(xiàn)實的,MySQL則在5.6版本開始嘗試引入可用的并行復(fù)制方案,總的來說,都是通過嘗試加強(qiáng)在從庫層面的應(yīng)用速度的方式。

2.4.1 基于Schema級別的并行復(fù)制

基于庫級別的并行復(fù)制是出于一個非常簡易的原則,實例中不同Database/Schema內(nèi)的數(shù)據(jù)以及數(shù)據(jù)變更是無關(guān)的,可以并行去處置。

在這種模式中,MySQL的從節(jié)點會啟動多個WorkThread ,而原來負(fù)責(zé)回放的SQLThread會轉(zhuǎn)變成Coordinator角色,負(fù)責(zé)判斷事務(wù)能否并行執(zhí)行并分發(fā)給WorkThread。

如果事務(wù)分別屬于不同的Schema,并且不是DDL語句,同時沒有跨Schema操作,那么就可以并行回放,否則需要等所有Worker線程執(zhí)行完成后再執(zhí)行當(dāng)前日志中的內(nèi)容。

對于從庫而言,如果接收到了來自主庫的aksay_record以及proxy_encrypt內(nèi)的數(shù)據(jù)變更,那么它是可以同時去處理這兩部分Schema的數(shù)據(jù)的。

但是這種方式也存在明顯缺陷和不足,首先只有多個Schema流量均衡的情況下才會有較大的性能改善,但如果存在熱點表或?qū)嵗现挥幸粋€Schema有數(shù)據(jù)變更,那么這種并行模式和早期的串行復(fù)制也不存在差異;同樣,雖然不同Schema的數(shù)據(jù)是沒有關(guān)聯(lián),這樣并行執(zhí)行也會影響事務(wù)的執(zhí)行順序,某種程度來說,整個Server的因果一致性被破壞了。

2.4.2?基于組提交的復(fù)制(Group Commit)

基于Schema的并行復(fù)制在大部分場景是沒有效力的,例如一庫多表的情況下,但改變從庫的單執(zhí)行線程的思路被延續(xù)了下來,在5.7版本新增加了一種基于事務(wù)組提交的并行復(fù)制方式,在具體介紹應(yīng)用在復(fù)制中的組提交策略前,需要先介紹Server本身Innodb引擎提交事務(wù)的邏輯:

Binlog的落盤是基于sync_binlog的配置來的,正常情況都是取sync_binlog=1,即每次事務(wù)提交就發(fā)起fsync刷盤。

主庫在大規(guī)模并發(fā)執(zhí)行事務(wù)時,因為每個事務(wù)都觸發(fā)加鎖落盤,反而使得所有的Binlog串行落盤,成為性能上的瓶頸。針對這個問題,MySQL本身在5.6版本引入了事務(wù)的組提交能力(這里并不是指在從庫上應(yīng)用的邏輯),設(shè)計原理很容易理解,只要是能在同一個時間取得資源,開啟Prepare的所有事務(wù),都是可以同時提交的。

在主庫具有這一能力的背景下,可以很容易得發(fā)現(xiàn)從庫也可以應(yīng)用相似的機(jī)制來并行地去執(zhí)行事務(wù),下面介紹MySQL具體實現(xiàn)經(jīng)歷的兩個階段:

  • 基于Commit-Parents-Based

MySQL中寫入是基于鎖的并發(fā)控制,所以所有在Master端同時處于Prepare階段且未提交的事務(wù)就不會存在鎖沖突,在Slave端執(zhí)行時都可以并行執(zhí)行。

因此可以在所有的事務(wù)進(jìn)入prepare階段的時候標(biāo)記上一個logical timestamp(實現(xiàn)中使用上一個提交事務(wù)的sequence_number),在Slave端同樣timestamp的事務(wù)就可以并發(fā)執(zhí)行。

但這種模式會依賴上一個事務(wù)組的提交,如果本身是不受資源限制的并發(fā)事務(wù),卻會因為它的commit-parent沒有提交而無法執(zhí)行;

  • 基于Logic-Based

針對Commit-Parent-Based中存在的限制進(jìn)行了解除,純粹的理解就是只有當(dāng)前事務(wù)的sequence_number一致就可以并發(fā)執(zhí)行,只根據(jù)是否能取得鎖且無沖突的情況即可以并發(fā)執(zhí)行,而不是依賴上一個已提交事務(wù)的sequence_number。

三、應(yīng)用

當(dāng)前vivo的在線MySQL數(shù)據(jù)庫服務(wù)標(biāo)準(zhǔn)架構(gòu)是基于一主一從一離線的異步復(fù)制集群,其中一從用于業(yè)務(wù)讀請求分離,離線節(jié)點不提供讀服務(wù),提供給大數(shù)據(jù)離線和實時抽數(shù)/DB平臺查詢以及備份系統(tǒng)使用;針對這樣的應(yīng)用背景,存儲研發(fā)組針對MySQL場景提供了兩種額外的擴(kuò)展服務(wù):

3.1?應(yīng)用高可用系統(tǒng)+中間件

雖然MySQL的主從復(fù)制可以提高系統(tǒng)的高可用性,但是MySQL在5.6,5.7版本是不具備類似Redis的自動故障轉(zhuǎn)移的能力,如果主庫宕機(jī)后不進(jìn)行干預(yù),業(yè)務(wù)實際上是無法正常寫入的,故障時間較長的情況下,分離在從庫上的讀也會變得不可靠。

3.1.1 VSQL(原高可用2.0架構(gòu))

那么在當(dāng)前這樣標(biāo)準(zhǔn)一主二從架構(gòu)的基礎(chǔ)上,為系統(tǒng)增加HA高可用組件以及中間件組件強(qiáng)化MySQL服務(wù)的高可用性、讀拓展性、數(shù)據(jù)可靠性:

  • HA組件管理MySQL的復(fù)制拓?fù)洌?fù)責(zé)監(jiān)控集群的健康狀態(tài),管理故障場景下的自動故障轉(zhuǎn)移;

  • 中間件Proxy用于管理流量,應(yīng)對原有域名場景下變更解析慢或緩存不生效的問題,控制讀寫分離、實現(xiàn)IP、SQL的黑白名單等;

3.1.2?數(shù)據(jù)可靠性強(qiáng)化

數(shù)據(jù)本身還是依賴MySQL原生的主從復(fù)制模式在集群中同步,這樣仍然存在異步復(fù)制本身的風(fēng)險,發(fā)生主庫宕機(jī)時,如果從庫上存在還未接收到的主庫數(shù)據(jù),這部分就會丟失,針對這個場景,我們提供了三種可行的方案:

  • 日志遠(yuǎn)程復(fù)制

配置HA的中心節(jié)點和全網(wǎng)MySQL機(jī)器的登錄機(jī)器后,按照經(jīng)典的MHA日志文件復(fù)制補(bǔ)償方案來保障故障時的數(shù)據(jù)不丟失,操作上即HA節(jié)點會訪問故障節(jié)點的本地文件目錄讀取候選主節(jié)點缺失的Binlog數(shù)據(jù)并在候選主上重放。

優(yōu)勢

  • 與1.0的MHA方案保持一致,可以直接使用舊的機(jī)制

  • 機(jī)制改造后可以混合在高可用的能力內(nèi),不需要機(jī)器間的免密互信,降低權(quán)限需求和安全風(fēng)險

劣勢

  • 不一定可用,需要故障節(jié)點所在機(jī)器可訪達(dá)且硬盤正常,無法應(yīng)對硬件或網(wǎng)絡(luò)異常的情況

  • 網(wǎng)絡(luò)上鏈路較長,可能無法控制中間重放日志的耗時,導(dǎo)致服務(wù)較長時間不可用

  • 日志集中存儲

依賴數(shù)據(jù)傳輸服務(wù)中的BinlogServer模塊,提供Binlog日志的集中存儲能力,HA組件同時管理MySQL集群以及BinlogServer,強(qiáng)化MySQL架構(gòu)的健壯性,真實從庫的復(fù)制關(guān)系全部建立在BinlogServer上,不直接連接主庫。

優(yōu)勢

  • 可以自定義日志的存儲形式:文件系統(tǒng)或其它共享存儲模式

  • 不涉及機(jī)器可用和權(quán)限的問題

  • 間接提高binlog的保存安全性(備份)

劣勢

  • 額外的資源使用,如果需要保留較長時間的日志,資源使用量較大

  • 如果不開啟半同步,也不能保證所有的binlog日志都能被采集到,即使采集(相當(dāng)于IO線程)速度遠(yuǎn)超relay速度,極限約110MB/s

  • 系統(tǒng)復(fù)雜度提升,需要承受引入額外鏈路的風(fēng)險


  • 改變?yōu)榘胪綇?fù)制

MySQL集群開啟半同步復(fù)制,通過配置防止退化(風(fēng)險較大),Agent本身支持半同步集群的相關(guān)監(jiān)控,可以減少故障切換時日志丟失的量(相比異步復(fù)制)

優(yōu)勢

  • MySQL原生的機(jī)制,不需要引入額外的風(fēng)險

  • 本質(zhì)上就是在強(qiáng)化高可用的能力(MySQL集群本身)

  • HA組件可以無縫接入開啟半同步的集群,不需要任何改造

劣勢

  • 存在不兼容的版本,不一定可以開啟

  • 業(yè)務(wù)可能無法接受性能下降的后果

  • 半同步不能保證完全不丟數(shù)據(jù),Agent本身機(jī)制實際上是優(yōu)先選擇“執(zhí)行最多”的從節(jié)點而不是“日志最多”的從節(jié)點

orchestrator will promote the replica which has executed more events rather than the replica which has more data in the relay logs.

目前來說,我們采用的是日志遠(yuǎn)程復(fù)制的方案,同時今年在規(guī)劃集中存儲的BinlogServer方案來強(qiáng)化數(shù)據(jù)安全性;不過值得一提的是,半同步也是一種有效可行的方式,對于讀多寫少的業(yè)務(wù)實際上是可以考慮升級集群的能力,這樣本質(zhì)上也可以保證分離讀流量的準(zhǔn)確性。

3.2?數(shù)據(jù)傳輸服務(wù)

3.2.1 基于Binlog的跨系統(tǒng)數(shù)據(jù)流轉(zhuǎn)

通過利用Binlog,實時地將MySQL的數(shù)據(jù)流轉(zhuǎn)到其它系統(tǒng),包括MySQL,ElasticSearch,Kafka等MQ已經(jīng)是一種非常經(jīng)典的應(yīng)用場景了,MySQL原生提供的這種變化數(shù)據(jù)同步的能力使其可以有效地在各個系統(tǒng)間實時聯(lián)動,DTS(數(shù)據(jù)傳輸服務(wù))針對MySQL的采集也是基于和前文介紹的復(fù)制原理一致的方法,這里介紹我們是如何利用和MySQL 從節(jié)點相同的機(jī)制去獲取數(shù)據(jù)的,也是對于完整開啟復(fù)制的拓展介紹:

(1)如何獲取Binlog

比較常規(guī)的方式有兩種:

  • 監(jiān)聽Binlog文件,類似日志采集系統(tǒng)的操作

  • MySQL Slave的機(jī)制,采集者偽裝成Slave來實現(xiàn)

本文只介紹第二種,F(xiàn)ake Slave的實現(xiàn)方式

(2)注冊Slave身份

這里以GO SDK為例,GO的byte范圍是0~255,其它語言做對應(yīng)轉(zhuǎn)換即可。

  1. 第0-3位為0,無意義

  2. 第4位是MySQL協(xié)議中的Command_Register_Slave,byte值為21

  3. 第5-8位是當(dāng)前實例預(yù)設(shè)的server_id(非uuid,是一個數(shù)值)使用小端編碼成的4個字節(jié)

  4. 接下來的若干位是把當(dāng)前實例的hostname,user,password

  5. 接下來的2位是小端編碼的port端口值

  6. 最后8位一般都置為0,其中最后4位指master_id,偽裝slave設(shè)置為0即可

(3)發(fā)起復(fù)制指令


  1. 第0-3位同樣置為0,無特殊意義

  2. 第4位是MySQL協(xié)議的Command_Binlog_Dump,byte值為18

  3. 第5-8位是Binlog Position值的小端序編碼產(chǎn)生的4位字節(jié)

  4. 第9-10位是MySQL Dump的類別,默認(rèn)是0,指Binlog_Dump_Never_Stop,即編碼成2個0值

  5. 第11-14位是實例的server_id(非uuid)基于小端編碼的四個字節(jié)值

  6. 最后若干位即直接追加Binlog File名稱

以上兩個命令通過客戶端連接執(zhí)行后,就可以在主庫上觀察到一個有效的復(fù)制連接。

3.2.2 利用并行復(fù)制模式提升性能

以上兩個命令通過客戶端連接執(zhí)行后,就可以在主庫上觀察到一個有效的復(fù)制連接。

根據(jù)早期的性能測試結(jié)果,不做任何優(yōu)化,直接單連接重放源集群數(shù)據(jù),在網(wǎng)絡(luò)上的平均傳輸速度在7.3MB/s左右,即使是和MySQL的SQL Relay速度相比也是相差很遠(yuǎn),在高壓場景下很難滿足需求。

DTS消費(fèi)單元實現(xiàn)了對消費(fèi)自kafka的事件的事務(wù)重組以及并發(fā)的事務(wù)解析工作,但實際最終執(zhí)行還是串行單線程地向MySQL回放,這一過程使得性能瓶頸完全集中在了串行執(zhí)行這一步驟。

  1. MySQL 5.7版本以前,會利用事務(wù)的Schema屬性,使不同db下的DML操作可以在備庫并發(fā)回放。在優(yōu)化后,可以做到不同表table下并發(fā)。但是如果業(yè)務(wù)在Master端高并發(fā)寫入一個庫(或者優(yōu)化后的表),那么slave端就會出現(xiàn)較大的延遲?;趕chema的并行復(fù)制,Slave作為只讀實例提供讀取功能時候可以保證同schema下事務(wù)的因果序(Causal Consistency,本文討論Consistency的時候均假設(shè)Slave端為只讀),而無法保證不同schema間的。例如當(dāng)業(yè)務(wù)關(guān)注事務(wù)執(zhí)行先后順序時候,在Master端db1寫入T1,收到T1返回后,才在db2執(zhí)行T2。但在Slave端可能先讀取到T2的數(shù)據(jù),才讀取到T1的數(shù)據(jù)。

  2. MySQL 5.7的LOGICAL CLOCK并行復(fù)制,解除了schema的限制,使得在主庫對一個db或一張表并發(fā)執(zhí)行的事務(wù)到slave端也可以并行執(zhí)行。Logical Clock并行復(fù)制的實現(xiàn),最初是Commit-Parent-Based方式,同一個commit parent的事務(wù)可以并發(fā)執(zhí)行。但這種方式會存在可以保證沒有沖突的事務(wù)不可以并發(fā),事務(wù)一定要等到前一個commit parent group的事務(wù)全部回放完才能執(zhí)行。后面優(yōu)化為Lock-Based方式,做到只要事務(wù)和當(dāng)前執(zhí)行事務(wù)的Lock Interval都存在重疊,即保證了Master端沒有鎖沖突,就可以在Slave端并發(fā)執(zhí)行。LOGICAL CLOCK可以保證非并發(fā)執(zhí)行事務(wù),即當(dāng)一個事務(wù)T1執(zhí)行完后另一個事務(wù)T2再開始執(zhí)行場景下的Causal Consistency。

(1)連接池改造

舊版的DTS的每一個消費(fèi)任務(wù)只有一條維持的MySQL長連接,該消費(fèi)鏈路的所有的事務(wù)都在這條長連接上串行執(zhí)行,產(chǎn)生了極大的性能瓶頸,那么考慮到并發(fā)執(zhí)行事務(wù)的需求,不可能對連接進(jìn)行并發(fā)復(fù)用,所以需要改造原本的單連接對象,提升到近似連接池的機(jī)制。

go-mysql/client包本身不包含連接池模式,這里基于事務(wù)并發(fā)解析的并發(fā)度在啟動時,擴(kuò)展存活連接的數(shù)量。

(2)并發(fā)選擇連接

  • 利用邏輯時鐘

開啟GTID復(fù)制的模式下,binlog中的GTID_EVENT的正文內(nèi)會包含兩個值:

lastCommitted是我們并發(fā)的依據(jù),原則上,LastCommitted相等事務(wù)可以并發(fā)執(zhí)行,結(jié)合原本事務(wù)并發(fā)解析完成后會產(chǎn)生并發(fā)度(配置值)數(shù)量的事務(wù)集合,那么對這個列表進(jìn)行分析判斷,進(jìn)行事務(wù)到連接池的分配,實現(xiàn)一種近似負(fù)載均衡的機(jī)制。

  • 非并發(fā)項互斥

對于并發(fā)執(zhí)行的場景,可以比較簡單地使用類似負(fù)載均衡的機(jī)制,從連接池中遍歷mysql connection執(zhí)行對應(yīng)的事務(wù);但需要注意到的是,源的事務(wù)本身是具有順序的,在logical-clock的場景下,存在部分并發(fā)prepare的事務(wù)是可以被并發(fā)執(zhí)行的,但仍然有相當(dāng)一部分的事務(wù)是不可并發(fā)執(zhí)行,它們顯然是分散于整個事務(wù)隊列中,可以認(rèn)為并發(fā)事務(wù)(最少2個)是被不可并發(fā)事務(wù)包圍的:

假定存在一個事務(wù)隊列有6個元素,其中只有t1、t2和t5、t6可以并發(fā)執(zhí)行,那么執(zhí)行t3時,需要t1、t2已經(jīng)執(zhí)行完畢,執(zhí)行t5時需要t3,t4都執(zhí)行完畢。


(3)校驗點更新

在并發(fā)的事務(wù)執(zhí)行場景下,存在水位低的事務(wù)后執(zhí)行完,而水位高的事務(wù)先執(zhí)行完,那么依照原本的機(jī)制,更低的水位會覆蓋掉更高的水位,存在一定的風(fēng)險:

  1. Write_Event的構(gòu)造SQL調(diào)整為replace into,可以回避沖突重復(fù)的寫事件;Update和Delete可以基于邏輯時鐘的并發(fā)保障,不會出現(xiàn)。

  2. 水位只會向上提升,不會向下降低。

但不論怎樣進(jìn)行優(yōu)化,并發(fā)執(zhí)行事務(wù)必然會引入更多的風(fēng)險,例如并發(fā)事務(wù)的回滾無法控制,目標(biāo)實例和源實例的因果一致性被破壞等,業(yè)務(wù)可以根據(jù)自身的需要進(jìn)行權(quán)衡,是否開啟并發(fā)的執(zhí)行。

基于邏輯時鐘并發(fā)執(zhí)行事務(wù)改造后,消費(fèi)端的執(zhí)行性能在同等的測試場景下,可以從7.3MB/s提升到13.4MB/s左右。

(4)小結(jié)

基于消費(fèi)任務(wù)本身的庫、表過濾,可以實現(xiàn)另一種形式下的并發(fā)執(zhí)行,可以啟動復(fù)數(shù)的消費(fèi)任務(wù)分別支持不同的庫、表,這也是利用了kafka的多消費(fèi)者組支持,可以橫向擴(kuò)展以提高并發(fā)性能,適用于數(shù)據(jù)遷移場景,這一部分可以專門提供支持。

而基于邏輯時鐘的方式,對于目前現(xiàn)網(wǎng)大規(guī)模存在的未開啟GTID的集群是無效的,所以這一部分我們也一直在尋找更優(yōu)的解決方案,例如更高版本的特性Write Set的合并等,繼續(xù)做性能優(yōu)化。

四、總結(jié)

最后,關(guān)于MySQL的復(fù)制能力不僅對于MySQL數(shù)據(jù)庫服務(wù)本身的可用性、可靠性有巨大的提升,也提供了Binlog這一非常靈活的開放式的數(shù)據(jù)接口用于擴(kuò)展數(shù)據(jù)的應(yīng)用范圍,通過利用這個“接口”,很容易就可以達(dá)成數(shù)據(jù)在多個不同存儲結(jié)構(gòu)、環(huán)境的實時同步,未來存儲組也將會聚焦于BinlogServer這一擴(kuò)展服務(wù)來強(qiáng)化MySQL的架構(gòu),包括但不限于數(shù)據(jù)安全性保障以及對下游數(shù)據(jù)鏈路的開放等。

參考資料:

  • MySQL官方文檔:https://dev.mysql.com/doc/internals/en/binlog-version.html

  • 數(shù)據(jù)庫內(nèi)核月報:http://mysql.taobao.org/monthly/2017/12/03/


MySQL主從復(fù)制原理剖析與應(yīng)用實踐的評論 (共 條)

分享到微博請遵守國家法律
新乡市| 通山县| 博客| 平谷区| 吉木萨尔县| 陕西省| 肥城市| 南华县| 新昌县| 岳阳市| 定陶县| 玛沁县| 农安县| 从江县| 新巴尔虎左旗| 剑阁县| 延安市| 常州市| 界首市| 四会市| 嫩江县| 乐安县| 垫江县| 宁乡县| 天津市| 韶关市| 凯里市| 托克托县| 新安县| 手游| 扶风县| 龙游县| 金川县| 饶阳县| 马山县| 东至县| 秦安县| 宝应县| 商丘市| 秦皇岛市| 虞城县|