阿里RDS開發專家解析MySQL各版本并行復制

ElsaOOSB 8年前發布 | 24K 次閱讀 MySQL 數據庫服務器

MySQL并行復制已經是老生常談,我從2010年開始就著手處理 線上這個問題,剛開始兩三年也樂此不疲地分享。現在再提這個話題有點“炒冷 飯”的感覺。

然而,又把它拎出來談,是因為有些同學覺得“5.7的并行復制終于徹底解決了復制并發性問題”。但我感覺還是有必要分析一下,這就好像大家都說沒有銀彈,但是又期待銀彈一樣。

既然要說5.7版本的并行復制,干脆順手把各個版本的并行復制都說明一下,也好有個對比。

目錄

  • 背景

  • 解決基本思路

  • MySQL5.5版本分析

  • MySQL5.6版本分析

  • MariaDB分析

  • MySQL5.7版本分析

  • 小結

一. 背景

背景就是MySQL一直以來的備庫復制都是單線程apply。 一句話說完,因為這幾年太多這樣文章了。

二. 解決的基本思路

改成多線程復制。

備庫有兩個線程與復制相關:io_thread 負責從主庫拿binlog并寫到relaylog, sql_thread 負責讀relaylog并執行。

多線程的思路就是把sql_thread 變成分發線程,然后由一組worker_thread來負責執行。

幾乎所有的并行復制都是這個思路,有不同的,便是sql_thread 的分發策略。

而這些策略里面又分成兩類:利用傳統binlog格式、修改binlog。

使用傳統的binlog格式的幾類,由于binlog里面的信息就那些,因此只能按照粒度來分,也就是:按庫、按表、按行。

另外有兩個策略是修改了binlog格式的,在binlog里面增加了別的信息,用于體現提交分組。

下面我們分別介紹幾個并行復制的實現。

三. 5.5版本分析

MySQL官方5.5是不支持并行復制的。但是在阿里的業務需要并行復制的年份,還沒有官方版本支持,只好自己實現。而且因從兼容性角度考慮,不修改binlog格式,所以采用的是利用傳統binlog格式的改造。

阿里的版本支持兩種分發策略:按表和按行。

前情說明,由于MySQLbinlog日志還有用于別的系統的要求,因此阿里的binlog格式都是row----這也給并行復制的實現減少了難度。

按表分發策略:row格式的binlog,每個DML前面都是有Table_map event的。因此很容易拿到庫名/表名。一個簡單的思路是,不同表的更新之間是不需要嚴格按照順序的。

因此按照表名hash,hash key是 庫名+表名,相同的表的更新放到同一個worker上。這樣就保證同一個表的更新順序,跟主庫上是一樣的。

應用場景:對于多表更新的場景效果特別好。缺點是,若是熱點表更新,則本策略無效。而且由于hash表的維護,性能反而下降。

按行分發策略:row格式的binlog中,也不難拿到主鍵ID。 有同學說如果沒有主鍵怎么辦,答案是"走開,現在誰還沒主鍵:)"。好吧,正經答案是沒有主鍵就不支持這個策略。

同樣的,我們認為不同行的更新,可以無序并發的。只要保證同一行的數據更新,在備庫上的順序與主庫上的相同即可。

因此按照主鍵id hash,所以這個hash key更長,必須是 庫名+表名+主鍵id。相同行的更新放到同一個worker上。

需要注意的是,上面的描述看上去都是對單個event的操作,實際上并不能!因為備庫可能接受讀,因此事務的原子性是要保證的,也就是說,對于涉及多個更新操作的事務,每次用于決策的不是一個hash key,而是一組。

應用場景:熱點表更新。缺點,hash key計算沖突的代價大。尤其是大事務,計算hash key的cpu消耗大,而且耗內存。這需要業務DBA做判斷得失。

四. 5.6版本分析

官方的5.6支持的是按庫分發。有了上面的背景,大家就知道,這個feature出來以后,在中國并沒有什么反響。

但是這個策略也要說也是有優點的:

1、對于可以按表分發的場景,可以通過將表遷到不同的庫,來應用此策略,有可操作性。

2、速度更快,因為hash key就一個庫名。

3、不要求binlog格式,大家知道不論是row還是statement格式,都是能夠輕松獲取庫名的。

所以并不是完全沒有用的。還是習慣問題。

五. MariaDB分析

MariaDB的并行復制策略看上去有好幾個選項,然而生產上可用的也就是默認值的 CONSERVATIVE。

由于maraiaDB支持多主復制,一個domain_id字段是用來標示事務來源的。如果來自于不同的主,自然可以并行(這個其實也是通用概念,還得業務DBA自己判斷)。

對于同一個主庫來的binlog,用commit_id 來決定分組。

想法是這樣的:在主庫上同時提交的事務設置成相同的commit_id。在備庫上apply時,相同的commit_id可以并行執行,因為這意味著這些事務之間是沒有行沖突的(否則不可能同時提交)。

這個思路跟最初從單線程改成多線程一樣,個人認為是劃時代的。

但是也并沒有解決了所有的問題。這個策略最怕的是,拖后腿事務。

設想一下這個場景,假設某個DB里面正在作大量小更新事務(比如每個事務更新一行),這樣在備庫就并行得很歡樂。

然后突然,在同一個實例,另外一個庫下,或者同一個庫的另外一個跟目前的更新無關的表,突然有一個delte操作刪除了10w行。

delete事務在提交的時候,跟當時一起提交的事務都算同一個commit_id。假設為N.

之后的小事務更新提交組commit_id為N+1。

到備庫apply時,就會發現N這個組里面,其他小事務都執行完了,線程進入空閑狀態,但是不能繼續執行N+1這個commit_id的事務,因為N里面還有一個大事務沒有執行完成,這個我們認為是拖后腿的。

而基于傳統binlog格式的上面三個策略,反而沒有這個問題。只要是策略上能夠判斷不沖突,大事務自己有個線程跑,其他事務繼續并行。

六. 5.7版本分析

MySQL官方5.7版本也是及時跟進,先引入了上述MariaDB的策略。當然從版權安全上,oracle是不會允許直接port代碼的。

然后官方5.7的新版本在此之上繼續優化。

實際上按組直接分段這個策略略顯粗暴。實際上事務提交并不是一個點,而是一個階段。至少我們可以分成:準備提交、提交中、提交完成。

這三個階段都是在事務已經完成了主要操作邏輯,進入commit狀態了。

同時進入“提交中”狀態的算同一個commit_id。但是實際上,在任意時刻,處于”準備提交”的事務,與“提交中”的事務,也是可以并行的。但是明顯他們會被分成兩個不同的commit_id。

這意味著這個策略還有提升并發度的空間。

我們來看一下兩種策略的對比差別。

假設主庫有如下面示意圖的事務序列。每個事務提交過程看成兩個階段,prepare ... commit. 分別給不同的編號。其中commit對應的數字是自然數遞增,sequence_no。而prepare是對應的數字是X+1,這個X表示的是當前已經提交完成的sequence_no。

分析:

在MariaDB的策略里面,并發執行序列如下:

每個group 執行完成后,下一個group 才可以開始。

完全執行完成的時間是每個group的最大事務時間之和,即 trx3 + trx4+trx6+trx7。

因此,如果某個group里面有一個很大的事務,則整個序列的執行時間就會被拖久。

再來看5.7的改進策略:

雖然也是group1先啟動,但是在trx1完成后,trx4就可以開始執行。

同樣的,trx7可以在trx4執行完成后就開始執行,與trx5和trx6并發。

因此可以說上面這個例子中,備庫apply過程完全達到了主庫執行的并發度。

但是對于大事務,比如trx2 commit 非常久的情況,仍然存在拖后腿的問題。

七. 小結

我們看到,就并行復制,有5種策略。

按粒度區分的三個策略,粒度從粗到細是按庫、按表、按行。

這三個的對比中,并行度越來越大,額外損耗也是。無關大事務不會影響并發度。

按照commit_id 的兩個策略,適用范圍更廣,額外消耗也低。

5.7的改進策略并發性更優。但出現大事務會拖后腿。

另外,很重要的一點,5.7的策略目的是“模擬主庫并發”,所以對于主庫單線程更新是無加速作用的。而基于沖突的前三個策略,若滿足并發條件,會出現備庫比主庫執行速度快的情況。這種需求在搭備庫或者延遲復制的場景中可能觸發。

實際上還是老話,沒有萬用的策略。策略的選擇取決于應用場景,這是架構師的工作之一。

PS:具體5.7的實現原理可參考我們團隊的@印風 同學的博客  http://mysqllover.com/?p=1370 (最后一個例子的case也從此摘錄)

Q & A

Q1: 請問tps到了多少才會觸發單線程復制不夠用的問題?

A1: 實際上單線程的能力受限非常大,尤其是備庫如果也設置了雙1的情況下。我們測試出來,2k/s的行更新(insert/update/delete)單線程就到頂了。

Q2: 除開雙1的那兩個參數,還會有什么其他的辦法or 思路去改進從庫的同步速度么?

A2:  業界做法有:1、relay fetch。 2、合并更新relay fetch 這個搜一下就有了。 合并跟新的主要思路是,把相鄰的N個事務,當做一個提交,提高提交效率。

Q3: 有個問題,對于這么多的并行復制解決方案,糅合和平衡是個很大的工程,能分享一下目前的使用情況不?

A3: 恩,其實我所知道的很多公司,選版本并不是因為要解決主備延遲的問題。一來是并不是大家都有備庫延遲的問題。二來是大家對里面的細節了解可能還不太夠,有時候就用分庫給分掉了。目前在RDS里面,我們也是默認不開的,對于有延遲的實例(監控可得), 會根據實際情況選擇分表或者分庫。明年RDS要支持mariadb和5.7。我們就會按照延遲的情況,建議用戶升級到對應的版本來解決。

Q4: 經常會看到有對比pg和mysql,總是看到有人說pg好,那mysql有什么明顯優于pg的地方么,在比較常見的場景下?

A4: 我的建議依然是,如果你發現MySQL夠用,就用MySQL,如果有需求MySQL滿足不了了(比如GIS),那再考慮選擇別的。因為MySQL在中國用的最多,你碰到的問題,上網基本都能搜到,你碰到的坑,很少是第一次被你碰到,會有人有經驗怎么跳出來。從工程的角度來說,能夠減少業務風險。

Q5: 一個大事務的更新導致mariadb多源復制hunge住但單源的沒有問題,這個問題可能的原因是什么?

A5: 比較大的可能是鎖住了。要先確認一下多源之間的數據確實是不沖突的。多源數據如果不沖突的話,得具體排查了,排查方法就是在出現被堵住的情況后,打一個pstack出來,看看除了那個大事務apply線程以外,別的線程在等什么,這只能對代碼了。 

講師介紹:林曉斌

  • 阿里云RDS數據庫源碼開發團隊負責人。

  • 淘寶核心系統數據庫組技術專家。

  • 淘寶MySQL版本的核心開發者之一。

小編精心為大家挑選了近日最受歡迎的幾篇熱文:

回復 01 1 ,看鄒德裕《數據庫運維工具化:一切從“簡”,只為DBA更輕松》。

回復 01 2 ,看 馬育義 《Oracle內核系列3-揭秘ASM磁盤頭信息》。

回復 013 ,看呂海波《去不去O,誰說了算?》;

回復 014 ,看楊德勝《Oracle故障日志采集“神助攻”—TFA工具詳解》;

回復 015 ,看郭耀龍《假事務之名,深入研究UNDO與REDO》;

回復 016 ,看陳能技《基于Docker的開發模式驅動持續集成落地實施》;

回復 017 ,看朱賢文《數據庫與存儲系統》;

回復 018 ,看盧鈞軼《揭秘非死book數據庫備份策略》;

回復 019 ,看王佩《基于Docker的mysql mha 的集群環境構建實踐》;

回復 020 ,看王津銀《互聯網運維的整體理念與最佳實踐》

來自: http://dbaplus.cn/news-11-221-1.html

 本文由用戶 ElsaOOSB 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!