天天看點

MySQL核心月報 2014.12-MySQL· 性能優化·并行複制外建限制問題

<b>背景:</b>

mysql 主備同步是通過binlog來進行的,備庫的 io 線程從主庫拉取binlog,sql線程将拉取的binlog應用到備庫,在5.6之前,備庫隻有一個線程應用binlog,主庫的更新量大,且備庫的執行效率低時,就會造成了大量從主庫拉取的binlog來不及執行,是以造成了主備延遲問題。為了解決主備延遲,需要提高備庫的執行效率,阿裡mysql 設計并開發了并行複制功能,所謂并行複制,指的是應用binlog的線程數量是多個的,而不是原生的單個線程,經過測試可以極大的提高複制性能(有3x的性能提升),在并行複制中,一個 io 線程,一個分發線程,多個sql_thread,分發線程讀取relay log,并将讀取的relay log 分發給多個sql_thread, 進而實作并行化的效果。

<b>原理:</b>

分發線程的分發原理是依據目前事務所操作的表的名稱來進行分發的,如果事務是跨表的(一個事務更新多張表),則需要等待已配置設定的該表相關的事務全部執行完畢,才會繼續分發,其配置設定行為的僞碼可以簡單的描述如下:

<b>問題描述(testcase):</b>

drop table t2 if exists t2; drop table t1 if exists t1; create table t1(c1 int primary key, c2 int); create table t2(c1 int primary key, c2 int , foreign key (c2) references t1(c1)); insert into t1 values(1,1); insert into t1 values(2,2); insert into t2 values(1,1); insert into t2 values(2,2);

以下兩個語句在備庫的執行順序不同,結果會不同

delete from t2 where c2=1; (語句1)

update t1 set c1=3 where c1=1;(語句2)

如果語句2先于語句1在備庫執行,則會報外建限制錯誤,因為在上述的分發原理中沒有考慮到外建限制問題,這種情況下,隻有串行化處理了,當然,你可以執行:set global foreign_key_checks=off;然後start slave;在類似語句執行完後,再恢複foreign check,但是這樣做真正安全嗎?答案是不一定的……

情況1:

在這種定義下,如果不檢測foreign key,則不會有問題,因為對t1, t2的操作都會記錄binlog;

情況2:

create table t2(c1 int primary key, c2 int , foreign key (c2) references t1(c1) on delete cascade);

在這種定義下,如果不檢測foreign key,則會有問題,因為對表t1的操作會影響t2表,在檢測foreign key的時候,會進行相應的cascade操作,如果不檢測foreign key,則不進行級聯操作,這種問題一旦發生,則會引起主備不一緻問題。

<b>解決方法</b>

5.6 并行複制沒有此問題,5.6中在檢測到foreign key的事件時,會等待已經分發的所有binlog都已執行完再執行,是以解決了此問題。

<b>改進方案 </b>這個方案雖然能解決問題,但是若系統中隻要出現一個外鍵關系,并且持續有更新,會導緻持續性的回退到單線程方案,那麼多線程複制的效果就會大打折扣。實際上這個做法比較極端,改進的方案是,遇到有foreign key 的表,應該将其分發到依賴他的表的同一個sql thread 中。這樣執行這些事務時,其他表的并行複制仍能繼續。

繼續閱讀