面試題
你們有沒有做 MySQL 讀寫分離?如何實(shí)現(xiàn) MySQL 的讀寫分離?MySQL 主從復(fù)制原理的是啥?如何解決 MySQL 主從同步的延時(shí)問題?
面試官心理分析
高并發(fā)這個(gè)階段,肯定是需要做讀寫分離的,啥意思?因?yàn)閷?shí)際上大部分的互聯(lián)網(wǎng)公司,一些網(wǎng)站,或者是 app,其實(shí)都是讀多寫少。所以針對(duì)這個(gè)情況,就是寫一個(gè)主庫(kù),但是主庫(kù)掛多個(gè)從庫(kù),然后從多個(gè)從庫(kù)來(lái)讀,那不就可以支撐更高的讀并發(fā)壓力了嗎?
面試題剖析
如何實(shí)現(xiàn) MySQL 的讀寫分離?
其實(shí)很簡(jiǎn)單,就是基于主從復(fù)制架構(gòu),簡(jiǎn)單來(lái)說(shuō),就搞一個(gè)主庫(kù),掛多個(gè)從庫(kù),然后我們就單單只是寫主庫(kù),然后主庫(kù)會(huì)自動(dòng)把數(shù)據(jù)給同步到從庫(kù)上去。
MySQL 主從復(fù)制原理的是啥?
主庫(kù)將變更寫入 binlog 日志,然后從庫(kù)連接到主庫(kù)之后,從庫(kù)有一個(gè) IO 線程,將主庫(kù)的 binlog 日志拷貝到自己本地,寫入一個(gè) relay 中繼日志中。接著從庫(kù)中有一個(gè) SQL 線程會(huì)從中繼日志讀取 binlog,然后執(zhí)行 binlog 日志中的內(nèi)容,也就是在自己本地再次執(zhí)行一遍 SQL,這樣就可以保證自己跟主庫(kù)的數(shù)據(jù)是一樣的。

這里有一個(gè)非常重要的一點(diǎn),就是從庫(kù)同步主庫(kù)數(shù)據(jù)的過程是串行化的,也就是說(shuō)主庫(kù)上并行的操作,在從庫(kù)上會(huì)串行執(zhí)行。所以這就是一個(gè)非常重要的點(diǎn)了,由于從庫(kù)從主庫(kù)拷貝日志以及串行執(zhí)行 SQL 的特點(diǎn),在高并發(fā)場(chǎng)景下,從庫(kù)的數(shù)據(jù)一定會(huì)比主庫(kù)慢一些,是有延時(shí)的。所以經(jīng)常出現(xiàn),剛寫入主庫(kù)的數(shù)據(jù)可能是讀不到的,要過幾十毫秒,甚至幾百毫秒才能讀取到。
而且這里還有另外一個(gè)問題,就是如果主庫(kù)突然宕機(jī),然后恰好數(shù)據(jù)還沒同步到從庫(kù),那么有些數(shù)據(jù)可能在從庫(kù)上是沒有的,有些數(shù)據(jù)可能就丟失了。
所以 MySQL 實(shí)際上在這一塊有兩個(gè)機(jī)制,一個(gè)是半同步復(fù)制,用來(lái)解決主庫(kù)數(shù)據(jù)丟失問題;一個(gè)是并行復(fù)制,用來(lái)解決主從同步延時(shí)問題。
這個(gè)所謂半同步復(fù)制,也叫 semi-sync 復(fù)制,指的就是主庫(kù)寫入 binlog 日志之后,就會(huì)將強(qiáng)制此時(shí)立即將數(shù)據(jù)同步到從庫(kù),從庫(kù)將日志寫入自己本地的 relay log 之后,接著會(huì)返回一個(gè) ack 給主庫(kù),主庫(kù)接收到至少一個(gè)從庫(kù)的 ack 之后才會(huì)認(rèn)為寫操作完成了。
所謂并行復(fù)制,指的是從庫(kù)開啟多個(gè)線程,并行讀取 relay log 中不同庫(kù)的日志,然后并行重放不同庫(kù)的日志,這是庫(kù)級(jí)別的并行。
MySQL 主從同步延時(shí)問題(精華)
以前線上確實(shí)處理過因?yàn)橹鲝耐窖訒r(shí)問題而導(dǎo)致的線上的 bug,屬于小型的生產(chǎn)事故。
是這個(gè)么場(chǎng)景。有個(gè)同學(xué)是這樣寫代碼邏輯的。先插入一條數(shù)據(jù),再把它查出來(lái),然后更新這條數(shù)據(jù)。在生產(chǎn)環(huán)境高峰期,寫并發(fā)達(dá)到了 2000/s,這個(gè)時(shí)候,主從復(fù)制延時(shí)大概是在小幾十毫秒。線上會(huì)發(fā)現(xiàn),每天總有那么一些數(shù)據(jù),我們期望更新一些重要的數(shù)據(jù)狀態(tài),但在高峰期時(shí)候卻沒更新。用戶跟客服反饋,而客服就會(huì)反饋給我們。
我們通過 MySQL 命令:
show status
查看 Seconds_Behind_Master,可以看到從庫(kù)復(fù)制主庫(kù)的數(shù)據(jù)落后了幾 ms。
一般來(lái)說(shuō),如果主從延遲較為嚴(yán)重,有以下解決方案:
- 分庫(kù),將一個(gè)主庫(kù)拆分為多個(gè)主庫(kù),每個(gè)主庫(kù)的寫并發(fā)就減少了幾倍,此時(shí)主從延遲可以忽略不計(jì)。
- 打開 MySQL 支持的并行復(fù)制,多個(gè)庫(kù)并行復(fù)制。如果說(shuō)某個(gè)庫(kù)的寫入并發(fā)就是特別高,單庫(kù)寫并發(fā)達(dá)到了 2000/s,并行復(fù)制還是沒意義。
- 重寫代碼,寫代碼的同學(xué),要慎重,插入數(shù)據(jù)時(shí)立馬查詢可能查不到。
- 如果確實(shí)是存在必須先插入,立馬要求就查詢到,然后立馬就要反過來(lái)執(zhí)行一些操作,對(duì)這個(gè)查詢設(shè)置直連主庫(kù)。不推薦這種方法,你這么搞導(dǎo)致讀寫分離的意義就喪失了。