
1 單機 =》集群
隨着數據量的增大,讀寫並發的增加,系統可用性要求的提升,單機 MySQL 出現危機:
-
容量問題,難以擴容,考慮數據庫拆分、分庫分表
-
讀寫壓力,QPS 過大,特別是分析類需求會影響到業務事務,考慮多機集群、主從復制
-
高可用性不足,易宕機,考慮故障轉移、MHA/MGR/Orchestrator
-
高峰時數據庫連接數經常超過上限
一致性問題,考慮分布式事務,X/A 柔性事務
讀寫分離的實現是基於主從復制架構:一主多從,只寫主庫,主庫會自動將數據同步到從庫。
為什么要讀寫分離?
高並發場景下MySQL的一種優化方案,依靠主從復制使得MySQL實現了數據復制為多份,增強了抵抗 高並發讀請求的能力,提升了MySQL查詢性能同時,也提升了數據的安全性。當某一個MySQL節點,無論是主庫還是從庫故障時,還有其他的節點中存儲着全量數據,保證數據不會丟失。
主庫將變更寫binlog日志,然后從庫連接到主庫后,從庫有個I/O線程,將主庫的binlog日志拷貝到本地,寫入一個中繼日志。接着從庫中有一個SQL線程會從中繼日志讀取binlog,然后執行binlog日志中的內容。即在本地再次執行一遍SQL,確保跟主庫的數據相同。
2 MySQL主從復制
2.1 發展史
2000年,MySQL 3.23.15版本引入了復制 2002年,MySQL 4.0.2版本分離 IO 和 SQL 線程,引入了 relay log 2010年,MySQL 5.5版本引入半同步復制 2016年,MySQL 在5.7.17中引入 InnoDB Group Replication
2.2 核心
-
主庫寫 binlog
-
從庫 relay log
binlog格式
-
ROW 記錄詳細但日志量會比較大
-
Statement 只是記錄SQL,記錄簡單 沒有查詢語句
-
Mixed
# 查看binlog
mysqlbinlog -vv mysql-bin.000005
異步復制
異步復制:經典的主從復制,Primary-Secondary Replication,2000年MySQL 3.23.15版本引入 Replication。
傳統的MySQL復制提供了一種簡單的主從復制方案。有一個主(source)並且有一或多個從(replicas)。主數據庫execute事務,將其commit,然后將它們稍后(異步)發送給從數據庫,以re-executed(在基於語句的復制中)或apply(在基於行的復制中)。它是一個無共享系統,默認情況下所有服務器都具有數據的完整副本。
-
MySQL Asynchronous Replication
優點
簡單
缺點
-
網絡或機器故障,會造成數據不一致
SQL的每個增刪改的會改變數據的操作,除了更新數據外,對這個增刪改操作還會寫入一個日志文件,記錄這個操作的日志,即binlog。
mysql 5.7新版本的並行復制,多個SQL線程,每個線程從relay日志里讀一個庫的 日志,重放。從庫同步主庫數據的過程是串行化的,即主庫上並行的操作,在從庫上會串行執行。由於從庫從主庫拷貝日志以及串行執行SQL的特點,在高並發下就有延時,從庫數據一定比主庫慢,所以經常出現,剛寫入主庫的數據讀不到,要過幾十甚至幾百ms才能讀到。從庫串行化過程:
-
讀取binlog日志
-
寫relay日志、應用日志變更到自己本地數據
從庫的I/O線程,讀取主庫的binlog日志時,老版本是單線程,5.6.x之后的新版本改為多線程讀取。
若主庫宕機時,恰好數據還沒同步到從庫,則有些數據可能在從庫上沒有,可能就這么丟失了。
所以MySQL實際上在這有兩個機制
半同步復制,它向協議添加了一個同步步驟。這意味着主數據庫在提交時等待從數據庫確認已接收到事務。只有這樣,主數據庫才會恢復提交操作。
半同步復制
2010 年引入Semisynchronous Replication,5.5 可用,解決主庫數據丟失問題,保證 Source 和 Replica 的最終一致性。需要啟用插件。


-
主庫寫入binlog日志后,會強制立即將數據同步到從庫
-
從庫將日志寫入自己的relay log后,會返回ack給主庫
-
主庫接收到至少一個從庫的ack后才會認為寫操作完成
上面的圖片可看到經典的異步MySQL復制協議(及其半同步變量)的示意圖。不同實例之間的箭頭表示服務器之間交換的消息或服務器與客戶端應用程序之間交換的消息。
組復制
2016年引入,5.7 開始,啟用插件。

基於 Paxos 協議實現的組復制,保證數據一致性。
組復制是一種可用於實施容錯系統的技術。復制組是一組服務器,每個服務器都有自己的完整數據副本(無共享復制方案),並通過消息傳遞相互交互。通信層提供了一組保證,例如原子消息和總訂單消息傳遞。這些功能非常強大,可以轉化為非常有用的抽象,可以用來構建更高級的數據庫復制解決方案。
MySQL組復制建立在這些屬性和抽象之上,並在所有復制協議中實現多源更新。一個復制組由多個服務器組成,該組中的每個服務器可以隨時獨立執行事務。但是,所有讀寫事務只有在組批准后才提交。換句話說,對於任何讀寫事務,組都需要決定是否提交,因此提交操作不是來自原始服務器的單方面決定。只讀事務無需組內的任何協調即可立即提交。
當讀寫事務准備好在原始服務器上提交時,服務器自動廣播寫值(已更改的行)和相應的寫集(已更新的行的唯一標識符)。由於事務是通過原子廣播發送的,因此該組中的所有服務器都將接收該事務,否則將不會。如果他們收到了,那么相對於之前發送的其他事務,他們都將以相同的順序收到它。因此,所有服務器都以相同的順序接收相同的交易集,並且為交易建立了全局總訂單。
但是,在不同服務器上同時執行的事務之間可能存在沖突。通過在稱為認證的過程中檢查並比較兩個不同並發事務的寫集,可以檢測到此類沖突。在認證過程中,沖突檢測是在行級別執行的:如果在不同服務器上執行的兩個並發事務更新同一行,則存在沖突。沖突解決過程指出,已首先訂購的事務在所有服務器上提交,而已訂購第二的事務中止,因此在原始服務器上回滾並由組中的其他服務器丟棄。例如,如果t1和t2在不同的站點上同時執行,都更改了同一行,並且t2在t1之前排序,則t2贏得了沖突,並且t1被回滾。這實際上是一個分布式的首次提交勝出規則。請注意,如果兩個事務之間的沖突經常發生,那么在同一個服務器上啟動它們是一個好習慣,在那里,它們有機會在本地鎖管理器上進行同步,而不必由於認證而回滾。
對於應用和外部化已認證的交易,如果不破壞一致性和有效性,組復制允許服務器偏離交易的約定順序。組復制是最終的一致性系統,這意味着一旦傳入流量減慢或停止,所有組成員將具有相同的數據內容。當流量在流動時,可以按略有不同的順序對事務進行外部化,或者對某些成員先進行外部化。例如,在多主要模式下,盡管尚未應用全局順序中較早的遠程事務,但是本地事務可能會在認證后立即被外部化。當證明過程確定交易之間沒有沖突時,這是允許的。在單主模式下,在主服務器上,並發,無沖突的本地事務以與組復制所同意的全局順序不同的順序進行提交和外部化的可能性很小。在不接受來自客戶端的寫操作的輔助服務器上,事務始終按照約定的順序進行提交和外部化。
下圖描述了MySQL組復制協議,通過將其與MySQL復制(甚至MySQL半同步復制)進行比較,您可以看到一些區別。請注意,為清楚起見,此圖中缺少一些基本的共識和Paxos相關的消息。

3 主從復制的缺點及解決方案
3.1 主從延遲
-
只能數據分片,把數據量做小
主從同步適用場景
推薦在讀 >> 寫,且讀時對數據時效性要求不高時采用。所以可以考慮用MySQL的並行復制,但問題是那是庫級別的並行,所以有時作用不是很大。
主從延遲嚴重解決方案
-
分庫 : 將一個主庫拆分,每個主庫的寫並發就降低了,主從延遲即可忽略不計
-
打開MySQL支持的並行復制,多個庫並行復制,若某個庫的寫入並發特別高,寫並發達到了2000/s,並行復制還是沒意義。二八法則,很多時候比如說,就是少數的幾個訂單表,寫入了2000/s,其他幾十個表10/s。從庫開啟多線程,並行讀取relay log中不同庫的日志,然后並行重放不同庫的日志,這是庫級別的並行。
-
重構代碼 : 重構代碼,插入數據后,直接更新,不查詢
-
若確實存在必須先插入,立馬要求查詢,然后立馬就反過來執行一些操作,對這個查詢設置直連主庫(不推薦,這會導致讀寫分離失去意義)
3.2 應用側需要配合讀寫分離框架
讀寫分離
借助於主從復制,我們現在有了多個 MySQL 服務器示例。如果借助這個新的集群,改進我們的業務系統數據處理能力?
最簡單的就是配置多個數據源,實現讀寫分離
動態切換數據源
-
基於 Spring/Spring Boot,配置多個數據源(例如2個,master 和 slave)
-
根據具體的 Service 方法是否會操作數據,注入不同的數據源,1.0版本
優化:1.1:基於操作 AbstractRoutingDataSource 和自定義注解 readOnly 之類的,簡化自動切換數據源 1.2:支持配置多個從庫 1.3:支持多個從庫的負載均衡

框架
“動態切換數據源”版問題:
-
代碼侵入性強
-
降低侵入性會導致”寫后立即讀”不一致問題 寫時(還沒同步到從庫),立馬讀(從庫),導致你 insert 數據后去查卻查不到!
改進方式,ShardingSphere-jdbc 的 Master-Slave 功能 1)SQL 解析和事務管理,自動實現讀寫分離 2)解決”寫完讀”不一致的問題 只要一個事務中解析到有寫,所有讀都讀主庫,而無需我們業務代碼處理。
數據庫中間件
“框架版本”的問題?
-
對業務系統還是有侵入
-
對已存在的舊系統改造不友好
優化方案:MyCat/ShardingSphere-Proxy 的 Master-Slave 功能
-
需要部署一個中間件,規則配置在中間件
-
模擬一個 MySQL 服務器,對業務系統無侵入
但是該方案需要單獨部署中間件,需要運維成本和領導審批,所以一般開發人員使用框架方案。
3.3 無法高可用
3.3.1 為什么要高可用
1、讀寫分離,提升讀的處理能力 2、故障轉移,提供 failover 能力
加上業務側連接池的心跳重試,實現斷線重連,業務不間斷,降低RTO和RPO。
高可用意味着,更少的不可服務時間。一般用SLA/SLO衡量。
1年 = 365天 = 8760小時
99 = 8760 * 1% = 8760 * 0.01 = 87.6小時
99.9 = 8760 * 0.1% = 8760 * 0.001 = 8.76小時
99.99 = 8760 * 0.0001 = 0.876小時 = 0.876 * 60 = 52.6分鍾
99.999 = 8760 * 0.00001 = 0.0876小時 = 0.0876 * 60 = 5.26分鍾
3.3.2 failover,故障轉移,災難恢復
容災:熱備與冷備 對於主從來說,就是主掛了,某一個從,變成主,整個集群來看,正常對外提供服務。常見的一些策略:
-
多個實例不在一個主機/機架上
-
跨機房和可用區部署
-
兩地三中心容災高可用方案
3.3.3 高可用方案
3.3.3.1 主從手動切換
如果主節點掛掉,將某個從改成主;重新配置其他從節點。修改應用數據源配置。缺點:
-
可能數據不一致
-
需要人工干預
-
代碼和配置的侵入性
3.3.3.2 主從手動切換
用 LVS+Keepalived 實現多個節點的探活+請求路由。配置 VIP 或 DNS 實現配置不變更。缺點:
-
手工處理主從切換
-
大量的配置和腳本定義
只能算半自動。
3.3.3.2 MHA
MHA,Master High Availability,目前在 MySQL 高可用方面是一個相對成熟的解決方案,它由日本 DeNA 公司的 youshimaton(現就職於 Facebook)開發,二手手機號地圖是一套優秀的作為 MySQL 高可用性環境下故障切換和主從提升的高可用軟件。

基於 Perl 語言開發,一般能在30s內實現主從切換。切換時,直接通過 SSH 復制主節點的日志。
缺點:
-
需要配置 SSH 信息
-
至少3台
3.3.3.2 MGR
不借助外部力量,只使用 MySQL 本身。如果主節點掛掉,將自動選擇某個從改成主;無需人工干預,基於組復制,保證數據一致性。

缺點:
-
外部獲得狀態變更需要讀取數據庫
-
外部需要使用 LVS/VIP 配置
特點:
-
高一致性 基於分布式Paxos協議實現組復制,保證數據一致性
-
高容錯性 自動檢測機制,只要不是大多數節點都宕機就可繼續工作,內置防腦裂保護機制
-
高擴展性 節點的增加與移除會自動更新組成員信息,新節點加入后,自動從其他節點同步增量數據,直到與其他節點數據一致
-
高靈活性 提供單主模式和多主模式,單主模式在主庫宕機后能夠自動選主,所有寫入都在主節點進行,多主模式支持多節點寫入
適用場景:
-
彈性復制 需要非常流暢的復制基礎架構的環境,其中服務器的數量必須動態地增長或縮減,而最少盡可能的痛苦。
-
高可用分片 Sharding is a popular approach to achieve write scale-out. Users can use MySQL Group Replication to implement highly available shards. Each shard can map into a Replication Group. 分片是實現寫橫向擴展的一種流行方法。用戶可以使用MySQL組復制來實現高度可用的分片。每個分片可以映射到副本組。
3.3.3.4 MySQL Cluster
完整的數據庫層高可用解決方案。 MySQL InnoDB Cluster是一個高可用的框架,構成組件:
-
MySQL Group Replication 提供DB的擴展、自動故障轉移
-
MySQL Router 輕量級中間件,提供應用程序連接目標的故障轉移。MySQL Router是一個輕量級的中間件,可以提供負載均衡和應用連接的故障轉移。它是MySQL團隊為MGR量身打造的,通過使用Router和Shell,用戶可以利用MGR實現完整的數據庫層的解決方案。如果您在使用MGR,請一定配合使用Router和Shell,可以理解為它們是為MGR而生的,會配合MySQl 的開發路線圖發展的工具。
-
MySQL Shell 新的MySQL客戶端,多種接口模式。可以設置群組復制及Router。MySQL Shell是MySQL團隊打造的一個統一的客戶端, 它可以對MySQL執行數據操作和管理。它支持通過JavaScript,Python,SQL對關系型數據模式和文檔型數據模式進行操作。使用它可以輕松配置管理InnoDB Cluster。
3.3.3.5 Orchestrator
如果主節點掛掉,將某個從改成主。一款MySQL高可用和復制拓撲管理工具,支持復制拓撲結構的調整,自動故障轉移和手動主從切換等。后端數據庫用MySQL或SQLite存儲元數據,並提供Web界面展示MySQl 復制的拓撲關系及狀態,通過Web可更改MySQL實例的復制關系和部分配置信息,同時也提供命令行和API接口,方便運維管理。
特點:
-
自動發現MySQL的復 制拓撲,並且在web.上展示;
-
重構復制關系, 可以在web進行拖圖來進行復制關系變更;
-
檢測主異常,並可以自動或手動恢復,通過Hooks進行自定義腳本;
-
支持命令行和web界面管理復制。
基於 Go 語言開發,實現了中間件本身的高可用。
兩種部署方式 orchestrator/raft:
-
數據一致性由orchestrator的raft協議保證
-
數據庫之間不通信 orchestrator/[galera | xtradb cluster | innodb cluster]:
-
數據一致性由數據庫集群保證
-
數據庫結點之間通信
如果不部署client
-
使用HTTP (/api/leader-check)查詢並路由到主節點
優勢:能直接在 UI 界面 拖拽改變主從關系

