久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL中主從復(fù)制的原理是什么

共計(jì) 8258 個(gè)字符,預(yù)計(jì)需要花費(fèi) 21 分鐘才能閱讀完成。

自動(dòng)寫代碼機(jī)器人,免費(fèi)開(kāi)通

MySQL 中主從復(fù)制的原理是什么,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

0. 主從復(fù)制

首先主從復(fù)制是什么? 簡(jiǎn)單來(lái)說(shuō)是讓一臺(tái) MySQL 服務(wù)器去復(fù)制另一臺(tái) MySQL 的數(shù)據(jù),使兩個(gè)服務(wù)器的數(shù)據(jù)保持一致。

這種方式與 Redis 的主從復(fù)制的思路沒(méi)有太大的出入。如果你對(duì) Redis 的主從復(fù)制感興趣可以去看看《Redis 的主從復(fù)制》。那既然 Redis 和 MySQL 都采用了復(fù)制這種方式,主從復(fù)制所帶來(lái)的意義是什么呢?

通過(guò)復(fù)制功能,構(gòu)建一個(gè)或者多個(gè)從庫(kù),可以提高數(shù)據(jù)庫(kù)的高可用性、可擴(kuò)展性,同時(shí)實(shí)現(xiàn)負(fù)載均衡。當(dāng)主庫(kù)發(fā)生故障時(shí),可以快速的切到其某一個(gè)從庫(kù),并將該從庫(kù)提升為主庫(kù),因?yàn)閿?shù)據(jù)都一樣,所以不會(huì)影響系統(tǒng)的運(yùn)行; 當(dāng) MySQL 服務(wù)器需要扛住更多的讀請(qǐng)求時(shí),可以把讀請(qǐng)求的流量分流到各個(gè)從庫(kù)上去,寫請(qǐng)求則轉(zhuǎn)發(fā)給主庫(kù),形成讀寫分離的架構(gòu),來(lái)提供更好的讀擴(kuò)展和請(qǐng)求的負(fù)載均衡。

讀寫分離的架構(gòu)應(yīng)用的其實(shí)非常廣泛,就比如 MySQL,還有 Redis,以及我們熟悉的 Zookeeper,Zookeeper 的 Follower 收到讀請(qǐng)求不會(huì)自己處理,而是會(huì)將讀請(qǐng)求轉(zhuǎn)發(fā)給 Leader,感興趣的可以自己下來(lái)了解一下,這里就不偏題了。

1. 復(fù)制原理

MySQL 的主從復(fù)制支持兩種方式:

基于行

基于語(yǔ)句

基于語(yǔ)句的復(fù)制在 MySQL3.23 中就已經(jīng)有了,而基于語(yǔ)句的方式則在 5.1 中才實(shí)現(xiàn)。其本質(zhì)都是基于主庫(kù)的 binlog 來(lái)實(shí)現(xiàn)的,主庫(kù)記錄 binlog,然后從庫(kù)將 binlog 在自己的服務(wù)器上重放,從而保證了主、從的數(shù)據(jù)一致性。

1.1 binlog

MySQL 中日志分為兩個(gè)維度,一個(gè)是 MySQL 服務(wù)器的,一個(gè)是底層存儲(chǔ)引擎的。而上文提到的 binlog 就是屬于 MySQL 服務(wù)器的日志,binlog 也叫二進(jìn)制日志,記錄了所有對(duì) MySQL 所做的更改。

基于行、語(yǔ)句的復(fù)制方式跟 binlog 的存儲(chǔ)方式有關(guān)系。binlog 有三種存儲(chǔ)格式,分別是 Statement、Row 和 Mixed。

Statement 基于語(yǔ)句,只記錄對(duì)數(shù)據(jù)做了修改的 SQL 語(yǔ)句,能夠有效的減少 binlog 的數(shù)據(jù)量,提高讀取、基于 binlog 重放的性能

Row   只記錄被修改的行,所以 Row 記錄的 binlog 日志量一般來(lái)說(shuō)會(huì)比 Statement 格式要多。基于 Row 的 binlog 日志非常完整、清晰,記錄了所有數(shù)據(jù)的變動(dòng),但是缺點(diǎn)是可能會(huì)非常多,例如一條 update 語(yǔ)句,有可能是所有的數(shù)據(jù)都有修改; 再例如 alter  table 之類的,修改了某個(gè)字段,同樣的每條記錄都有改動(dòng)。

Mixed Statement 和 Row 的結(jié)合,怎么個(gè)結(jié)合法呢。例如像 update 或者 alter  table 之類的語(yǔ)句修改,采用 Statement 格式。其余的對(duì)數(shù)據(jù)的修改例如 update 和 delete 采用 Row 格式進(jìn)行記錄。

為什么會(huì)有這么多方式呢? 因?yàn)?Statement 只會(huì)記錄 SQL 語(yǔ)句,但是并不能保證所有情況下這些語(yǔ)句在從庫(kù)上能夠正確的被重放出來(lái)。因?yàn)榭赡茼樞虿粚?duì)。

MySQL 什么時(shí)候會(huì)記錄 binlog 呢? 是在事務(wù)提交的時(shí)候,并不是按照語(yǔ)句的執(zhí)行順序來(lái)記錄,當(dāng)記錄完 binlog 之后,就會(huì)通知底層的存儲(chǔ)引擎提交事務(wù),所以有可能因?yàn)檎Z(yǔ)句順序錯(cuò)誤導(dǎo)致語(yǔ)句出錯(cuò)。

1.2 查看 binlog

這里拿 MySQL 5.6 舉例子,binlog 默認(rèn)是處于關(guān)閉狀態(tài)的。我們可以通過(guò)命令 show variables like %log_bin%   來(lái)查看關(guān)于 binlog 的配置。

MySQL 中主從復(fù)制的原理是什么

默認(rèn)配置

log_bin 代表是否開(kāi)啟了 binlog,其默認(rèn)值為 OFF。

log_bin 代表是否開(kāi)啟了 binlog,其默認(rèn)值為 OFF

log_bin_basename binlog 存儲(chǔ)文件的完整名稱,會(huì)在默認(rèn)的文件名后面添加上遞增的序號(hào),就例如 mysql-bin.000001

log_bin_index binlog 索引文件名稱,例如 mysql-bin.index

sql_log_bin 在 binlog 開(kāi)啟的時(shí)候,可以禁用當(dāng)前 session 的 binlog

你可以在 MySQL 中通過(guò)命令 show binary logs 查看所有的 binlog 文件

圖片

查看 binlog

知道了有哪些文件之后我們可以來(lái)看看 binlog 文件中的內(nèi)容,可以在 MySQL 通過(guò) show binlog events 命令來(lái)查看。

show binglog events   查看第一個(gè) binlog 文件,我們也可以通過(guò) in 參數(shù)來(lái)指定,假設(shè)我們想看的文件名是 mysql-bin.000001,那么可以使用命令 show binlog  events in mysql-bin.000001 來(lái)查看指定的 binlog 文件

MySQL 中主從復(fù)制的原理是什么

查看 binlog

接下來(lái)我們來(lái)看看我們?cè)?MySQL 中的操作所對(duì)應(yīng)的 binlog 內(nèi)容分別是什么。

初始化

我們上面提到過(guò),binlog 是由一個(gè)一個(gè)的 event 組成的。從 MySQL  5.0 開(kāi)始,binlog 的第一個(gè) event 都為 Format_desc,位于圖中的 Event_type 那一列。可以看到內(nèi)容為 Server  ver;5.6.50-log, Binlog ver: 4,說(shuō)明當(dāng)前使用的 MySQL 版本為 5.6.50,Binlog 的版本是 V4。

創(chuàng)建數(shù)據(jù)庫(kù)

然后我創(chuàng)建了一個(gè)名為 student 的 DB,其 Event_type 是 Query,這個(gè) event 的內(nèi)容為 CREATE DATABASE student  DEFAULT CHARACTER SET = utf8mb4,一個(gè)建庫(kù)語(yǔ)句。

新建表

然后我創(chuàng)建了一個(gè)名為 student 的表,Event_type 也是 Query,內(nèi)容為 use student; CREATE TABLE student  (id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT),一個(gè)建表語(yǔ)句。

插入數(shù)據(jù)

然后我們執(zhí)行 INSERT 語(yǔ)句給該表插入兩行數(shù)據(jù),再次查看 binlog。

INSERT INTO `student` (`id`, `name`) VALUES (NULL,  張三  INSERT INTO `student` (`id`, `name`) VALUES (NULL,  李四

MySQL 中主從復(fù)制的原理是什么

image-20210106123550397

可以看到每次 INSERT 都會(huì)開(kāi)啟一個(gè)事務(wù),你可能會(huì)疑惑,我們只是簡(jiǎn)單的執(zhí)行了 INSERT 語(yǔ)句,沒(méi)有顯示的開(kāi)啟事務(wù)。那為什么會(huì)有事務(wù)產(chǎn)生呢?

這是因?yàn)?MySQL 采用了自動(dòng)提交 (AUTOCOMMIT) 的機(jī)制,我使用的 InnoDB 存儲(chǔ)引擎,是支持事務(wù)的,所有的用戶活動(dòng)都發(fā)生在事務(wù)中。我們可以通過(guò) show  variables like %AUTOCOMMIT% 命令查看,如果結(jié)果是 ON 則代表是開(kāi)啟的。

1.3 復(fù)制的核心步驟

我們假設(shè)主庫(kù)已經(jīng)開(kāi)啟了 binlog,并正常的記錄 binlog。

首先從庫(kù)啟動(dòng) I / O 線程,跟主庫(kù)建立客戶端連接。

主庫(kù)啟動(dòng) binlog dump 線程,讀取主庫(kù)上的 binlog event 發(fā)送給從庫(kù)的 I / O 線程,I/ O 線程獲取到 binlog  event 之后將其寫入到自己的 Relay Log 中。

然后從庫(kù)啟動(dòng) SQL 線程,將 Relay 中的數(shù)據(jù)進(jìn)行重放,完成從庫(kù)的數(shù)據(jù)更新。

總結(jié)來(lái)說(shuō),主庫(kù)上只會(huì)有一個(gè)線程,而從庫(kù)上則會(huì)有兩個(gè)線程。

MySQL 中主從復(fù)制的原理是什么

主從復(fù)制流程

1.4 Relay Log

relay log 其實(shí)和 binlog 沒(méi)有太大的區(qū)別,在 MySQL 4.0 之前是沒(méi)有 Relay  Log 這部分的,整個(gè)過(guò)程中只有兩個(gè)線程。但是這樣也帶來(lái)一個(gè)問(wèn)題,那就是復(fù)制的過(guò)程需要同步的進(jìn)行,很容易被影響,而且效率不高。例如主庫(kù)必須要等待從庫(kù)讀取完了才能發(fā)送下一個(gè) binlog 事件。這就有點(diǎn)類似于一個(gè)阻塞的信道和非阻塞的信道。

MySQL 中主從復(fù)制的原理是什么

阻塞信道

阻塞信道就跟你在柜臺(tái)一樣,你要遞歸柜員一個(gè)東西,但是你和柜員之間沒(méi)有可以放東西的地方,你就只能一直把文件拿著,直到柜員接手; 而非阻塞信道就像你們之間有個(gè)地方可以放文件,你就直接放上去就好了,不用等柜員接手。

引入了 Relay Log 之后,讓原本同步的獲取事件、重放事件解耦了,兩個(gè)步驟可以異步的進(jìn)行,Relay Log 充當(dāng)了緩沖區(qū)的作用。Relay  Log 有一個(gè) relay-log.info 的文件,用于記錄當(dāng)前復(fù)制的進(jìn)度,下一個(gè)事件從什么 Pos 開(kāi)始寫入,該文件由 SQL 線程負(fù)責(zé)更新。

1.5 Relay Log 核心參數(shù)

接下來(lái)讓我們了解一下 Relay Log 的核心參數(shù)。

max_relay_log_size 中繼日志的最大 size,默認(rèn)值 0,如果為 0 就會(huì)取默認(rèn)的 size 1G,否則就為設(shè)置的值

relay_log 定義 relay 的名稱,默認(rèn)為主機(jī)名 +relay-bin,例如像 hostname-relay-bin

relay_log_basename 中繼日志的全路徑,即路徑 +   文件名,例如 /path/to/hostname-relay-bin,最大長(zhǎng)度為 256

relay_log_index 定義中繼日志的索引文件的全路徑,同樣其最大的長(zhǎng)度為 256. 其默認(rèn)值為 hostname +  relay-bin.index,例如 /path/to/hostname-relay-bin.index

relay_log_info_file 定義 relay-log.info 文件的名稱

relay_log_info_repository 存放 relay  log 重放的數(shù)據(jù)的方式,可以設(shè)置為 FILE 和 TABLE。FILE 代表將中繼日志重放的數(shù)據(jù)記錄在 relay-info.log 中,TABLE 則將其存放在 slave_relay_log_info 這張表里。

relay_log_purge 是否自動(dòng)清空不需要的中繼日志,默認(rèn)值為 ON

relay_log_recovery 當(dāng)從庫(kù)宕機(jī)后,如果 relay  log 損壞了導(dǎo)致部分的中繼日志沒(méi)有進(jìn)行同步,則自動(dòng)放棄所有未進(jìn)行重放的中繼日志,并從主庫(kù)重新獲取,默認(rèn)值為 OFF

relay_log_space_limit   設(shè)置中繼日志的最大值,防止寫滿磁盤。但是不建議設(shè)置這個(gè)值,建議還是給中繼日志需要的空間,0 就是不限制,0 也是默認(rèn)值

sync_relay_log   用于控制中繼日志寫入磁盤的變量,假設(shè)值為 n,那么在中繼日志每接受 n 次 binlog 事件之后就會(huì)調(diào)用 fdatasync()函數(shù)將中繼日志強(qiáng)制的刷入磁盤; 相反,如果值為 0,則寫入 OS 的緩沖區(qū)內(nèi),由 OS 調(diào)度決定何時(shí)將中繼日志刷入磁盤,這樣一來(lái)如果在沒(méi)有刷入之前報(bào)錯(cuò)了,那么中繼日志就會(huì)丟失。默認(rèn)值是 10000,也就是每向中繼日志中寫入 1w 次 binlog 事件就將中繼日志強(qiáng)制的刷入磁盤。

sync_relay_log_info   該參數(shù)的影響跟參數(shù) relay_log_info_repository 有一定關(guān)系,同時(shí)也跟是否使用支持事務(wù)的存儲(chǔ)引擎有關(guān)系。該值默認(rèn)也是 10000.

relay_log_info_repository 為 FILE,假設(shè)設(shè)置的值為 N,那么每 N 次事務(wù)都會(huì)都會(huì)調(diào)用 fdatasync()強(qiáng)制將 relay-log.info 刷入磁盤

relay_log_info_repository 為 TABLE,如果使用了支持事務(wù)的引擎,則該表每次事務(wù)結(jié)束都會(huì)被更新; 如果沒(méi)有使用事務(wù)引擎則會(huì)在寫入 N 個(gè) binlog 事件的時(shí)候更新該表。

relay_log_info_repository 為 FILE,MySQL 不會(huì)調(diào)用 fdatasync(),而是將刷入磁盤的調(diào)度交給 OS;

relay_log_info_repository 為 TABLE,如果使用了支持事務(wù)的存儲(chǔ)引擎,則每次事務(wù)的時(shí)候該表都會(huì)被更新; 如果沒(méi)有使用事務(wù)引擎,則永遠(yuǎn)不會(huì)被更新

當(dāng) sync_relay_log_info 為 0 時(shí)

當(dāng) sync_relay_log_info 大于 0 時(shí)

2. 復(fù)制模型

平常的開(kāi)發(fā)中,其實(shí)很少說(shuō)一上來(lái)就直接搞主從架構(gòu)的。費(fèi)時(shí)間、費(fèi)錢還引入了額外的復(fù)雜度,最后發(fā)現(xiàn)投入了這么多一個(gè)單 MySQL 服務(wù)器就完全能 handle。

這就跟一個(gè)產(chǎn)品的架構(gòu)迭代是一樣的,剛剛起步的時(shí)候一個(gè)單體應(yīng)用足夠了。當(dāng)你的業(yè)務(wù)擴(kuò)展,請(qǐng)求膨脹,單體無(wú)法抗住壓力了,就會(huì)考慮開(kāi)始部署多實(shí)例,開(kāi)始采用微服務(wù)架構(gòu)去做橫向擴(kuò)展、負(fù)載均衡。

2.1 一主多從

當(dāng)然你也可以把它當(dāng)成一主一從。

這是最簡(jiǎn)單的模型,特別適合少量寫、大量讀的情況。讀請(qǐng)求被分到了各個(gè)從庫(kù)上,有效的幫主庫(kù)分散了壓力,能夠提升讀并發(fā)。當(dāng)然,你也可以只是把從庫(kù)當(dāng)成一個(gè)災(zāi)備庫(kù),除了主從復(fù)制之外,沒(méi)有其他任何的請(qǐng)求和數(shù)據(jù)傳輸。

甚至你可以把其中一個(gè)備庫(kù)作為你的預(yù)發(fā)環(huán)境的數(shù)據(jù)庫(kù),當(dāng)然,這說(shuō)到底還是直接動(dòng)了生產(chǎn)環(huán)境的數(shù)據(jù)庫(kù),是一種過(guò)于理想的用途,因?yàn)檫@還涉及到生產(chǎn)環(huán)境數(shù)據(jù)庫(kù)的數(shù)據(jù)敏感性。不是所有人都能夠接觸到的,需要有完善的權(quán)限機(jī)制。

MySQL 中主從復(fù)制的原理是什么

MySQL 一主多從

值得注意的是,如果有 n 個(gè)從庫(kù),那么主庫(kù)上就會(huì)有 n 個(gè) binlog  dump 線程。如果這個(gè) n 比較大的話在復(fù)制的時(shí)候可能會(huì)造成主庫(kù)的性能抖動(dòng)。所以在從庫(kù)較多的情況下可以采用級(jí)聯(lián)復(fù)制。

2.2 級(jí)聯(lián)復(fù)制

級(jí)聯(lián)復(fù)制用大白話說(shuō)就是套娃。

本來(lái)從庫(kù) B、C、D、E、F、G 都是復(fù)制的主庫(kù) A,但是現(xiàn)在由于 A 的壓力比較大,就不這么干了,調(diào)整成了如下的模式。

B、C 復(fù)制 A

D、E 復(fù)制 B

F、G 復(fù)制 C

MySQL 中主從復(fù)制的原理是什么

MySQL 級(jí)聯(lián)復(fù)制

這就叫級(jí)聯(lián)復(fù)制,開(kāi)啟瘋狂套娃模式。你甚至?xí)X(jué)得這種套娃很眼熟,在 Redis 主從復(fù)制中也可以采用級(jí)聯(lián)模式,slave 去復(fù)制另一個(gè) slave。

級(jí)聯(lián)復(fù)制的好處在于很大程度上減輕了主庫(kù)的壓力,主庫(kù)只需要關(guān)心與其有直接復(fù)制關(guān)系的從庫(kù),剩下的復(fù)制則交給從庫(kù)即可。相反,由于是這種層層嵌套的關(guān)系,如果在較上層出現(xiàn)了錯(cuò)誤,會(huì)影響到掛在該服務(wù)器下的所有子庫(kù),這些錯(cuò)誤的影響效果被放大了。

2.3 主主復(fù)制

顧名思義,就是兩個(gè)主庫(kù)相互復(fù)制,客戶端可以對(duì)任意一臺(tái)主庫(kù)進(jìn)行寫操作。任何一臺(tái)主庫(kù)服務(wù)器上的數(shù)據(jù)發(fā)生了變化都會(huì)同步到另一臺(tái)服務(wù)器上去。有點(diǎn)類似于 Eureka  Server 的雙節(jié)點(diǎn)模式,兩個(gè)注冊(cè)中心相互注冊(cè)。這樣一來(lái),任何一臺(tái)掛了都不會(huì)對(duì)系統(tǒng)產(chǎn)生影響。

而且主主復(fù)制可以打破數(shù)據(jù)庫(kù)性能瓶頸,一個(gè)很酷的功能 mdash; mdash; 橫向擴(kuò)展。為什么說(shuō)很酷呢,如果 DB 能做到橫向擴(kuò)展,那很多被數(shù)據(jù)庫(kù)并發(fā)所限制的瓶頸都可以被突破,然而 …

但是主主復(fù)制其實(shí)并不可靠,兩邊的數(shù)據(jù)沖突的可能性很大。例如復(fù)制停止了,系統(tǒng)仍然在向兩個(gè)主庫(kù)中寫入數(shù)據(jù),也就是說(shuō)一部分?jǐn)?shù)據(jù)在 A,另一部分的數(shù)據(jù)在 B,但是沒(méi)有相互復(fù)制,且數(shù)據(jù)也不同步了。要修復(fù)這部分?jǐn)?shù)據(jù)的難度就會(huì)變得相當(dāng)大。

所以我認(rèn)為雙主的更多的意義在于 HA,而不是負(fù)載均衡。

2.4 主、被動(dòng)的主主復(fù)制

同樣還是雙主的結(jié)構(gòu),但是區(qū)別在于其中一臺(tái)是只讀的被動(dòng)服務(wù)器,客戶端不會(huì)向該庫(kù)進(jìn)行寫操作。

其用途在哪里呢? 例如我們要在不中斷服務(wù)的前提下對(duì) MySQL 進(jìn)行維護(hù)、優(yōu)化,舉個(gè)例子 mdash; mdash; 修改表結(jié)構(gòu)。假設(shè)我們有兩個(gè)數(shù)據(jù)庫(kù),主庫(kù) A 和被動(dòng)主庫(kù) B,注意此處的被動(dòng)主庫(kù)是只讀的,我們先停止 A 對(duì) B 的復(fù)制,也就是停掉 A 上的 SQL 線程。

MySQL 中主從復(fù)制的原理是什么

主主停止復(fù)制

這樣一來(lái),我們之后在 B 上執(zhí)行的非常耗時(shí)、可能需要鎖表的操作就不會(huì)立即同步到 A 上來(lái)。因?yàn)榇藭r(shí) A 正在對(duì)外提供服務(wù),所以不能使其收到影響,但是由于采用的是異步的復(fù)制模式,所以 Relay  Log 還是繼續(xù)由 I / O 線程寫入,只是不去進(jìn)行重放。

然后我們?cè)?B 上執(zhí)行此次的維護(hù)操作,注意,此時(shí) A 上面發(fā)生的更新還是會(huì)正常的同步到 B 來(lái)。執(zhí)行完后交換讀寫的角色。也就是讓 A 變成只讀的被動(dòng)主庫(kù),而 B 變?yōu)橹鲃?dòng)主庫(kù)對(duì)外提供服務(wù)。

MySQL 中主從復(fù)制的原理是什么

重新開(kāi)啟 SQL 線程

然后重新開(kāi)啟 SQL 線程,A 開(kāi)始去對(duì)之前 Relay  Log 中積累的 event 進(jìn)行重放。雖然 A 此時(shí)可能會(huì)阻塞住,但是 A 已經(jīng)沒(méi)有對(duì)外提供服務(wù)了,所以沒(méi)有問(wèn)題。

主、被動(dòng)下的主主模式的好處大家也就清楚了,可以在不停止服務(wù)的情況下去做數(shù)據(jù)庫(kù)的結(jié)構(gòu)更新,其次可以在主庫(kù)發(fā)生故障的情況下,快速的切換,保證數(shù)據(jù)庫(kù)的 HA。

3. 復(fù)制方式

上文我們不止一次的提到了復(fù)制是異步的,接下來(lái)我們來(lái)了解一下 MySQL 的主從復(fù)制都有哪些方式。

3.1 異步復(fù)制

首先就是異步,這也是 MySQL 默認(rèn)的方式。在異步復(fù)制下,主庫(kù)不會(huì)主動(dòng)的向從庫(kù)發(fā)送消息,而是等待從庫(kù)的 I / O 線程建立連接,然后主庫(kù)創(chuàng)建 binlog  dump 線程,把 binlog event 發(fā)送給 I / O 線程,流程如下圖。

MySQL 中主從復(fù)制的原理是什么

MySQL 復(fù)制模式

主庫(kù)在執(zhí)行完自己的事務(wù)、記錄完 binlog 之后就會(huì)直接返回,不會(huì)與客戶端確認(rèn)任何結(jié)果。然后后續(xù)由 binlog  dump 線程異步的讀取 binlog,然后發(fā)送給從庫(kù)。處理請(qǐng)求和主從復(fù)制是兩個(gè)完全異步化的過(guò)程。

3.2 同步復(fù)制

同步模式則是,主庫(kù)執(zhí)行一個(gè)事務(wù),那么主庫(kù)必須等待所有的從庫(kù)全部執(zhí)行完事務(wù)返回 commit 之后才能給客戶端返回成功,

MySQL 中主從復(fù)制的原理是什么

同步復(fù)制

值得注意的是,主庫(kù)會(huì)直接提交事務(wù),而不是等待所有從庫(kù)返回之后再提交。MySQL 只是延遲了對(duì)客戶端的返回,并沒(méi)有延后事務(wù)的提交。

同步模式用腳趾頭想知道性能會(huì)大打折扣,它把客戶端的請(qǐng)求和主從復(fù)制耦合在了一起,如果有某個(gè)從庫(kù)復(fù)制線程執(zhí)行的慢,那么對(duì)客戶端的響應(yīng)也會(huì)慢很多。

3.3 半同步復(fù)制

半同步相對(duì)于同步的區(qū)別在于,同步需要等待所有的從庫(kù) commit,而半同步只需要一個(gè)從庫(kù) commit 就可以返回了。如果超過(guò)默認(rèn)的時(shí)間仍然沒(méi)有從庫(kù) commit,就會(huì)切換為異步模式再提交。客戶端也不會(huì)一直去等待了。

MySQL 中主從復(fù)制的原理是什么

MySQL 復(fù)制模式

因?yàn)榧词购竺嬷鲙?kù)宕機(jī)了,也能至少保證有一個(gè)從庫(kù)節(jié)點(diǎn)是可以用的,此外還減少了同步時(shí)的等待時(shí)間。

4. 復(fù)制中的數(shù)據(jù)一致性

我們?cè)?1.3 中討論了復(fù)制的核心步驟,看似很簡(jiǎn)單的一個(gè)流程,主庫(kù)的 binlog dump 去讀取 binlog,然后從庫(kù)的 I / O 線程去讀取、寫入 Relay  Log,進(jìn)而從庫(kù)的 SQL 線程再讀取 Relay Log 進(jìn)行重放。

那如果 I / O 線程復(fù)制到一半自己突然掛掉了呢? 又或者復(fù)制到一半主庫(kù)宕機(jī)了呢? 如果和保證數(shù)據(jù)一致性的呢?

我們上面提到過(guò),有一個(gè) relay-log.info 的文件,用于記錄當(dāng)前從庫(kù)正在復(fù)制的 binlog 和寫入的 Relay  Log 的 Pos,只要這個(gè)文件還在,那么當(dāng)從庫(kù)意外重啟之后,就會(huì)重新讀取文件,從上次復(fù)制的地方開(kāi)始繼續(xù)復(fù)制。這就跟 Redis 中的主從復(fù)制類似,雙方要維護(hù)一個(gè) offset,通過(guò)對(duì)比 offset,來(lái)進(jìn)行 psync 增量數(shù)據(jù)同步。

但是在 MySQL  5.5 以及之前,都只能將復(fù)制的進(jìn)度記錄在 relog-log.info 文件中。換句話說(shuō),參數(shù) relay_log_info_repository 只支持 FILE,可以再回到上面的 1.5  Relay Log 核心參數(shù)看一下。所以只有在 sync_relay_log_info 次事務(wù)之后才會(huì)把 relay-log.info 文件刷入磁盤。

如果在刷入磁盤之前從庫(kù)掛了,那么重啟之后就會(huì)發(fā)現(xiàn) SQL 線程實(shí)際執(zhí)行到位置和數(shù)據(jù)庫(kù)記錄的不一致,數(shù)據(jù)一致性的問(wèn)題就這么產(chǎn)生了。

所以在 MySQL  5.6 時(shí),參數(shù) relay_log_info_repository 支持了 TABLE,這樣一來(lái)我們就可以將復(fù)制的進(jìn)度放在系統(tǒng)的 mysql.slave_relay_log_info 表里去,并且把更新進(jìn)度、SQL 線程執(zhí)行用戶事務(wù)綁定成一個(gè)事務(wù)執(zhí)行。即使 slave 宕機(jī)了,我們也可以通過(guò) MySQL 內(nèi)建的崩潰恢復(fù)機(jī)制來(lái)使實(shí)際執(zhí)行的位置和數(shù)據(jù)庫(kù)保存的進(jìn)度恢復(fù)到一致。

其次還有上面提到的半同步復(fù)制,主庫(kù)會(huì)先提交事務(wù),然后等待從庫(kù)的返回,再將結(jié)果返回給客戶端,但是如果在主庫(kù)等待的時(shí)候,從庫(kù)掛了呢?

此時(shí)主庫(kù)上由于事務(wù)已經(jīng)提交了,但是從庫(kù)上卻沒(méi)有這個(gè)數(shù)據(jù)。所以在 MySQL  5.7 時(shí)引入了無(wú)損半同步復(fù)制,增加了參數(shù) rpl_semi_sync_master_wait_point 的值,在 MySQL  5.7 中值默認(rèn)為 after_sync,在 MySQL 5.6 中默認(rèn)值為 after_commit。

after_sync   主庫(kù)先不提交事務(wù),等待某一個(gè)從庫(kù)返回了結(jié)果之后,再提交事務(wù)。這樣一來(lái),如果從庫(kù)在沒(méi)有任何返回的情況下宕機(jī)了,master 這邊也無(wú)法提交事務(wù)。主從仍然是一致的

after_commit 與之前討論的一樣,主庫(kù)先提交事務(wù),等待從庫(kù)返回結(jié)果再通知客戶端

看完上述內(nèi)容,你們掌握 MySQL 中主從復(fù)制的原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注丸趣 TV 行業(yè)資訊頻道,感謝各位的閱讀!

向 AI 問(wèn)一下細(xì)節(jié)

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-12-04發(fā)表,共計(jì)8258字。
轉(zhuǎn)載說(shuō)明:除特殊說(shuō)明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒(méi)有評(píng)論)
主站蜘蛛池模板: 尉氏县| 马公市| 社会| 双鸭山市| 泸州市| 乌海市| 密山市| 东乌珠穆沁旗| 南丰县| 锡林郭勒盟| 灯塔市| 岐山县| 万宁市| 尚志市| 资中县| 宁化县| 沙雅县| 临安市| 高唐县| 体育| 靖宇县| 苏尼特右旗| 青岛市| 韶山市| 延庆县| 右玉县| 交口县| 建宁县| 泸州市| 大足县| 荔波县| 两当县| 高陵县| 措勤县| 永胜县| 朝阳市| 荣昌县| 泰兴市| 台东市| 衡阳县| 巨鹿县|