微信開源PhxSQL:高可用、強(qiáng)一致的MySQL集群
PhxSQL是一個(gè)兼容MySQL、服務(wù)高可用、數(shù)據(jù)強(qiáng)一致的關(guān)系型數(shù)據(jù)庫(kù)集群。PhxSQL以單Master多Slave方式部署,在集群內(nèi)超過一半機(jī)器存活的情況下,可自身實(shí)現(xiàn)自動(dòng)Master切換,且保證數(shù)據(jù)一致性。
PhxSQL基于Percona 5.6開發(fā)。Percona是MySQL的一個(gè)分支,功能和實(shí)現(xiàn)與MySQL基本一致。因此本文后續(xù)直接把MySQL作為討論對(duì)象。
MySQL半同步復(fù)制存在缺陷,在Master進(jìn)行切換的場(chǎng)景下,數(shù)據(jù)難以保證一致。
- 當(dāng)舊Master復(fù)制失敗時(shí),舊Master和Updated Slave(已收到Binlog的Slave)需要回滾數(shù)據(jù)。
- 當(dāng)Master進(jìn)行切換時(shí),舊Master仍有部分Client進(jìn)行讀寫。
關(guān)于MySQL半同步復(fù)制的數(shù)據(jù)一致性問題可查看微信后臺(tái)團(tuán)隊(duì)公眾號(hào)文章MySQL半同步復(fù)制的數(shù)據(jù)一致性探討。
PhxSQL的設(shè)計(jì)是為了解決MySQL半同步復(fù)制的不足,使MySQL集群在Master切換過程中保證數(shù)據(jù)的一致。
PhxSQL架構(gòu)
圖1 PhxSQL 三層架構(gòu)
為了解決MySQL的兩個(gè)問題(Binlog復(fù)制和Master切換),PhxSQL設(shè)計(jì)了兩個(gè)模塊(Phxbinlogsvr、Phxsqlproxy)和一個(gè)MySQL插件(Phxsync)。Phxbinlogsvr負(fù)責(zé)處理MySQL的Binlog復(fù)制和Master管理;Phxsqlproxy負(fù)責(zé)透?jìng)鰿lient請(qǐng)求到Master;Phxsync插件負(fù)責(zé)MySQL和Phxbinlogsvr的交互。 一臺(tái)部署了Phxsqlproxy,MySQL和Phxbinlogsvr的機(jī)器稱為PhxSQL Node。如圖1。
PhxSQL復(fù)制流程
圖2.1 MySQL復(fù)制流程
圖2.2 PhxSQL復(fù)制流程
圖2 MySQL和PhxSQL的數(shù)據(jù)復(fù)制流程
在PhxSQL中,Phxbinlogsvr負(fù)責(zé)管理MySQL的角色和存儲(chǔ)MySQL的Binlog,Phxbinlogsvr和其管理的MySQL部署在同一臺(tái)物理機(jī)上。
MySQL Master在Send Event階段不再把Binlog復(fù)制給Slave,而是通過Phxsync插件,把數(shù)據(jù)復(fù)制到Phxbinlogsvr集群。
MySQL Slave也不再?gòu)腗aster獲取Binlog,而是從本機(jī)的Phxbinlogsvr獲取。
Phxbinlogsvr集群使用Paxos協(xié)議進(jìn)行數(shù)據(jù)復(fù)制。
PhxSQL使用PhxPaxos庫(kù),詳情請(qǐng)查看微信后臺(tái)團(tuán)隊(duì)公眾號(hào)文章微信自研生產(chǎn)級(jí)paxos類庫(kù)PhxPaxos實(shí)現(xiàn)原理介紹。
圖3 Phxbinlogsvr形成一個(gè)可靠日志存儲(chǔ)
圖4 重啟向Phxbinlogsvr詢問PendingBinlog狀態(tài)
從邏輯上來(lái)看,利用Paxos協(xié)議進(jìn)行復(fù)制,使Phxbinlogsvr形成一個(gè)可靠的日志存儲(chǔ)。PhxSQL可以看成是為MySQL增加了一個(gè)用Paxos實(shí)現(xiàn)的可靠Binlog存儲(chǔ),只要集群中多數(shù)派機(jī)器存活,就可以解決半同步復(fù)制的回滾問題。如圖3。
分別從Master和Slave的角度來(lái)解釋:
Master重啟時(shí),通過詢問Phxbinlogsvr(多數(shù)派)Pending Binlog是否存在來(lái)決定是否需要回滾。如圖4。
Slave從本機(jī)Phxbinlogsvr能拉取到的Binlog都已經(jīng)經(jīng)過Paxos協(xié)議成功復(fù)制到多數(shù)派機(jī)器,因此對(duì)于Slave來(lái)說(shuō)不存在回滾的問題。
Phxbinlogsvr通過Paxos協(xié)議復(fù)制數(shù)據(jù),很好的解決了MySQL中需要手動(dòng)回滾Binlog和在大集群時(shí)同時(shí)需要回滾Updated Slave上的Binlog的問題。
PhxSQL的Master管理
圖5 多個(gè)Master同時(shí)寫入數(shù)據(jù),導(dǎo)致數(shù)據(jù)不一致
MySQL多Master同時(shí)寫入會(huì)導(dǎo)致數(shù)據(jù)的不一致。如圖5,機(jī)器A是舊Master,在收到機(jī)器B成為了新Master的消息之前提交了Transaction 3;而同時(shí)機(jī)器B已成為新Master,Transaction 3則會(huì)留在機(jī)器A而未復(fù)制到機(jī)器B,最終兩機(jī)的數(shù)據(jù)不一致。
MySQL多Master問題的產(chǎn)生,源于機(jī)器間無(wú)法得知當(dāng)前Master的狀態(tài),***導(dǎo)致兩臺(tái)機(jī)器的數(shù)據(jù)不一致。
即使使用外部服務(wù)(例如zookeeper)也無(wú)法解根本問題。
- 對(duì)Master查詢和查詢之后的操作不是原子操作,無(wú)法保證操作時(shí)的準(zhǔn)確狀態(tài)(例如機(jī)器A向外部服務(wù)查詢得知自己是Master,然后執(zhí)行復(fù)制Binlog操作。但期間出現(xiàn)故障導(dǎo)致兩個(gè)操作之間停頓了很長(zhǎng)時(shí)間(譬如1天)。在該期間內(nèi)Master被切換,使得機(jī)器A在執(zhí)行復(fù)制Binlog時(shí),已不再是Master,導(dǎo)致了多Master的情況發(fā)生。)
- Master管理依賴外部服務(wù)的穩(wěn)定性。
多Master問題由于細(xì)節(jié)太多,暫不在此討論。
PhxSQL自身進(jìn)行了Master管理,具有以下特點(diǎn):
- Master通過Paxos協(xié)議投票選出。
- Master帶有租約,并定時(shí)續(xù)租。租約過期后,需重新選舉新的Master。
- 全局只有1個(gè)Master,或者沒有Master存在。
- 有效拒絕過期Master的非法寫入。
PhxSQL的Master自動(dòng)切換
PhxSQL實(shí)現(xiàn)了舊Master的自動(dòng)數(shù)據(jù)回滾和Master管理,使得PhxSQL可以安全地實(shí)現(xiàn)Master的自動(dòng)切換,提供高可用服務(wù)。和常見的MySQL切換Master方案不同,PhxSQL在切換Master之后仍然保證集群內(nèi)各機(jī)數(shù)據(jù)一致。
圖6
PhxSQL自動(dòng)Master流程如下:
- Slave機(jī)器上的Phxbinlogsvr定期檢查Master是否過期。如果過期轉(zhuǎn)第2步,否則繼續(xù)第1步;
- Phxbinlogsvr檢查本機(jī)MySQL是否已執(zhí)行完所有Binlog。如果已完成轉(zhuǎn)第3步,否則繼續(xù)第1步;
- Phxbinlogsvr發(fā)起投票選舉新的Master。如果投票成功,提升本機(jī)MySQL為Master,關(guān)閉readonly開關(guān);否則繼續(xù)第1步;
- 舊Master恢復(fù),本機(jī)的Phxbinlogsvr查詢發(fā)現(xiàn)已不是Master,切換MySQL角色為Slave,設(shè)置從本機(jī)Phxbinlogsvr拉取Binlog,并開啟readonly開關(guān)。
Phxsqlproxy請(qǐng)求透?jìng)?/strong>
Phxbinlogsvr解決了多Master同時(shí)寫入的問題,使得MySQLClient向舊Master寫入數(shù)據(jù)會(huì)產(chǎn)生失敗。雖然保證了數(shù)據(jù)的一致性,但仍存在下面2個(gè)問題:
- MySQLClient持續(xù)向舊Master寫入數(shù)據(jù),從而持續(xù)的失敗。(服務(wù)不可用)
- 部分MySQLClient向新Master寫入數(shù)據(jù),但其他MySQLClient仍然向舊Master讀取數(shù)據(jù),導(dǎo)致讀不到***的數(shù)據(jù)。
圖7
上述兩個(gè)問題都是由于MySQLClient的Master信息更新不及時(shí);部分Client沒有及時(shí)更新,使得有可能產(chǎn)生PhantomRead(兩次讀的結(jié)果不一致)。
圖8 Phxsqlproxy的請(qǐng)求透?jìng)?/p>
若Slave機(jī)器被訪問,Phxsqlproxy則會(huì)把請(qǐng)求透?jìng)鞯組aster機(jī)器的Phxsqlproxy。由于PhxSQL Master的全局唯一性,保證了只存在一臺(tái)MySQL被訪問。從而解決了多臺(tái)機(jī)器同時(shí)被讀寫的問題。
PhxSQL性能
使用sysbench工具對(duì)PhxSQL和MySQL的半同步復(fù)制進(jìn)行了性能對(duì)比。PhxSQL因?yàn)樵黾恿薖hxsqlproxy,導(dǎo)致讀性能比原生MySQL略低;但由于PhxPaxos的實(shí)現(xiàn)比MySQL的半同步更加高效,讓PhxSQL的寫性能比半同步復(fù)制更好。
PhxSQL比MySQL讀性能比原生MySQL略低,但寫性能比MySQL半同步復(fù)制更好。
| 讀性能 | 寫性能 | |||
| Client線程數(shù) | QPS | 耗時(shí) | QPS | 耗時(shí) |
| 200 | 約降低3% | 耗時(shí)約增加2% | 約增高25% | 約降低20% |
| 500 | 約降低13% | 約增加10% | 約增高16% | 約降低10% |
測(cè)試環(huán)境和結(jié)果如下:
機(jī)型信息
CPU : Intel(R) Xeon(R) CPU E5-2420 0 @ 1.90GHz * 24
內(nèi)存 : 32G
磁盤 : SSD Raid10
網(wǎng)絡(luò)互Ping耗時(shí)
Master -> Slave : 3 ~ 4ms
Client -> Master : 4ms
壓測(cè)工具和參數(shù)
sysbench --oltp-tables-count=10 --oltp-table-size=1000000 --num-threads=500 --max-requests=100000 --report-interval=1 --max-time=200
壓測(cè)內(nèi)容
PhxSQL和半同步復(fù)制在Client線程200和500的環(huán)境下進(jìn)行下面方式的壓測(cè):
- insert.lua (100%寫)
- select.lua (0%寫)
- OLTP.lua (20%寫)
壓測(cè)結(jié)果
Client線程數(shù):200
| insert.lua (100%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
5076 | 39.34/56.93 |
|
MySQL 半同步 |
4055 | 49.27/66.64 |
| select.lua (0%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
46334 | 4.21/5.12 |
|
MySQL 半同步 |
47528 | 4.10/5.00 |
| OLTP.lua (20%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
25657 | 140.16/186.39 |
|
MySQL 半同步 |
20391 | 176.39/226.76 |
Client線程數(shù):500
| insert.lua (100%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
8260 | 60.41/83.14 |
|
MySQL 半同步 |
7072 | 70.60/91.72 |
| select.lua (0%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
105928 | 4.58/5.81 |
|
MySQL 半同步 |
121535 | 4.17/5.08 |
| OLTP.lua (20%寫) | ||
| QPS | 耗時(shí) | |
|
PhxSQL |
46543 | 192.93/242.85 |
|
MySQL 半同步 |
33229 | 270.38/345.84 |
注:耗時(shí)分別為測(cè)試結(jié)果的平均耗時(shí)/95%分位數(shù)耗時(shí),單位ms
總結(jié)
PhxSQL解決了MySQL半同步復(fù)制中數(shù)據(jù)回滾和多Master的問題,使其能實(shí)現(xiàn)自動(dòng)Master切換且保證數(shù)據(jù)一致。PhxSQL因?yàn)樵黾恿薖hxsqlproxy,導(dǎo)致讀性能比原生MySQL略低;但由于PhxPaxos的實(shí)現(xiàn)比MySQL的半同步更加高效,讓PhxSQL的寫性能比半同步復(fù)制更好。



































