MySQL 能不能雙主?有沒有一致性問題?
MySQL能不能使用雙主架構(gòu)?
可以。
MySQL為什么要使用雙主架構(gòu)?
MySQL最常見的集群架構(gòu),是一主多從,主從同步,讀寫分離,但此時(shí)寫庫仍然是單點(diǎn)。
為了保證MySQL寫庫的高可用,可以在一個(gè)MySQL數(shù)據(jù)庫集群中可以設(shè)置兩個(gè)主庫,并設(shè)置雙向同步,以冗余寫庫的方式,來保證寫庫的高可用。
MySQL雙主架構(gòu),會存在什么問題?
如果MySQL雙主架構(gòu),同時(shí)提供服務(wù),可能會引發(fā)數(shù)據(jù)的一致性問題。因?yàn)閿?shù)據(jù)的同步有一個(gè)時(shí)間差,并發(fā)的寫入可能導(dǎo)致數(shù)據(jù)同步失敗,引起數(shù)據(jù)丟失。
舉個(gè)栗子:
如上圖所述,假設(shè)主庫使用了auto increment來作為自增主鍵:
- 兩個(gè)MySQL主庫設(shè)置雙向同步來保證主庫的高可用;
- 數(shù)據(jù)庫中現(xiàn)存的記錄主鍵是1,2,3;
- 主庫1插入了一條記錄,主鍵為4,并向主庫2同步數(shù)據(jù);
- 數(shù)據(jù)同步成功之前,主庫2也插入了一條記錄,由于數(shù)據(jù)還沒有同步成功,插入記錄生成的主鍵也為4,并向主庫1也同步數(shù)據(jù);
- 主庫1和主庫2都插入了主鍵為4的記錄,雙主同步失敗,數(shù)據(jù)不一致;
能否在MySQL層面,保證兩個(gè)主庫生成的主鍵一定不沖突呢?
可以的,只需要為兩個(gè)主庫的自增ID:
- 設(shè)置不同的初始值;
- 設(shè)置相同的增長步長;
如上圖所示:
- 兩個(gè)MySQL主庫設(shè)置雙向同步來保證主庫的高可用;
- 庫1的自增初始值是1,庫2的自增初始值是2,增長步長都為2;
- 庫1中插入數(shù)據(jù)主鍵為1/3/5/7,庫2中插入數(shù)據(jù)主鍵為2/4/6/8,不沖突;
- 數(shù)據(jù)雙向同步后,兩個(gè)主庫會包含全部數(shù)據(jù);
如上圖所示,兩個(gè)主庫最終都將包含1/2/3/4/5/6/7/8所有數(shù)據(jù),即使有一個(gè)主庫掛了,另一個(gè)主庫也能夠保證寫庫的高可用。
上述方案,依賴與數(shù)據(jù)庫的配置,能不能由應(yīng)用程序,來保證數(shù)據(jù)的一致性呢?
答案是肯定的,應(yīng)用程序使用統(tǒng)一的ID生成器,可以保證ID的生成不沖突。
如上圖所示,調(diào)用方插入數(shù)據(jù)時(shí),帶入全局唯一ID,而不依賴于數(shù)據(jù)庫的auto increment,也能解決這個(gè)問題。
引發(fā)不一致的根本原因,是保證高可用的兩個(gè)主庫都對外提供服務(wù),如果只有一個(gè)主庫對外提供服務(wù),另一個(gè)主庫平時(shí)不提供服務(wù),僅僅在主庫掛了的時(shí)候提供服務(wù),能否消除上述數(shù)據(jù)不一致呢?
答案是悲觀的,仍然不行。
使用虛IP+keepalived的方式保證數(shù)據(jù)庫主庫的高可用,平時(shí)只有一臺主庫提供服務(wù),也可能出現(xiàn)數(shù)據(jù)不一致。
如上圖所示:
- 兩個(gè)MySQL主庫設(shè)置雙向同步來保證主庫的高可用;
- 只有主庫1對外提供寫入服務(wù);
- 兩個(gè)主庫設(shè)置相同的虛IP,在主庫1掛掉或者網(wǎng)絡(luò)異常的時(shí)候,虛IP自動漂移,備用主庫頂上,保證主庫的高可用;
切換過程中,由于虛IP沒有變化,所以切換過程對調(diào)用方是透明的,但在極限的情況下,仍可能引發(fā)數(shù)據(jù)不一致。
如上圖所示:
- 兩個(gè)MySQL主庫設(shè)置雙向同步,來保證主庫的高可用,并設(shè)置了相同的虛IP;
- 網(wǎng)絡(luò)抖動前,主庫1對上游提供寫入服務(wù),插入了一條記錄,主鍵為4,并向備用主庫2同步數(shù)據(jù);
- 突然主庫1網(wǎng)絡(luò)異常,keepalived檢測出異常后,實(shí)施虛IP漂移,備用主庫2開始提供服務(wù);
- 在主鍵4的數(shù)據(jù)同步成功之前,主庫2插入了一條記錄,也生成了主鍵為4的記錄,結(jié)果導(dǎo)致數(shù)據(jù)不一致;
有沒有辦法緩解上述問題呢?
虛IP漂移,雙主同步延時(shí)導(dǎo)致的數(shù)據(jù)不一致,本質(zhì)上,需要在雙主同步完數(shù)據(jù)之后,再實(shí)施虛IP偏移。
使用內(nèi)網(wǎng)DNS探測,緩解上述問題:
- 使用內(nèi)網(wǎng)域名連接數(shù)據(jù)庫,例如:db.kg.org;
- 主庫1和主庫2設(shè)置雙主同步,不使用相同虛IP,而是分別使用ip1和ip2;
- 一開始db.kg.org指向ip1;
- 用一個(gè)小腳本輪詢探測ip1主庫的連通性;
- 當(dāng)ip1主庫發(fā)生異常時(shí),腳本delay一個(gè)x秒的延時(shí),等待主庫2同步完數(shù)據(jù)之后,再將db.kg.org解析到ip2;
- 應(yīng)用程序以內(nèi)網(wǎng)域名進(jìn)行重連,即可自動連接到ip2主庫,并保證了數(shù)據(jù)的一致性;
畫外音:本質(zhì)上,這是一個(gè)可用性與一致性的折衷。
總結(jié)
MySQL主庫高可用,主庫一致性,一些小技巧:
- 雙主同步是一種常見的保證寫庫高可用的方式;
- 設(shè)置相同步長,不同初始值,可以避免auto increment生成沖突主鍵;
- 不依賴數(shù)據(jù)庫,業(yè)務(wù)調(diào)用方自己生成全局唯一ID是一個(gè)好方法;
- 雙主保證寫庫高可用,只有一個(gè)寫庫提供服務(wù),并不能完全保證一致性;
- 內(nèi)網(wǎng)DNS探測,可以實(shí)現(xiàn)在主庫1出現(xiàn)問題后,延時(shí)一個(gè)時(shí)間,再進(jìn)行主庫切換,以保證數(shù)據(jù)一致性,但犧牲了幾秒鐘的高可用;
知其然,知其所以然。
思路比結(jié)論更重要。