日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一、背景

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

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

  • 實時數據變化備份
    主庫的寫入數據會持續地在冗余的從庫節點上被執行保留,減少數據丟失的風險
  • 橫向拓展節點,支撐讀寫分離
    當主庫本身承受壓力較大時,可以將讀流量分散到其它的從庫節點上,達成讀擴展性和負載均衡
  • 高可用性保障
    當主庫發生故障時,可以快速的切到其某一個從庫,并將該從庫提升為主庫,因為數據都一樣,所以不會影響系統的運行

具備包括但不限于以上特性的MySQL集群就可以覆蓋絕大多數應用和故障場景,具備較高的可用性與數據可靠性,當前存儲組提供的生產環境MySQL就是基于默認的異步主從復制的集群,向業務保證可用性99.99%,數據可靠性99.9999%的在線數據庫服務。

本文將深入探討MySQL的復制機制實現的方式, 同時討論如何具體地應用復制的能力來提升數據庫的可用性,可靠性等。

二、復制的原理

2.1 Binlog 的引入

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

  • Statement:基于語句格式
    Statement模式下,復制過程中向獲取數據的從庫發送的就是在主庫上執行的SQL原句,主庫會將執行的SQL原有發送到從庫中。
  • Row:基于行格式
    Row模式下,主庫會將每次DML操作引發的數據具體行變化記錄在Binlog中并復制到從庫上,從庫根據行的變更記錄來對應地修改數據,但DDL類型的操作依然是以Statement的格式記錄。
  • Mixed:基于混合語句和行格式
    MySQL 會根據執行的每一條具體的 SQL 語句來區分對待記錄的日志形式,也就是在 statement 和 row 之間選擇一種。

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

這三種模式各有優劣,相對來說,基于Row的行格式被應用的更廣泛,雖然這種模式下對資源的開銷會偏大,但數據變化的準確性以及可靠性是要強于Statement格式的,同時這種模式下的Binlog提供了完整的數據變更信息,可以使其應用不被局限在MySQL集群系統內,可以被例如Binlogserver,DTS數據傳輸等服務應用,提供靈活的跨系統數據傳輸能力, 目前互聯網業務的在線MySQL集群全部都是基于Row行格式的Binlog

2.2  Binlog 的要點

2.2.1 Binlog事件類型

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

 

圖片

 

各類Event出現是具有顯著的規律的:

  • XID_EVENT標志一個事務的結尾
  • 當發生了DDL類型的QUERY_EVENT,那么也是一次事務的結束提交點,且不會出現XID_EVENT
  • GTID_EVENT只有開啟了GTID_MODE(MySQL版本大于5.6)
  • TABLE_MAP_EVENT必定出現在某個表的變更數據前,存在一對多個ROW_EVENT的情況

除了上面和數據更貼近的事件類型外,還有ROTATE_EVENT(標識Binlog文件發生了切分),FORMAT_DESCRIPTION_EVENT(定義元數據格式)等。

2.2.2 Binlog的生命周期

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

所以這種機制背景下,只能在短期內追溯歷史數據的狀態,而不可能完整追溯數據庫的數據變化的,除非是還沒有發生過日志過期回收的Server。 

2.2.3 Binlog事件示例

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

 

圖片

 

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

在復制的過程中,就是這樣的Binlog數據通過建立的連接發送到從庫,等待從庫處理并應用。

2.2.4 復制基準值

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

  • FILE Position

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

File: binlog.000001
Position: 381808617

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

基于這種模式開啟復制,需要顯式地在復制關系中指定對應的File和Position:

CHANGE MASTER TO MASTER_LOG_FILE='binlog.000001', MASTER_LOG_POSITION=381808617;

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

  • GTID

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

Executed_Gtid_Set: e2e0a733-3478-11eb-90fe-b4055d009f6c:1-753

其中e2e0a733-3478-11eb-90fe-b4055d009f6c用于唯一地標識產生該Binlog事件的實例,1-753表示已經產生或接收了由e2e0a733-3478-11eb-90fe-b4055d009f6c實例產生的753個事務;

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

如果說從庫上可以看到和復制的主庫不一致的值,那么可以認為是存在errant GTID,這個一般是由于主從切換或強制在從庫上執行了寫操作引發,正常情況下從庫的Binlog GTID應該和主庫的保持一致;

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

CHANGE MASTER TO MASTER_AUTO_POSITION=1;

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

2.3 復制的具體流程

2.3.1 基本復制流程

當主庫已經開啟了binlog( log_bin = ON ),并正常地記錄binlog,如何開啟復制?

這里以MySQL默認的異步復制模式進行介紹:

  1. 首先從庫啟動I/O線程,跟主庫建立客戶端連接。
  2. 主庫啟動binlog dump線程,讀取主庫上的binlog event發送給從庫的I/O線程,I/O線程獲取到binlog event之后將其寫入到自己的Relay Log中。
  3. 從庫啟動SQL線程,將等待Relay中的數據進行重放,完成從庫的數據更新。

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

 

圖片

 

  • 時序關系

當集群進入運行的狀態時,從庫會持續地從主庫接收到Binlog事件,并做對應的處理,那么這個過程中將會按照下述的數據流轉方式:

  1. Master將數據更改記錄在Binlog中,BinlogDump Thread接到寫入請求后,讀取對應的Binlog
  2. Binlog信息推送給Slave的I/O Thread。
  3. Slave的I/O 線程將讀取到的Binlog信息寫入到本地Relay Log中。
  4. Slave的SQL 線程讀取Relay Log中內容在從庫上執行。

 

圖片

 

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

  • Relay Log的意義

Relay log在本質上可以認為和binlog是等同的日志文件,即使是直接在本地打開兩者也只能發現很少的差異;

Binlog Version 3 (MySQL 4.0.2 - < 5.0.0)

added the relay logs and changed the meaning of the log position

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

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

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

2.3.2 半同步復制

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

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

介紹半同步復制之前先快速過一下 MySQL 事務寫入碰到主從復制時的完整過程,主庫事務寫入分為 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
  • 當Master不需要關注Slave是否接受到Binlog Event時,即為異步主從復制
  • 當Master需要在第3步Commit Write回復客戶端前等待Slave的ACK時,為半同步復制(after-commit)
  • 當Master需要在第2步Flush&Sync,即Commit前等待Slave的ACK時,為增強半同步復制(after-sync)
  • 時序關系

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

圖片

2.3.3 小結

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

2.4 復制的特性

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

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

  • 機器:64核 256G,MySQL 5.7.29
  • 測試場景:常規的INSERT,UPDATE壓測場景
  • 結果:MySQL Server的IO線程速度以網絡上的數據量評估,每秒超過100MB,正常是可以覆蓋業務使用的,然而SQL線程的預估速度只有21~23MB/s,如果是涉及UPDATE場景,性能還會減少;
  • 需要注意的是,以上結果是在高版本的MySQL具備并行復制能力的前提下取得,如果是不具備該特性的版本,性能會更差。

期望業務層限制使用是不現實的,MySQL則在5.6版本開始嘗試引入可用的并行復制方案,總的來說,都是通過嘗試加強在從庫層面的應用速度的方式。

2.4.1 基于Schema級別的并行復制

基于庫級別的并行復制是出于一個非常簡易的原則,實例中不同Database/Schema內的數據以及數據變更是無關的,可以并行去處置。

在這種模式中,MySQL的從節點會啟動多個WorkThread ,而原來負責回放的SQLThread會轉變成Coordinator角色,負責判斷事務能否并行執行并分發給WorkThread。

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

MySQL Server
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| aksay_record       |
| mysql              |
| performance_schema |
| proxy_encrypt      |
| sys                |
| test               |
+--------------------+
7 rows in set (0.06 sec)

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

但是這種方式也存在明顯缺陷和不足,首先只有多個Schema流量均衡的情況下才會有較大的性能改善,但如果存在熱點表或實例上只有一個Schema有數據變更,那么這種并行模式和早期的串行復制也不存在差異;同樣,雖然不同Schema的數據是沒有關聯,這樣并行執行也會影響事務的執行順序,某種程度來說,整個Server的因果一致性被破壞了。

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

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

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

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

在主庫具有這一能力的背景下,可以很容易得發現從庫也可以應用相似的機制來并行地去執行事務,下面介紹MySQL具體實現經歷的兩個階段:

  • 基于Commit-Parents-Based
    MySQL中寫入是基于鎖的并發控制,所以所有在Master端同時處于Prepare階段且未提交的事務就不會存在鎖沖突,在Slave端執行時都可以并行執行。因此可以在所有的事務進入prepare階段的時候標記上一個logical timestamp(實現中使用上一個提交事務的sequence_number),在Slave端同樣timestamp的事務就可以并發執行。但這種模式會依賴上一個事務組的提交,如果本身是不受資源限制的并發事務,卻會因為它的commit-parent沒有提交而無法執行;
  • 基于Logic-Based
    針對Commit-Parent-Based中存在的限制進行了解除,純粹的理解就是只有當前事務的sequence_number一致就可以并發執行,只根據是否能取得鎖且無沖突的情況即可以并發執行,而不是依賴上一個已提交事務的sequence_number。

三、應用

當前vivo的在線MySQL數據庫服務標準架構是基于一主一從一離線的異步復制集群,其中一從用于業務讀請求分離,離線節點不提供讀服務,提供給大數據離線和實時抽數/DB平臺查詢以及備份系統使用;針對這樣的應用背景,存儲研發組針對MySQL場景提供了兩種額外的擴展服務:

3.1 應用高可用系統+中間件

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

3.1.1 VSQL(原高可用2.0架構)

那么在當前這樣標準一主二從架構的基礎上,為系統增加HA高可用組件以及中間件組件強化MySQL服務的高可用性、讀拓展性、數據可靠性:

  • HA組件管理MySQL的復制拓撲,負責監控集群的健康狀態,管理故障場景下的自動故障轉移;
  • 中間件Proxy用于管理流量,應對原有域名場景下變更解析慢或緩存不生效的問題,控制讀寫分離、實現IP、SQL的黑白名單等;

圖片

3.1.2 數據可靠性強化

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

  • 日志遠程復制

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

優勢

  • 與1.0的MHA方案保持一致,可以直接使用舊的機制
  • 機制改造后可以混合在高可用的能力內,不需要機器間的免密互信,降低權限需求和安全風險

劣勢

  • 不一定可用,需要故障節點所在機器可訪達且硬盤正常,無法應對硬件或網絡異常的情況
  • 網絡上鏈路較長,可能無法控制中間重放日志的耗時,導致服務較長時間不可用

 

圖片

 

  • 日志集中存儲

依賴數據傳輸服務中的BinlogServer模塊,提供Binlog日志的集中存儲能力,HA組件同時管理MySQL集群以及BinlogServer,強化MySQL架構的健壯性,真實從庫的復制關系全部建立在BinlogServer上,不直接連接主庫。

優勢

  • 可以自定義日志的存儲形式:文件系統或其它共享存儲模式
  • 不涉及機器可用和權限的問題
  • 間接提高binlog的保存安全性(備份)

劣勢

  • 額外的資源使用,如果需要保留較長時間的日志,資源使用量較大
  • 如果不開啟半同步,也不能保證所有的binlog日志都能被采集到,即使采集(相當于IO線程)速度遠超relay速度,極限約110MB/s
  • 系統復雜度提升,需要承受引入額外鏈路的風險
  • 改變為半同步復制

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

優勢

  • MySQL原生的機制,不需要引入額外的風險
  • 本質上就是在強化高可用的能力(MySQL集群本身)
  • HA組件可以無縫接入開啟半同步的集群,不需要任何改造

劣勢

  • 存在不兼容的版本,不一定可以開啟
  • 業務可能無法接受性能下降的后果
  • 半同步不能保證完全不丟數據,Agent本身機制實際上是優先選擇“執行最多”的從節點而不是“日志最多”的從節點

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

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

3.2 數據傳輸服務

3.2.1 基于Binlog的跨系統數據流轉

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

(1)如何獲取Binlog

比較常規的方式有兩種:

  • 監聽Binlog文件,類似日志采集系統的操作
  • MySQL Slave的機制,采集者偽裝成Slave來實現

本文只介紹第二種,Fake Slave的實現方式

 

圖片

(2)注冊Slave身份

這里以GO SDK為例,GO的byte范圍是0~255,其它語言做對應轉換即可。

data := make([]byte, 4+1+4+1+len(hostname)+1+len(b.cfg.User)+1+len(b.cfg.Password)+2+4+4)
  1. 第0-3位為0,無意義
  2. 第4位是MySQL協議中的Command_Register_Slave,byte值為21
  3. 第5-8位是當前實例預設的server_id(非uuid,是一個數值)使用小端編碼成的4個字節
  4. 接下來的若干位是把當前實例的hostname,user,password
  5. 接下來的2位是小端編碼的port端口值
  6. 最后8位一般都置為0,其中最后4位指master_id,偽裝slave設置為0即可

(3)發起復制指令

data := make([]byte, 4+1+4+2+4+len(p.Name))

 

 

圖片

 

  1. 第0-3位同樣置為0,無特殊意義
  2. 第4位是MySQL協議的Command_Binlog_Dump,byte值為18
  3. 第5-8位是Binlog Position值的小端序編碼產生的4位字節
  4. 第9-10位是MySQL Dump的類別,默認是0,指Binlog_Dump_Never_Stop,即編碼成2個0值
  5. 第11-14位是實例的server_id(非uuid)基于小端編碼的四個字節值
  6. 最后若干位即直接追加Binlog File名稱

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

 

圖片

 

3.2.2 利用并行復制模式提升性能

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

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

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

  1. MySQL 5.7版本以前,會利用事務的Schema屬性,使不同db下的DML操作可以在備庫并發回放。在優化后,可以做到不同表table下并發。但是如果業務在Master端高并發寫入一個庫(或者優化后的表),那么slave端就會出現較大的延遲。基于schema的并行復制,Slave作為只讀實例提供讀取功能時候可以保證同schema下事務的因果序(Causal Consistency,本文討論Consistency的時候均假設Slave端為只讀),而無法保證不同schema間的。例如當業務關注事務執行先后順序時候,在Master端db1寫入T1,收到T1返回后,才在db2執行T2。但在Slave端可能先讀取到T2的數據,才讀取到T1的數據。
  2. MySQL 5.7的LOGICAL CLOCK并行復制,解除了schema的限制,使得在主庫對一個db或一張表并發執行的事務到slave端也可以并行執行。Logical Clock并行復制的實現,最初是Commit-Parent-Based方式,同一個commit parent的事務可以并發執行。但這種方式會存在可以保證沒有沖突的事務不可以并發,事務一定要等到前一個commit parent group的事務全部回放完才能執行。后面優化為Lock-Based方式,做到只要事務和當前執行事務的Lock Interval都存在重疊,即保證了Master端沒有鎖沖突,就可以在Slave端并發執行。LOGICAL CLOCK可以保證非并發執行事務,即當一個事務T1執行完后另一個事務T2再開始執行場景下的Causal Consistency。

(1)連接池改造

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

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

// 初始化客戶端連接數
se.conn = make([]*Connection, meta.MaxConcurrenceTransaction)

(2)并發選擇連接

  • 利用邏輯時鐘

開啟GTID復制的模式下,binlog中的GTID_EVENT的正文內會包含兩個值:

LastCommitted  int64
SequenceNumber int64

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

  • 非并發項互斥

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

假定存在一個事務隊列有6個元素,其中只有t1、t2和t5、t6可以并發執行,那么執行t3時,需要t1、t2已經執行完畢,執行t5時需要t3,t4都執行完畢。

 

圖片

 

(3)校驗點更新

在并發的事務執行場景下,存在水位低的事務后執行完,而水位高的事務先執行完,那么依照原本的機制,更低的水位會覆蓋掉更高的水位,存在一定的風險:

  1. Write_Event的構造SQL調整為replace into,可以回避沖突重復的寫事件;Update和Delete可以基于邏輯時鐘的并發保障,不會出現。
  2. 水位只會向上提升,不會向下降低。

但不論怎樣進行優化,并發執行事務必然會引入更多的風險,例如并發事務的回滾無法控制,目標實例和源實例的因果一致性被破壞等,業務可以根據自身的需要進行權衡,是否開啟并發的執行。

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

(4)小結

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

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

四、總結

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

參考資料:

  • ??MySQL官方文檔??
  • ??數據庫內核月報?

分享到:
標簽:MySQL
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定