詳解如何避免MYSQL主從延遲帶來的讀寫問題
在MYSQL 部署架構(gòu)選型上,許多公司都會(huì)用到主從讀寫分離的架構(gòu),如下是一個(gè)一主一從的架構(gòu),主庫master負(fù)責(zé)寫入,從庫slave進(jìn)行讀取。
但是既然是讀寫分離,必然會(huì)面臨這樣一個(gè)問題,當(dāng)在主庫上進(jìn)行更新后,有可能數(shù)據(jù)還沒來得及同步到從庫,但是這個(gè)時(shí)候又有讀數(shù)據(jù)的需求,為了能正確讀取出數(shù)據(jù),這個(gè)時(shí)候就只有讀主庫了。但是這樣做增加了主庫的壓力,違反了我們做讀寫分離的初衷。所以這一節(jié)我們就來針對(duì)這種情況探討下,如何盡量的避免對(duì)主庫的壓力,盡量的從從庫讀取數(shù)據(jù)。
主從復(fù)制的原理
在探討解決方案前,我們先要對(duì)主從復(fù)制的原理有所了解,數(shù)據(jù)庫的操作都會(huì)記錄到binlog,如下圖所示,
1,從數(shù)據(jù)庫(slave
)會(huì)啟動(dòng)兩個(gè)線程io_thread
和sql_thread
,通過io_thread將自身與主數(shù)據(jù)庫(master
)建立連接。
2,slave向master發(fā)出要同步的位置信息(包含同步的文件名和偏移量),表示需要從該位置發(fā)起同步。
3,主數(shù)據(jù)庫master 將位置點(diǎn)后的binlog發(fā)送給slave, slave獲取到本地形成relay log
(中轉(zhuǎn)日志)。
4, 接著通過sql_thread解析relay log,執(zhí)行sql。
從主從復(fù)制的過程可以看出,主從延遲時(shí)間是 在主庫master執(zhí)行sql的時(shí)間點(diǎn)到從庫通過解析relay log 執(zhí)行sql后的時(shí)間點(diǎn)之間的差值。如果應(yīng)用程序能夠在master寫入數(shù)據(jù)后等待這么一段時(shí)間,再去slave讀取,就能正確的讀取出來數(shù)據(jù)了。
但是這個(gè)時(shí)間差值是不確定的,究竟應(yīng)用程序需要等待多久才去讀取slave,就成了我們需要思考??的問題。
如何避免延遲期間的主從數(shù)據(jù)不一致
比起在寫入數(shù)據(jù)后讀取主庫或者寫入數(shù)據(jù)后sleep一段時(shí)間讀取從庫,我給出兩個(gè)我覺得比較靠譜點(diǎn)的方法。
判斷位點(diǎn)是否同步
第一種方法是通過等待slave 將master寫入數(shù)據(jù)后的 binlog的位點(diǎn)同步完成再對(duì)slave進(jìn)行讀取。
每次修改型sql的執(zhí)行會(huì)將master的binlog 的位點(diǎn)(日志偏移量)前移,如果在修改型sql執(zhí)行完成后,能夠獲取到master的binlog 位點(diǎn),并且在客戶端阻塞等待slave同步該位點(diǎn)完畢,再從slave讀取就可以了。
MYSQL中提供了一個(gè)函數(shù)select master_pos_wait(file, pos[, timeout])
用于在slave上執(zhí)行等待master節(jié)點(diǎn)上的位點(diǎn)同步完成,其中file,和pos是在master上的文件和位點(diǎn),timeout 為了讓master_pos_wait
函數(shù)在timeout秒內(nèi)沒有返回,則會(huì)直接觸發(fā)超時(shí)返回。
返回結(jié)果解析,
- 返回結(jié)果正常情況下是一個(gè)大于0的整數(shù),表示從pos位點(diǎn)開始完成了多少個(gè)事務(wù)。
- 如果直接返回結(jié)果0,則說明在執(zhí)行
select master_pos_wait(file, pos[, timeout])
時(shí),位點(diǎn)已經(jīng)同步完成。 - 如果觸發(fā)超時(shí)則返回-1。
- 如果執(zhí)行期間slave發(fā)生錯(cuò)誤,則返回NULL。
所以,在判斷是否應(yīng)該在寫入數(shù)據(jù)后讀從庫的邏輯,我們可以這樣來寫,
1, 在master寫入數(shù)據(jù)后立馬執(zhí)行 show master status
,可以獲取如下結(jié)果
可以看到master的binlog文件名稱以及位點(diǎn)。
2, 在slave上執(zhí)行 select master_pos_wait('mysql-bin.232011',3129472,1);
,如果1s內(nèi)沒有返回,則直接返回-1。
3, 在上一步如果觸發(fā)超時(shí)返回返回-1,則直接讀取主庫,如果是>=0 的值,則直接讀取從庫。
這樣便能最大程度從從庫讀取數(shù)據(jù)。
判斷GTID 是否同步
接著,我們來看下第二種方式,其實(shí)第二種方式和通過位點(diǎn)的方式類似,不同的是slave判斷是否將數(shù)據(jù)同步完成的依據(jù)是看GTID的值。
什么是GTID值?
GTID 的全稱是 Global Transaction Identifier
,全局事務(wù) ID,是一個(gè)事務(wù)在提交的時(shí)候生成的,是這個(gè)事務(wù)的唯一標(biāo)識(shí)。
MYSQL開啟 GTID 模式的方式是 在啟動(dòng)一個(gè) MySQL 實(shí)例的時(shí)候,加上參數(shù) gtid_mode=on
和 enforce_gtid_consistency=on
。
每個(gè)事務(wù)是和GTID 值一一對(duì)應(yīng)的,每個(gè)MYSQL實(shí)例會(huì)維護(hù)一個(gè)GTID 集合,來表示實(shí)例執(zhí)行過的事務(wù)。
在slave節(jié)點(diǎn)上,通過show slave status
可以看到 GTID集合,如下圖所示,
Auto_Position=1
,表示這對(duì)主備關(guān)系使用了 GTID 協(xié)議。Retrieved_Gtid_Set
,是備庫收到的所有日志的 GTID 集合。Executed_Gtid_Set
,是備庫所有已經(jīng)執(zhí)行完成的 GTID 集合。
如果Executed_Gtid_Set 等于Retrieved_Gtid_Set 說明slave將從master那里獲取到的binlog全部執(zhí)行完畢。
在master節(jié)點(diǎn)執(zhí)行 show master status
,也能看到GTID集合,Executed_Gtid_Set
為master節(jié)點(diǎn)執(zhí)行過的GTID集合。如下圖所示,
GTID 模式下判斷同步的步驟
在GTID 模式下,從庫slave從主庫master取binlog的邏輯將不再是直接告訴master 要取的文件和位點(diǎn)了,而是由slave將自身的GTID集合告訴master。
master再結(jié)合自身的GTID集合,找出在master中有但是在slave中沒有的GTID集合,然后從binlog中找到第一個(gè)不在GTID集合中的事務(wù),從該事務(wù)的binlog位點(diǎn)開始,往后讀取binlog發(fā)送給slave。
MYSQL針對(duì)于GTID同樣提供 了一個(gè)函數(shù)select wait_for_executed_gtid_set(gtid_set, 1);
來讓slave去判斷對(duì)master執(zhí)行過的gtid_set 是否已經(jīng)同步完成。
wait_for_executed_gtid_set
函數(shù)的返回結(jié)果解析如下,
- 如果slave 執(zhí)行的事務(wù)中包含傳入的 gtid_set,返回 0。
- 如果等待1s后還沒同步完成,則返回1。
所以在GTID 模式下的,在判斷是否應(yīng)該在寫入數(shù)據(jù)后讀從庫的邏輯,我們可以這樣來寫,
1, 在master寫入數(shù)據(jù)后立馬執(zhí)行 show master status
,可以獲取如下結(jié)果
可以看到master的Executed_Gtid_Set的值。
2, 在slave上執(zhí)行
select wait_for_executed_gtid_set('76cd5ea1-c541-11ee-87ef-fa163eefe144:1-56382789, 808d2fb8-687b-11ec-b8b9-fa163e410530:1-144078103, 9081c19b-63de-11ed-9755-fa163eb8b97f:1-1093294115', 1);
,如果1s內(nèi)沒有返回,則直接返回1。
3, 在上一步如果觸發(fā)超時(shí)即返回1,則直接讀取主庫,如果是=0 ,則直接讀取從庫。 這樣便能最大程度從從庫讀取數(shù)據(jù)。
以上就是詳解如何避免MYSQL主從延遲帶來的讀寫問題的詳細(xì)內(nèi)容,更多關(guān)于MYSQL主從延遲的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SQL數(shù)據(jù)分表Mybatis?Plus動(dòng)態(tài)表名優(yōu)方案
這篇文章主要介紹了SQL數(shù)據(jù)分表Mybatis?Plus動(dòng)態(tài)表名優(yōu)方案,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08MySQL Where 條件語句介紹和運(yùn)算符小結(jié)
這篇文章主要介紹了MySQL Where 條件語句介紹和運(yùn)算符小結(jié),本文同時(shí)還給出了一些用法示例,需要的朋友可以參考下2014-11-11Mysql查詢條件判斷是否包含字符串的方法實(shí)現(xiàn)
本文主要介紹了Mysql查詢條件判斷是否包含字符串的方法實(shí)現(xiàn),主要包括like,locate,postion,instr,find_in_set這幾種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10golang實(shí)現(xiàn)mysql數(shù)據(jù)庫備份的操作方法
這篇文章主要介紹了golang實(shí)現(xiàn)mysql數(shù)據(jù)庫備份的操作方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06詳解MySQL如何有效的存儲(chǔ)IP地址及字符串IP和數(shù)值之間如何轉(zhuǎn)換
本文主要介紹了MySQL如何有效的存儲(chǔ)IP地址及字符串IP和數(shù)值之間如何轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01