轉(zhuǎn)自:
https://www.w3cschool.cn/architectroad/architectroad-mysql-parallel-copy.html
https://blog.csdn.net/linuxlsq/article/details/52606292
http://cenalulu.github.io/mysql/mysql-5-6-gtid-basic/
一首启、緣起
mysql主從復(fù)制,讀寫分離是互聯(lián)網(wǎng)用的非常多的mysql架構(gòu)驻龟,主從復(fù)制最令人詬病的地方就是温眉,在數(shù)據(jù)量較大并發(fā)量較大的場景下,主從延時會比較嚴重翁狐。
為什么mysql主從延時這么大类溢?
回答:從庫使用【單線程】重放relaylog。
什么是relay log(中繼日志)
The relay log, like the binary log, consists of a set of numbered files containing events that describe database changes, and an index file that contains the names of all used relay log files.
The term “relay log file” generally denotes an individual numbered file containing database events. The term”relay log” collectively denotes the set of numbered relay log files plus the index file
參見:http://www.21yunwei.com/archives/4896
優(yōu)化思路是什么露懒?
回答:使用單線程重放relaylog使得同步時間會比較久闯冷,導(dǎo)致主從延時很長,優(yōu)化思路不難想到懈词,可以【多線程并行】重放relaylog來縮短同步時間蛇耀。
mysql如何“多線程并行”來重放relaylog,是本文要分享的主要內(nèi)容坎弯。
二纺涤、如何多線程并行重放relaylog
通過多個線程來并行重放relaylog是一個很好縮短同步時間的思路,但實施之前要解決這樣一個問題:
如何來分割relaylog抠忘,才能夠讓多個work-thread并行操作數(shù)據(jù)data時撩炊,使得data保證一致性?
首先崎脉,【隨機的分配relaylog肯定是不行的】拧咳,假設(shè)relaylog中有這樣三條串行的修改記錄:
update account set money=100 where uid=58;
update account set money=150 where uid=58;
update account set money=200 where uid=58;
串行執(zhí)行:肯定能保證與主庫的執(zhí)行序列一致,最后得到money=200
隨機分配并行執(zhí)行:3個工作線程并發(fā)執(zhí)行這3個語句囚灼,誰最后執(zhí)行成功是不確定的骆膝,故得到的數(shù)據(jù)可能與主庫不同
好,對于這個問題灶体,可以用什么樣的思路來解決呢(大伙怎么想阅签,mysql團隊其實也就是這么想的)
【方法一:相同庫上的寫操作,用相同的work-thread來重放relaylog赃春;不同庫上的寫操作愉择,可以用多個work-thread并發(fā)來重放relaylog】
如何做到呢?
回答:不難,hash(db-name) % thread-num锥涕,庫名hash之后再模上線程數(shù)衷戈,就能夠做到。
存在的不足层坠?
很多公司對mysql的使用是“單庫多表”殖妇,如果是這樣的話,仍然是同一個work-thread在串行執(zhí)行破花,還是不能提高relaylog的重放速度谦趣。
優(yōu)化方案:將“單庫多表”的模式升級為“多庫多表”的模式。
其實座每,數(shù)據(jù)量大并發(fā)量大的互聯(lián)網(wǎng)業(yè)務(wù)場景前鹅,“多庫”模式還具備著其他很多優(yōu)勢,例如:
(1)非常方便的實例擴展:dba很容易將不同的庫擴展到不同的實例上
(2)按照業(yè)務(wù)進行庫隔離:業(yè)務(wù)解耦峭梳,進行業(yè)務(wù)隔離舰绘,減少耦合與相互影響
(3)…
對于架構(gòu)師進行架構(gòu)設(shè)計的啟示是:使用多庫的方式設(shè)計db架構(gòu),能夠降低主從同步的延時葱椭。
新的想法:“單庫多表”的場景捂寿,還有并行執(zhí)行優(yōu)化余地么?
仔細回顧和思考孵运,即使只有一個庫秦陋,數(shù)據(jù)的修改和事務(wù)的執(zhí)行在主庫上也是并行操作的,既然在主庫上可以并行操作治笨,在從庫上為啥就不能并行操作驳概,而要按照庫來串行執(zhí)行呢(表示不服)?
新的思路:將主庫上同時并行執(zhí)行的事務(wù)旷赖,分為一組抡句,編一個號,這些事務(wù)在從庫上的回放可以并行執(zhí)行(事務(wù)在主庫上的執(zhí)行都進入到prepare階段杠愧,說明事務(wù)之間沒有沖突,否則就不可能提交)逞壁,沒錯流济,mysql正是這么做的。
【方法二:基于GTID的并行復(fù)制】
新版的mysql腌闯,將組提交的信息存放在GTID中绳瘟,使用mysqlbinlog工具,可以看到組提交內(nèi)部的信息:
20160607 23:22 server_id 58 XXX GTID last_committed=0 sequence_numer=1
20160607 23:22 server_id 58 XXX GTID last_committed=0 sequence_numer=2
20160607 23:22 server_id 58 XXX GTID last_committed=0 sequence_numer=3
20160607 23:22 server_id 58 XXX GTID last_committed=0 sequence_numer=4
和原來的日志相比姿骏,多了last_committed和sequence_number糖声。
last_committed表示事務(wù)提交時,上次事務(wù)提交的編號,如果具備相同的last_committed蘸泻,說明它們在一個組內(nèi)琉苇,可以并發(fā)回放執(zhí)行。
三谴咸、結(jié)尾
從mysql并行復(fù)制縮短主從同步時延的思想可以看到馍管,架構(gòu)的思路是相同的:
(1)多線程是一種常見的縮短執(zhí)行時間的方法
(2)多線程并發(fā)分派任務(wù)時必須保證冪等性:mysql的演進思路琼腔,提供了“按照庫冪等”,“按照commit_id冪等”兩種方式穷蛹,思路大伙可以借鑒
mysql在并行復(fù)制上的逐步優(yōu)化演進:
mysql5.5 -> 不支持并行復(fù)制,對大伙的啟示:升級mysql吧
mysql5.6 -> 按照庫并行復(fù)制昼汗,對大伙的啟示:使用“多庫”架構(gòu)吧
mysql5.7 -> 按照GTID并行復(fù)制
四肴熏、附錄
一、MySQL主從復(fù)制原理介紹
MySQL的主從復(fù)制是一個異步的復(fù)制過程(雖然一般情況下感覺是實時的)顷窒,數(shù)據(jù)將從一個Mysql數(shù)據(jù)庫(我們稱之為Master)復(fù)制到另一個Mysql數(shù)據(jù)庫(我們稱之為Slave)蛙吏,在Master與Slave之間實現(xiàn)整個主從復(fù)制的過程是由三個線程參與完成的。其中有兩個線程(SQL線程和IO線程)在Slave端蹋肮,另一個線程(I/O線程)在Master端出刷。
要實現(xiàn)MySQL的主從復(fù)制,首先必須打開Master端的binlog記錄功能坯辩,否則就無法實現(xiàn)馁龟。因為整個復(fù)制過程實際上就是Slave從aster端獲取binlog日志,然后再在Slave上以相同順序執(zhí)行獲取的binlog日志中的記錄的各種SQL操作
1)在Slave 服務(wù)器上執(zhí)行sart slave命令開啟主從復(fù)制開關(guān)漆魔,開始進行主從復(fù)制坷檩。
2)此時,Slave服務(wù)器的IO線程會通過在master上已經(jīng)授權(quán)的復(fù)制用戶權(quán)限請求連接master服務(wù)器改抡,并請求從執(zhí)行binlog日志文件的指定位置(日志文件名和位置就是在配置主從復(fù)制服務(wù)時執(zhí)行change
master命令指定的)之后開始發(fā)送binlog日志內(nèi)容
3)Master服務(wù)器接收到來自Slave服務(wù)器的IO線程的請求后矢炼,其上負責(zé)復(fù)制的IO線程會根據(jù)Slave服務(wù)器的IO線程請求的信息分批讀取指定binlog日志文件指定位置之后的binlog日志信息,然后返回給Slave端的IO線程阿纤。返回的信息中除了binlog日志內(nèi)容外句灌,還有在Master服務(wù)器端記錄的IO線程。返回的信息中除了binlog中的下一個指定更新位置欠拾。
4)當(dāng)Slave服務(wù)器的IO線程獲取到Master服務(wù)器上IO線程發(fā)送的日志內(nèi)容胰锌、日志文件及位置點后,會將binlog日志內(nèi)容依次寫到Slave端自身的Relay Log(即中繼日志)文件(Mysql-relay-bin.xxx)的最末端藐窄,并將新的binlog文件名和位置記錄到master-info文件中资昧,以便下一次讀取master端新binlog日志時能告訴Master服務(wù)器從新binlog日志的指定文件及位置開始讀取新的binlog日志內(nèi)容
5)Slave服務(wù)器端的SQL線程會實時檢測本地Relay Log 中IO線程新增的日志內(nèi)容,然后及時把Relay LOG 文件中的內(nèi)容解析成sql語句荆忍,并在自身Slave服務(wù)器上按解析SQL語句的位置順序執(zhí)行應(yīng)用這樣sql語句格带,并在relay-log.info中記錄當(dāng)前應(yīng)用中繼日志的文件名和位置點
二撤缴、GTID簡介
什么是GTID
GTID(Global Transaction ID)是對于一個已提交事務(wù)的編號,并且是一個全局唯一的編號叽唱。 GTID實際上是由UUID+TID組成的屈呕。其中UUID是一個MySQL實例的唯一標(biāo)識。TID代表了該實例上已經(jīng)提交的事務(wù)數(shù)量尔觉,并且隨著事務(wù)提交單調(diào)遞增凉袱。下面是一個GTID的具體形式
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
更詳細的介紹可以參見:官方文檔
GTID的作用
那么GTID功能的目的是什么呢?具體歸納主要有以下兩點:
- 根據(jù)GTID可以知道事務(wù)最初是在哪個實例上提交的
- GTID的存在方便了Replication的Failover
此時,Server A
的服務(wù)器宕機钉稍,需要將業(yè)務(wù)切換到Server B
上涤躲。同時,我們又需要將Server C
的復(fù)制源改成Server B
贡未。復(fù)制源修改的命令語法很簡單即CHANGE MASTER TO MASTER_HOST='xxx', MASTER_LOG_FILE='xxx', MASTER_LOG_POS=nnnn
种樱。而難點在于,由于同一個事務(wù)在每臺機器上所在的binlog名字和位置都不一樣俊卤,那么怎么找到Server C
當(dāng)前同步停止點嫩挤,對應(yīng)Server B
的master_log_file
和master_log_pos
是什么的時候就成為了難題。這也就是為什么M-S復(fù)制集群需要使用MMM
,MHA
這樣的額外管理工具的一個重要原因消恍。 這個問題在5.6的GTID出現(xiàn)后岂昭,就顯得非常的簡單。由于同一事務(wù)的GTID在所有節(jié)點上的值一致狠怨,那么根據(jù)Server C
當(dāng)前停止點的GTID就能唯一定位到Server B
上的GTID。甚至由于MASTER_AUTO_POSITION
功能的出現(xiàn)佣赖,我們都不需要知道GTID的具體值恰矩,直接使用CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION
命令就可以直接完成failover的工作。 So easy不是么?