mysql 半同步復(fù)制

轉(zhuǎn)載:https://www.cnblogs.com/zero-gg/p/9057092.html

MySQL 5.7半同步復(fù)制技術(shù)](https://upload-images.jianshu.io/upload_images/9967595-ca40427e8cbb1916.gif?imageMogr2/auto-orient/strip "MySQL 5.7半同步復(fù)制技術(shù)")](http://www.ywnds.com/wp-content/uploads/2016/04/2017012217093677.gif)

  • 對(duì)于異步復(fù)制,主庫(kù)將事務(wù)Binlog事件寫入到Binlog文件中,此時(shí)主庫(kù)只會(huì)通知一下Dump線程發(fā)送這些新的Binlog,然后主庫(kù)就會(huì)繼續(xù)處理提交操作,而此時(shí)不會(huì)保證這些Binlog傳到任何一個(gè)從庫(kù)節(jié)點(diǎn)上。
  • 對(duì)于全同步復(fù)制,當(dāng)主庫(kù)提交事務(wù)之后,所有的從庫(kù)節(jié)點(diǎn)必須收到,APPLY并且提交這些事務(wù),然后主庫(kù)線程才能繼續(xù)做后續(xù)操作。這里面有一個(gè)很明顯的缺點(diǎn)就是,主庫(kù)完成一個(gè)事務(wù)的時(shí)間被拉長(zhǎng),性能降低。
  • 對(duì)于半同步復(fù)制,是介于全同步復(fù)制和異步復(fù)制之間的一種,主庫(kù)只需要等待至少一個(gè)從庫(kù)節(jié)點(diǎn)收到并且Flush Binlog到Relay Log文件即可,主庫(kù)不需要等待所有從庫(kù)給主庫(kù)反饋。同時(shí),這里只是一個(gè)收到的反饋,而不是已經(jīng)完全執(zhí)行并且提交的反饋,這樣就節(jié)省了很多時(shí)間。

二、半同步復(fù)制技術(shù)

我們今天談?wù)摰诙N架構(gòu)。我們知道,普通的replication,即MySQL的異步復(fù)制,依靠MySQL二進(jìn)制日志也即binary log進(jìn)行數(shù)據(jù)復(fù)制。比如兩臺(tái)機(jī)器,一臺(tái)主機(jī)(master),另外一臺(tái)是從機(jī)(slave)。

1)正常的復(fù)制為:事務(wù)一(t1)寫入binlog buffer;dumper線程通知slave有新的事務(wù)t1;binlog buffer進(jìn)行checkpoint;slave的io線程接收到t1并寫入到自己的的relay log;slave的sql線程寫入到本地?cái)?shù)據(jù)庫(kù)。 這時(shí),master和slave都能看到這條新的事務(wù),即使master掛了,slave可以提升為新的master。

2)異常的復(fù)制為:事務(wù)一(t1)寫入binlog buffer;dumper線程通知slave有新的事務(wù)t1;binlog buffer進(jìn)行checkpo
,一直沒有收到t1;master掛掉,slave提升為新的master,t1丟失。

3)很大的問題是:主機(jī)和從機(jī)事務(wù)更新的不同步,就算是沒有網(wǎng)絡(luò)或者其他系統(tǒng)的異常,當(dāng)業(yè)務(wù)并發(fā)上來時(shí),slave因?yàn)橐樞驁?zhí)行master批量事務(wù),導(dǎo)致很大的延遲。

為了彌補(bǔ)以上幾種場(chǎng)景的不足,MySQL從5.5開始推出了半同步復(fù)制。相比異步復(fù)制,半同步復(fù)制提高了數(shù)據(jù)完整性,因?yàn)楹苊鞔_知道,在一個(gè)事務(wù)提交成功之后,這個(gè)事務(wù)就至少會(huì)存在于兩個(gè)地方。即在master的dumper線程通知slave后,增加了一個(gè)ack(消息確認(rèn)),即是否成功收到t1的標(biāo)志碼,也就是dumper線程除了發(fā)送t1到slave,還承擔(dān)了接收slave的ack工作。如果出現(xiàn)異常,沒有收到ack,那么將自動(dòng)降級(jí)為普通的復(fù)制,直到異常修復(fù)后又會(huì)自動(dòng)變?yōu)榘胪綇?fù)制。

半同步復(fù)制具體特性:

  • 從庫(kù)會(huì)在連接到主庫(kù)時(shí)告訴主庫(kù),它是不是配置了半同步。
  • 如果半同步復(fù)制在主庫(kù)端是開啟了的,并且至少有一個(gè)半同步復(fù)制的從庫(kù)節(jié)點(diǎn),那么此時(shí)主庫(kù)的事務(wù)線程在提交時(shí)會(huì)被阻塞并等待,結(jié)果有兩種可能,要么至少一個(gè)從庫(kù)節(jié)點(diǎn)通知它已經(jīng)收到了所有這個(gè)事務(wù)的Binlog事件,要么一直等待直到超過配置的某一個(gè)時(shí)間點(diǎn)為止,而此時(shí),半同步復(fù)制將自動(dòng)關(guān)閉,轉(zhuǎn)換為異步復(fù)制。
  • 從庫(kù)節(jié)點(diǎn)只有在接收到某一個(gè)事務(wù)的所有Binlog,將其寫入并Flush到Relay Log文件之后,才會(huì)通知對(duì)應(yīng)主庫(kù)上面的等待線程。
  • 如果在等待過程中,等待時(shí)間已經(jīng)超過了配置的超時(shí)時(shí)間,沒有任何一個(gè)從節(jié)點(diǎn)通知當(dāng)前事務(wù),那么此時(shí)主庫(kù)會(huì)自動(dòng)轉(zhuǎn)換為異步復(fù)制,當(dāng)至少一個(gè)半同步從節(jié)點(diǎn)趕上來時(shí),主庫(kù)便會(huì)自動(dòng)轉(zhuǎn)換為半同步方式的復(fù)制。
  • 半同步復(fù)制必須是在主庫(kù)和從庫(kù)兩端都開啟時(shí)才行,如果在主庫(kù)上沒打開,或者在主庫(kù)上開啟了而在從庫(kù)上沒有開啟,主庫(kù)都會(huì)使用異步方式復(fù)制。

半同步復(fù)制潛在問題:

先看一下半同步復(fù)制原理圖,如下:

master將每個(gè)事務(wù)寫入binlog(sync_binlog=1),傳遞到slave刷新到磁盤(sync_relay=1),同時(shí)主庫(kù)提交事務(wù)(commit)。master等待slave反饋收到relay log,只有收到ACK后master才將commit OK結(jié)果反饋給客戶端。

在MySQL 5.5~5.6使用after_commit的模式下,客戶端事務(wù)在存儲(chǔ)引擎層提交后,在得到從庫(kù)確認(rèn)的過程中,主庫(kù)宕機(jī)了。此時(shí),即主庫(kù)在等待Slave ACK的時(shí)候,雖然沒有返回當(dāng)前客戶端,但事務(wù)已經(jīng)提交,其他客戶端會(huì)讀取到已提交事務(wù)。如果Slave端還沒有讀到該事務(wù)的events,同時(shí)主庫(kù)發(fā)生了crash,然后切換到備庫(kù)。那么之前讀到的事務(wù)就不見了,出現(xiàn)了幻讀。如下圖所示,圖片引自Loss-less Semi-Synchronous Replication on MySQL 5.7.2。

如果主庫(kù)永遠(yuǎn)啟動(dòng)不了,那么實(shí)際上在主庫(kù)已經(jīng)成功提交的事務(wù),在從庫(kù)上是找不到的,也就是數(shù)據(jù)丟失了,這是MySQL不愿意看到的。所以在MySQL 5.7版本中增加了after_sync(無損復(fù)制)參數(shù),并將其設(shè)置為默認(rèn)半同步方式,解決了數(shù)據(jù)丟失的問題。

三、MySQL 5.6半同步復(fù)制配置

具體完整配置可參考:MySQL基于日志點(diǎn)做主從復(fù)制(二)

Master配置

1)安裝半同步模塊并啟動(dòng)(此模塊就在/usr/local/mysql/lib/plugin/semisync_master.so)

|

1

|

mysql> install plugin rpl_semi_sync_master soname ``'semisync_master.so'``;

|

|

1

2

|

mysql> ``set global rpl_semi_sync_master_enabled = 1;

mysql> ``set global rpl_semi_sync_master_timeout = 2000;

|

安裝后啟動(dòng)和定制主從連接錯(cuò)誤的超時(shí)時(shí)間默認(rèn)是10s可改為2s,一旦有一次超時(shí)自動(dòng)降級(jí)為異步。(以上內(nèi)容要想永久有效需要寫到配置文件中)

|

1

2

3

4

|

[root@localhost ~]# cat /etc/my.cnf

[mysqld]

rpl_semi_sync_master_enabled = 1;

rpl_semi_sync_master_timeout = 2000;

|

Slave配置

1)安裝半同步模塊并啟動(dòng)

|

1

2

3

4

5

6

7

8

9

10

|

mysql> install plugin rpl_semi_sync_slave soname ``'semisync_slave.so'``;

mysql> ``set global rpl_semi_sync_slave_enabled = 1;

mysql> show global variables like ``'%semi%'``;

+---------------------------------+-------+

| Variable_name | Value |

+---------------------------------+-------+

| rpl_semi_sync_slave_enabled | ON |

| rpl_semi_sync_slave_trace_level | 32 |

+---------------------------------+-------+

2 rows ``in set (0.00 sec)

|

2)從節(jié)點(diǎn)需要重新連接主服務(wù)器半同步才會(huì)生效

|

1

2

|

mysql> stop slave io_thread;

mysql> start slave io_thread;

|

PS:如果想卸載異步模塊就使用uninstall即可。

Master上查看是否啟用了半同步

現(xiàn)在半同步已經(jīng)正常工作了,主要看Rpl_semi_sync_master_clients是否不為0,Rpl_semi_sync_master_status是否為ON。如果Rpl_semi_sync_master_status為OFF,說明出現(xiàn)了網(wǎng)絡(luò)延遲或Slave IO線程延遲。

那么可以驗(yàn)證一下半同步超時(shí),是否會(huì)自動(dòng)降為異步工作??梢栽赟lave上停掉半同步協(xié)議,然后在Master上創(chuàng)建數(shù)據(jù)庫(kù)看一下能不能復(fù)制到Slave上。

Slave

|

1

2

3

4

|

# 關(guān)閉半同步;

mysql> ``set global rpl_semi_sync_slave_enabled = 0 ;

mysql> stop slave io_thread;

mysql> start slave io_thread;

|

Master

|

1

2

3

4

|

mysql> create database dbtest;

Query OK, 1 row affected (2.01 sec)<br>

mysql> create database dbtest01;

Query OK, 1 row affected (0.01 sec)

|

創(chuàng)建第一個(gè)數(shù)據(jù)庫(kù)花了2.01秒,而我們前面設(shè)置的超時(shí)時(shí)間是2秒,而創(chuàng)建第二個(gè)數(shù)據(jù)庫(kù)花了0.01秒,由此得出結(jié)論是超時(shí)轉(zhuǎn)換為異步傳送??梢栽贛aster上查看半同步相關(guān)的參數(shù)值Rpl_semi_sync_master_clients和Rpl_semi_sync_master_status是否正常。

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

|

mysql> show global status like ``'%semi%'``;

+--------------------------------------------+-----------+

| Variable_name | Value |

+--------------------------------------------+-----------+

| Rpl_semi_sync_master_clients | 0 |

| Rpl_semi_sync_master_net_avg_wait_time | 0 |

| Rpl_semi_sync_master_net_wait_time | 0 |

| Rpl_semi_sync_master_net_waits | 37490 |

| Rpl_semi_sync_master_no_times | 3 |

| Rpl_semi_sync_master_no_tx | 197542 |

| Rpl_semi_sync_master_status | OFF |

| Rpl_semi_sync_master_timefunc_failures | 0 |

| Rpl_semi_sync_master_tx_avg_wait_time | 51351 |

| Rpl_semi_sync_master_tx_wait_time | 362437445 |

| Rpl_semi_sync_master_tx_waits | 7058 |

| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |

| Rpl_semi_sync_master_wait_sessions | 0 |

| Rpl_semi_sync_master_yes_tx | 7472 |

+--------------------------------------------+-----------+

14 rows ``in set (0.00 sec)

|

可以看到都自動(dòng)關(guān)閉了,需要注意一點(diǎn)的是,當(dāng)Slave開啟半同步后,或者當(dāng)主從之間網(wǎng)絡(luò)延遲恢復(fù)正常的時(shí)候,半同步復(fù)制會(huì)自動(dòng)從異步復(fù)制又轉(zhuǎn)為半同步復(fù)制,還是相當(dāng)智能的。

另外個(gè)人在實(shí)際使用中還碰到一種情況從庫(kù)IO線程有延遲時(shí),主庫(kù)會(huì)自動(dòng)把半同步復(fù)制降為異步復(fù)制;當(dāng)從庫(kù)IO延遲沒有時(shí),主庫(kù)又會(huì)把異步復(fù)制升級(jí)為半同步復(fù)制。可以進(jìn)行壓測(cè)模擬,但是此時(shí)查看Master的狀態(tài)跟上面直接關(guān)閉Slave半同步有些不同,會(huì)發(fā)現(xiàn)Rpl_semi_sync_master_clients仍然等于1,而Rpl_semi_sync_master_status等于OFF。

隨著MySQL 5.7版本的發(fā)布,半同步復(fù)制技術(shù)升級(jí)為全新的Loss-less Semi-Synchronous Replication架構(gòu),其成熟度、數(shù)據(jù)一致性與執(zhí)行效率得到顯著的提升。

四、MySQL 5.7半同步復(fù)制的改進(jìn)

現(xiàn)在我們已經(jīng)知道,在半同步環(huán)境下,主庫(kù)是在事務(wù)提交之后等待Slave ACK,所以才會(huì)有數(shù)據(jù)不一致問題。所以這個(gè)Slave ACK在什么時(shí)間去等待,也是一個(gè)很關(guān)鍵的問題了。因此MySQL針對(duì)半同步復(fù)制的問題,在5.7.2引入了Loss-less Semi-Synchronous,在調(diào)用binlog sync之后,engine層commit之前等待Slave ACK。這樣只有在確認(rèn)Slave收到事務(wù)events后,事務(wù)才會(huì)提交。在commit之前等待Slave ACK,同時(shí)可以堆積事務(wù),利于group commit,有利于提升性能。

MySQL 5.7安裝半同步模塊,命令如下:

|

1

2

|

mysql> install plugin rpl_semi_sync_master soname ``'semisync_master.so'``;

Query OK, 0 rows affected (0.00 sec)

|

看一下相關(guān)狀態(tài)信息

|

1

2

3

4

5

6

7

8

9

10

11

12

|

mysql> show global variables like ``'%semi%'``;

+-------------------------------------------+------------+

| Variable_name | Value |

+-------------------------------------------+------------+

| rpl_semi_sync_master_enabled | OFF |

| rpl_semi_sync_master_timeout | 10000 |

| rpl_semi_sync_master_trace_level | 32 |

| rpl_semi_sync_master_wait_for_slave_count | 1 |

| rpl_semi_sync_master_wait_no_slave | ON |

| rpl_semi_sync_master_wait_point | AFTER_SYNC |

+-------------------------------------------+------------+

6 rows ``in set (0.00 sec)

|

  • 支持無損復(fù)制(Loss-less Semi-Synchronous)

在Loss-less Semi-Synchronous模式下,master在調(diào)用binlog sync之后,engine層commit之前等待Slave ACK(需要收到至少一個(gè)Slave節(jié)點(diǎn)回復(fù)的ACK后)。這樣只有在確認(rèn)Slave收到事務(wù)events后,master事務(wù)才會(huì)提交,然后把結(jié)果返回給客戶端。此時(shí)此事務(wù)才對(duì)其他事務(wù)可見。在這種模式下解決了after_commit模式帶來的幻讀和數(shù)據(jù)丟失問題,因?yàn)橹鲙?kù)沒有提交事務(wù)。但也會(huì)有個(gè)問題,假設(shè)主庫(kù)在存儲(chǔ)引擎提交之前掛了,那么很明顯這個(gè)事務(wù)是不成功的,但由于對(duì)應(yīng)的Binlog已經(jīng)做了Sync操作,從庫(kù)已經(jīng)收到了這些Binlog,并且執(zhí)行成功,相當(dāng)于在從庫(kù)上多了數(shù)據(jù),也算是有問題的,但多了數(shù)據(jù),問題一般不算嚴(yán)重。這個(gè)問題可以這樣理解,作為MySQL,在沒辦法解決分布式數(shù)據(jù)一致性問題的情況下,它能保證的是不丟數(shù)據(jù),多了數(shù)據(jù)總比丟數(shù)據(jù)要好。

無損復(fù)制其實(shí)就是對(duì)semi sync增加了rpl_semi_sync_master_wait_point參數(shù),來控制半同步模式下主庫(kù)在返回給會(huì)話事務(wù)成功之前提交事務(wù)的方式。rpl_semi_sync_master_wait_point該參數(shù)有兩個(gè)值:AFTER_COMMIT和AFTER_SYNC

第一個(gè)值:AFTER_COMMIT(5.6默認(rèn)值)

master將每個(gè)事務(wù)寫入binlog(sync_binlog=1),傳遞到slave刷新到磁盤(sync_relay=1),同時(shí)主庫(kù)提交事務(wù)。master等待slave反饋收到relay log,只有收到ACK后master才將commit OK結(jié)果反饋給客戶端。

第二個(gè)值:AFTER_SYNC(5.7默認(rèn)值,但5.6中無此模式)

master將每個(gè)事務(wù)寫入binlog , 傳遞到slave刷新到磁盤(relay log)。master等待slave反饋接收到relay log的ack之后,再提交事務(wù)并且返回commit OK結(jié)果給客戶端。 即使主庫(kù)crash,所有在主庫(kù)上已經(jīng)提交的事務(wù)都能保證已經(jīng)同步到slave的relay log中。

半同步復(fù)制與無損復(fù)制的對(duì)比

1.1 ACK的時(shí)間點(diǎn)不同

  • 半同步復(fù)制在InnoDB層的Commit Log后等待ACK,主從切換會(huì)有數(shù)據(jù)丟失風(fēng)險(xiǎn)。
  • 無損復(fù)制在MySQL Server層的Write binlog后等待ACK,主從切換會(huì)有數(shù)據(jù)變多風(fēng)險(xiǎn)。

1.2 主從數(shù)據(jù)一致性

  • 半同步復(fù)制意味著在Master節(jié)點(diǎn)上,這個(gè)剛剛提交的事物對(duì)數(shù)據(jù)庫(kù)的修改,對(duì)其他事物是可見的。因此,如果在等待Slave ACK的時(shí)候crash了,那么會(huì)對(duì)其他事務(wù)出現(xiàn)幻讀,數(shù)據(jù)丟失。
  • 無損復(fù)制在write binlog完成后,就傳輸binlog,但還沒有去寫commit log,意味著當(dāng)前這個(gè)事物對(duì)數(shù)據(jù)庫(kù)的修改,其他事物也是不可見的。因此,不會(huì)出現(xiàn)幻讀,數(shù)據(jù)丟失風(fēng)險(xiǎn)。

因此5.7引入了無損復(fù)制(after_sync)模式,帶來的主要收益是解決after_commit導(dǎo)致的master crash后數(shù)據(jù)丟失問題,因此在引入after_sync模式后,所有提交的數(shù)據(jù)已經(jīng)都被復(fù)制,故障切換時(shí)數(shù)據(jù)一致性將得到提升。

  • 性能提升,支持發(fā)送binlog和接受ack的異步化

舊版本的semi sync受限于dump thread ,原因是dump thread承擔(dān)了兩份不同且又十分頻繁的任務(wù):傳送binlog給slave ,還需要等待slave反饋信息,而且這兩個(gè)任務(wù)是串行的,dump thread必須等待slave返回之后才會(huì)傳送下一個(gè)events事務(wù)。dump thread已然成為整個(gè)半同步提高性能的瓶頸。在高并發(fā)業(yè)務(wù)場(chǎng)景下,這樣的機(jī)制會(huì)影響數(shù)據(jù)庫(kù)整體的TPS 。

為了解決上述問題,在5.7版本的semi sync框架中,獨(dú)立出一個(gè)Ack Receiver線程 ,專門用于接收slave返回的ack請(qǐng)求,這將之前dump線程的發(fā)送和接受工作分為了兩個(gè)線程來處理。這樣master上有兩個(gè)線程獨(dú)立工作,可以同時(shí)發(fā)送binlog到slave,和接收slave的ack信息。因此半同步復(fù)制得到了極大的性能提升。這也是MySQL 5.7發(fā)布時(shí)號(hào)稱的Faster semi-sync replication。

但是在MySQL 5.7.17之前,這個(gè)Ack Receiver線程采用了select機(jī)制來監(jiān)聽slave返回的結(jié)果,然而select機(jī)制監(jiān)控的文件句柄只能是0-1024,當(dāng)超過1024時(shí),用戶在MySQL的錯(cuò)誤日志中或許會(huì)收到類似如下的報(bào)錯(cuò),更有甚者會(huì)導(dǎo)致MySQL發(fā)生宕機(jī)。

semi-sync master failed on net_flush() before waiting for slave reply.

MySQL 5.7.17版本開始,官方修復(fù)了這個(gè)bug,開始使用poll機(jī)制來替換原來的select機(jī)制,從而可以避免上面的問題。其實(shí)poll調(diào)用本質(zhì)上和select沒有區(qū)別,只是在I/O句柄數(shù)理論上沒有上限了,原因是它是基于鏈表來存儲(chǔ)的。但是同樣有缺點(diǎn):比如大量的fd的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核地址空間之間,而不管這樣的復(fù)制是不是有意義。poll還有一個(gè)特點(diǎn)是“水平觸發(fā)”,如果報(bào)告了fd后,沒有被處理,那么下次poll時(shí)會(huì)再次報(bào)告該fd。

其實(shí)在高性能軟件中都是用另外一種調(diào)用機(jī)制,名為epoll,高性能的代表,比如Nginx,haproxy等都是使用epoll。可能poll的復(fù)雜性比epoll低,另外對(duì)于ack receiver線程來說可能poll足矣。

  • 性能提升,控制主庫(kù)接收slave寫事務(wù)成功反饋數(shù)量

MySQL 5.7新增了rpl_semi_sync_master_wait_slave_count參數(shù),可以用來控制主庫(kù)接受多少個(gè)slave寫事務(wù)成功反饋,給高可用架構(gòu)切換提供了靈活性。如圖所示,當(dāng)count值為2時(shí),master需等待兩個(gè)slave的ack。

  • 性能提升, Binlog互斥鎖改進(jìn)

舊版本半同步復(fù)制在主提交binlog的寫會(huì)話和dump thread讀binlog的操作都會(huì)對(duì)binlog添加互斥鎖,導(dǎo)致binlog文件的讀寫是串行化的,存在并發(fā)度的問題。

MySQL 5.7對(duì)binlog lock進(jìn)行了以下兩方面優(yōu)化:

1. 移除了dump thread對(duì)binlog的互斥鎖。

2. 加入了安全邊際保證binlog的讀安全。

可以看到從replication功能引入后,官方MySQL一直在不停的完善,前進(jìn)。同時(shí)我們可以發(fā)現(xiàn)當(dāng)前原生的MySQL主備復(fù)制實(shí)現(xiàn)實(shí)際上很難在滿足數(shù)據(jù)一致性的前提下做到高可用、高性能。

五、參數(shù)sync_binlog/sync_relay與半同步復(fù)制

sync_binlog的配置

其實(shí)無損復(fù)制流程中也會(huì)存在著會(huì)導(dǎo)致主備數(shù)據(jù)不一致的情況,使主備同步失敗的情形。見下面sync_binlog配置的分析。

源碼剖析

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

|

sql/binlog.cc ordered_commit

9002 update_binlog_end_pos_after_sync= (get_sync_period() == 1);

...

//當(dāng)sync_period(sync_binlog)為1時(shí),在sync之后update binlog end pos

9021 ``if (!update_binlog_end_pos_after_sync)

//更新binlog end position,dump線程會(huì)發(fā)送更新后的events

9022 update_binlog_end_pos();

...

//

9057 std::pair<``bool``, ``bool``> result= sync_binlog_file(``false``);

...

//

9061 ``if (update_binlog_end_pos_after_sync)

9062 {

...

9068 update_binlog_end_pos(tmp_thd->get_trans_pos());

9069 }

sql/binlog.cc sync_binlog_file

8618 std::pair<``bool``, ``bool``>

8619 MYSQL_BIN_LOG::sync_binlog_file(``bool force)

8620 {

8621 ``bool synced= ``false``;

8622 unsigned ``int sync_period= get_sync_period(); ``//sync_binlog值

//sync_period為0不做sync操作,其他值為達(dá)到sync調(diào)用次數(shù)后sync

8623 ``if (force || (sync_period && ++sync_counter >= sync_period))

8624 {

|

配置分析

當(dāng)sync_binlog為0的時(shí)候,binlog sync磁盤由操作系統(tǒng)負(fù)責(zé)。當(dāng)不為0的時(shí)候,其數(shù)值為定期sync磁盤的binlog commit group數(shù)。通過源碼我們知道,sync_binlog值不等于1的時(shí)候事務(wù)在FLUSH階段就傳輸binlog到從庫(kù)了,而值為1時(shí),binlog同步操作是在SYNC階段后。當(dāng)sync_binlog值大于1的時(shí)候,sync binlog操作可能并沒有使binlog落盤。如果沒有落盤,事務(wù)在提交前,Master掉電,然后恢復(fù),那么這個(gè)時(shí)候該事務(wù)被回滾。但是Slave上可能已經(jīng)收到了該事務(wù)的events并且執(zhí)行,這個(gè)時(shí)候就會(huì)出現(xiàn)Slave事務(wù)比Master多的情況,主備同步會(huì)失敗。所以如果要保持主備一致,需要設(shè)置sync_binlog為1。

WAIT_AFTER_SYNC和WAIT_AFTER_COMMIT兩圖中Send Events的位置,也可能導(dǎo)致主備數(shù)據(jù)不一致,出現(xiàn)同步失敗的情形。實(shí)際在rpl_semi_sync_master_wait_point分析的圖中是sync binlog大于1的情況。根據(jù)上面源碼,流程如下圖所示。Master依次執(zhí)行flush binlog, update binlog position, sync binlog。如果Master在update binlog position后,sync binlog前掉電,Master再次啟動(dòng)后原事務(wù)就會(huì)被回滾。但可能出現(xiàn)Slave獲取到Events,這也會(huì)導(dǎo)致Slave數(shù)據(jù)比Master多,主備同步失敗。

由于上面的原因,sync_binlog設(shè)置為1的時(shí)候,MySQL會(huì)update binlog end pos after sync。流程如下圖所示。這時(shí)候,對(duì)于每一個(gè)事務(wù)都需要sync binlog,同時(shí)sync binlog和網(wǎng)絡(luò)發(fā)送events會(huì)是一個(gè)串行的過程,性能下降明顯。

sync_relay_log的配置

源碼剖析

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

|

sql/rpl_slave.cc handle_slave_io

5764 ``if (queue_event(mi, event_buf, event_len))

...

5771 ``if (RUN_HOOK(binlog_relay_io, after_queue_event,

5772 (thd, mi, event_buf, event_len, synced)))

after_queue_event

->plugin/semisync/semisync_slave_plugin.cc repl_semi_slave_queue_event

->plugin/semisync/semisync_slave.cc ReplSemiSyncSlave::slaveReply

queue_event

->sql/binlog.cc MYSQL_BIN_LOG::append_buffer(``const char``* buf, ``uint len, Master_info *mi)

->sql/binlog.cc after_append_to_relay_log(mi);

->sql/binlog.cc flush_and_sync(0)

->sql/binlog.cc sync_binlog_file(force)

|

配置分析

在Slave的IO線程中g(shù)et_sync_period獲得的是sync_relay_log的值,與sync_binlog對(duì)sync控制一樣。當(dāng)sync_relay_log不是1的時(shí)候,semisync返回給Master的position可能沒有sync到磁盤。在gtid_mode下,在保證前面兩個(gè)配置正確的情況下,sync_relay_log不是1的時(shí)候,僅發(fā)生Master或Slave的一次Crash并不會(huì)發(fā)生數(shù)據(jù)丟失或者主備同步失敗情況。如果發(fā)生Slave沒有sync relay log,Master端事務(wù)提交,客戶端觀察到事務(wù)提交,然后Slave端Crash。這樣Slave端就會(huì)丟失掉已經(jīng)回復(fù)Master ACK的事務(wù)events。

但當(dāng)Slave再次啟動(dòng),如果沒有來得及從Master端同步丟失的事務(wù)Events,Master就Crash。這個(gè)時(shí)候,用戶訪問Slave就會(huì)發(fā)現(xiàn)數(shù)據(jù)丟失。

通過上面這個(gè)Case,MySQL semisync如果要保證任意時(shí)刻發(fā)生一臺(tái)機(jī)器宕機(jī)都不丟失數(shù)據(jù),需要同時(shí)設(shè)置sync_relay_log為1。對(duì)relay log的sync操作是在queue_event中,對(duì)每個(gè)event都要sync,所以sync_relay_log設(shè)置為1的時(shí)候,事務(wù)響應(yīng)時(shí)間會(huì)受到影響,對(duì)于涉及數(shù)據(jù)比較多的事務(wù)延遲會(huì)增加很多。

MySQL三節(jié)點(diǎn)

在一主一從的主備semisync的數(shù)據(jù)一致性分析中放棄了高可用,當(dāng)主備之間網(wǎng)絡(luò)抖動(dòng)或者一臺(tái)宕機(jī)的情況下停止提供服務(wù)。要做到高可用,很自然我們可以想到一主兩從,這樣解決某一網(wǎng)絡(luò)抖動(dòng)或一臺(tái)宕機(jī)時(shí)候的可用性問題。但是,前文敘述要保證數(shù)據(jù)一致性配置要求依然存在,即正常情況下的性能不會(huì)有改善。同時(shí)需要解決Master宕機(jī)時(shí)候,如何選取新主機(jī)的問題,如何避免多主的情形。

選取新主機(jī)時(shí)一定要讀取兩個(gè)從機(jī),看哪一個(gè)從機(jī)有最新的日志,否則可能導(dǎo)致數(shù)據(jù)丟失。這樣的三節(jié)點(diǎn)方案就類似分布式Quorum機(jī)制,寫的時(shí)候需要保證寫成功三節(jié)點(diǎn)中的法定集合,確定新主的時(shí)候需要讀取法定集合。利用分布式一致性協(xié)議Paxos/Raft可以解決數(shù)據(jù)一致性問題,選主問題和多主問題,因此近些年,國(guó)內(nèi)數(shù)據(jù)庫(kù)團(tuán)隊(duì)大多實(shí)現(xiàn)了基于Paxos/Raft的三節(jié)點(diǎn)方案。近來MySQL官方也以插件形式引入了支持多主集群的Group Replication方案。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容