分布式系統(tǒng)中的數(shù)據(jù)復(fù)制
什么是數(shù)據(jù)復(fù)制?
數(shù)據(jù)復(fù)制是指將數(shù)據(jù)復(fù)制到一個(gè)或多個(gè)數(shù)據(jù)容器以確??捎眯缘倪^程。復(fù)制的數(shù)據(jù)通常存儲(chǔ)在不同的數(shù)據(jù)庫實(shí)例中,即使一個(gè)實(shí)例發(fā)生故障,我們也可以從其他實(shí)例獲取數(shù)據(jù)。
一種流行數(shù)據(jù)復(fù)制的實(shí)現(xiàn)架構(gòu)是主從架構(gòu)。
推薦博主開源的 H5 商城項(xiàng)目waynboot-mall,這是一套全部開源的微商城項(xiàng)目,包含三個(gè)項(xiàng)目:運(yùn)營后臺(tái)、H5 商城前臺(tái)和服務(wù)端接口。實(shí)現(xiàn)了商城所需的首頁展示、商品分類、商品詳情、商品 sku、分詞搜索、購物車、結(jié)算下單、支付寶/微信支付、收單評(píng)論以及完善的后臺(tái)管理等一系列功能。技術(shù)上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中間件。分模塊設(shè)計(jì)、簡(jiǎn)潔易維護(hù),歡迎大家點(diǎn)個(gè) star、關(guān)注博主。
github 地址:https://github.com/wayn111/waynboot-mall
主從架構(gòu)
為了理解這個(gè)架構(gòu),我們舉一個(gè)例子。
- 我們有四個(gè)客戶端,每個(gè)客戶端都連接到一個(gè)負(fù)載均衡器。
- 然后負(fù)載均衡器將請(qǐng)求分發(fā)到三個(gè)應(yīng)用程序服務(wù)器。
- 每臺(tái)服務(wù)器連接到一個(gè)數(shù)據(jù)庫實(shí)例。
你能注意到這里有什么問題嗎?
我們的數(shù)據(jù)庫存在單點(diǎn)故障。如果它崩潰了,我們的整個(gè)系統(tǒng)就會(huì)停止工作。
為了避免這種單點(diǎn)故障,我們可以使用另一個(gè)數(shù)據(jù)庫(最好是不同的數(shù)據(jù)庫實(shí)例)來存儲(chǔ)原始數(shù)據(jù)的副本(一般我們成為從庫)?,F(xiàn)在如果原始數(shù)據(jù)庫(主庫)崩潰,我們可以將請(qǐng)求轉(zhuǎn)到從庫。
但是我們?nèi)绾伪3謴膸炫c主庫同步呢?這有兩種方法。
同步復(fù)制數(shù)據(jù)
- 在這種方法中,數(shù)據(jù)同時(shí)寫入主庫和從庫
- 數(shù)據(jù)始終一致。即數(shù)據(jù)如果寫入主庫,它也會(huì)寫入從庫
- 數(shù)據(jù)庫負(fù)載較高
異步復(fù)制數(shù)據(jù)
- 在這種方法中,首先將數(shù)據(jù)寫入主庫,并定期將更新寫入從庫
- 由于復(fù)制以固定間隔進(jìn)行,因此存在數(shù)據(jù)丟失和不一致的可能性
- 數(shù)據(jù)庫負(fù)載相對(duì)較低
這里我們的一般定義是收到寫請(qǐng)求的主庫數(shù)據(jù)庫是 master)。從庫被稱為 slaves。
主從架構(gòu)
如上圖我們的主站也就是 Server2 維護(hù)事務(wù)日志。他會(huì)更新從站中(Server1)的數(shù)據(jù),它發(fā)送命令,然后從站以相同的順序執(zhí)行這些命令。
如果服務(wù)器向從站發(fā)送寫入請(qǐng)求會(huì)發(fā)生什么?
有兩種方法可以處理這種情況
- 不允許對(duì)從站的寫請(qǐng)求,從站無法寫入數(shù)據(jù)庫,它只能去讀從庫數(shù)據(jù)。
- 允許從站寫入數(shù)據(jù)。我們將允許從站寫入數(shù)據(jù)。然后從站將更改復(fù)制到主站。在這種情況下,從站就接替了主站的角色。所以不再是主從架構(gòu)而是主主架構(gòu)
主主架構(gòu)的問題
網(wǎng)絡(luò)故障可能會(huì)導(dǎo)致主主架構(gòu)中的數(shù)據(jù)不一致。
讓我們用一個(gè)例子來理解這一點(diǎn),假設(shè)我們有兩個(gè)數(shù)據(jù)庫實(shí)例 A 和 B。
- 兩人都是 master。
- 它們之間的路由器出現(xiàn)故障。所以 A 認(rèn)為 B 離線,B 認(rèn)為 A 離線。
- 他們有一個(gè)數(shù)據(jù)項(xiàng) X,其值最初為 100。
現(xiàn)在用戶發(fā)送以下請(qǐng)求,
- X 減去 20,該請(qǐng)求被路由到 A,此時(shí) A 中 X 的值為 80。
- X 減去 80,這個(gè)請(qǐng)求被路由到 B(因?yàn)槎际?master,所以寫請(qǐng)求可以路由到任何數(shù)據(jù)庫)?,F(xiàn)在 B 中 X 的值為 20。
由于存在通信故障,A 和 B 無法同步,它們具有不同的數(shù)據(jù)值,因此不一致。
- 現(xiàn)在,如果用戶發(fā)出讀請(qǐng)求,他/她將獲得不同的值,具體取決于他/她將連接到的數(shù)據(jù)庫。
這個(gè)問題被稱為裂腦問題。
解決裂腦問題
解決裂腦問題
我們可以通過添加第三個(gè)節(jié)點(diǎn)(數(shù)據(jù)庫實(shí)例)來解決裂腦問題。
這里我們假設(shè)一個(gè)節(jié)點(diǎn)崩潰以及其他兩個(gè)節(jié)點(diǎn)之間的路由器崩潰的可能性極小。
讓我們考慮三個(gè)數(shù)據(jù)庫實(shí)例 A、B 和 C。
- 如果 C 崩潰,A 和 B 是主庫并且它們是同步的。所以他們處于一致的狀態(tài)。當(dāng) C 在線時(shí),他們可以讀取 A 或 B 的內(nèi)容。
- 如果 A 和 B 之間出現(xiàn)通信故障
- 當(dāng) A 收到寫入請(qǐng)求時(shí),它將其狀態(tài)傳播到 C。最初狀態(tài)為 S0,然后轉(zhuǎn)移到 Sx。所以現(xiàn)在 A 和 C 都有 Sx。
- 當(dāng) B 收到寫入請(qǐng)求時(shí),它將其狀態(tài)從 S0 移至 Sy。它嘗試將其狀態(tài)傳播到 C,但失敗,因?yàn)?B 的先前狀態(tài)不等于 C?,F(xiàn)在 B 中止寫入請(qǐng)求并將其狀態(tài)更新為 Sx。現(xiàn)在 B 可以接受寫入請(qǐng)求并將更改傳播到 C。
這稱為分布式共識(shí)。多個(gè)節(jié)點(diǎn)就特定值達(dá)成一致。在這種情況下,A、B 和 C 在最終狀態(tài)上達(dá)成一致。