PolarDB提供了最終一致性和會(huì)話讀一致性兩種一致性級(jí)別,滿足您在不同場(chǎng)景下對(duì)一致性級(jí)別的要求。

PolarDB架構(gòu)

PolarDB是一個(gè)由多個(gè)節(jié)點(diǎn)構(gòu)成的數(shù)據(jù)庫(kù)集群,一個(gè)主節(jié)點(diǎn),多個(gè)讀節(jié)點(diǎn)。對(duì)外默認(rèn)提供兩個(gè)地址,一個(gè)是集群地址,一個(gè)是主地址,推薦使用集群地址,因?yàn)樗邆渥x寫分離功能可以把所有節(jié)點(diǎn)的資源整合到一起對(duì)外提供服務(wù)。

PolarDB架構(gòu)

PostgreSQL讀寫分離解決和引入

PostgreSQL具有主從復(fù)制簡(jiǎn)單易用的特點(diǎn),通過(guò)把主庫(kù)的WAL異步地傳輸?shù)絺鋷?kù)并實(shí)時(shí)應(yīng)用,一方面可以實(shí)現(xiàn)高可用,另一方面?zhèn)鋷?kù)也可以提供查詢,來(lái)減輕對(duì)主庫(kù)的壓力。

雖然備庫(kù)可以提供查詢,但存在兩個(gè)問(wèn)題:

  • 問(wèn)題1:主庫(kù)和備庫(kù)一般提供兩個(gè)不同的訪問(wèn)地址,應(yīng)用程序端需要選擇使用哪一個(gè),對(duì)應(yīng)用有侵入。
  • 問(wèn)題2:PostgreSQL的復(fù)制是異步的。客戶端提交commit并且成功之后,數(shù)據(jù)可能還沒(méi)有同步到只讀節(jié)點(diǎn)。因此備庫(kù)的數(shù)據(jù)并不是最新的,無(wú)法保證查詢的一致性。

為了解決問(wèn)題1,PolarDB引入了讀寫分離代理。代理會(huì)偽造成PostgreSQL與應(yīng)用程序建立連接,解析發(fā)送進(jìn)來(lái)的每一條SQL,如果是UPDATE、DELETE、INSERT、CREATE等寫操作則直接發(fā)往主節(jié)點(diǎn),如果是SELECT則發(fā)送到只讀節(jié)點(diǎn)。

PolarDB PG讀寫分離WAL

但是問(wèn)題2,延遲導(dǎo)致的查詢不一致還是沒(méi)有解決,使用時(shí)就不可避免遇到備庫(kù)SELECT查詢數(shù)據(jù)不一致的現(xiàn)象(因?yàn)橹鱾溆醒舆t)。PostgreSQL負(fù)載低的時(shí)候延遲可以控制在5秒內(nèi),但當(dāng)負(fù)載很高時(shí),尤其是對(duì)大表做DDL(比如加字段)或者大批量插入的時(shí)候,延遲會(huì)非常嚴(yán)重。

PolarDB最終一致性和會(huì)話一致性

  • 最終一致性:PolarDB采用異步物理復(fù)制方式在主庫(kù)和只讀庫(kù)間做數(shù)據(jù)同步, 在主庫(kù)更新后,相關(guān)的更新會(huì)apply到只讀庫(kù),具體的延遲時(shí)間與寫入壓力有關(guān), 一般在ms級(jí)別, 通過(guò)異步復(fù)制的方式實(shí)現(xiàn)主庫(kù)和只讀庫(kù)之間的最終數(shù)據(jù)一致。
  • 會(huì)話一致性:為了解決最終一致性會(huì)出現(xiàn)的查詢不一致,PolarDB利用自身物理復(fù)制速度快的優(yōu)點(diǎn),將查詢發(fā)給已經(jīng)更新了數(shù)據(jù)的只讀節(jié)點(diǎn),詳細(xì)原理請(qǐng)參見(jiàn)實(shí)現(xiàn)原理。

PolarDB讀寫分離的會(huì)話一致性

PolarDB是讀寫分離的架構(gòu),傳統(tǒng)的讀寫分離都只提供最終一致性的保證,主從復(fù)制延遲會(huì)導(dǎo)致從不同節(jié)點(diǎn)查詢到的結(jié)果不同,比如一個(gè)會(huì)話內(nèi)連續(xù)執(zhí)行以下QUERY:

INSERT INTO t1(id, price) VALUES(111, 96);
UPDATE t1 SET price = 100 WHERE id=111;
SELECT price FROM t1;

在讀寫分離的下,最后一個(gè)查詢的結(jié)果是不確定的,因?yàn)樽x會(huì)發(fā)到只讀庫(kù),在執(zhí)行SELECT時(shí)之前的更新是否同步到了只讀庫(kù)時(shí)不確定的,因此結(jié)果也是不確定的。因?yàn)榇嬖诓樵兘Y(jié)果不確定的問(wèn)題,所以就要求應(yīng)用程序去適應(yīng)最終一致性,而一般的解決方法是: 將業(yè)務(wù)做拆分,有高一致性要求的請(qǐng)求直連到主庫(kù),可以接受最終一致性的部分走讀寫分離。這樣會(huì)增加應(yīng)用開(kāi)發(fā)的負(fù)擔(dān),還會(huì)增大主庫(kù)的壓力,影響讀寫分離的效果。

為了解決這個(gè)問(wèn)題,PolarDB提供了會(huì)話一致性或者說(shuō)因果一致性的保證,會(huì)話一致性即保證同一個(gè)會(huì)話內(nèi),后面的請(qǐng)求一定能夠看到此前更新所產(chǎn)生版本的數(shù)據(jù)或者比這個(gè)版本更新的數(shù)據(jù),保證單調(diào)性,就很好的解決了上面這個(gè)例子里的問(wèn)題。

實(shí)現(xiàn)原理

實(shí)現(xiàn)原理

PolarDB鏈路的中間層做讀寫分離的同時(shí),中間層會(huì)跟蹤各個(gè)節(jié)點(diǎn)已經(jīng)apply了的redolog位點(diǎn)并產(chǎn)生LSN,同時(shí)每次更新時(shí)會(huì)記錄此次更新的位點(diǎn)為Session LSN,當(dāng)有新請(qǐng)求到來(lái)時(shí)PolarDB會(huì)比較Session LSN 和當(dāng)前各個(gè)節(jié)點(diǎn)的LSN,僅將請(qǐng)求發(fā)往LSN >= Session LSN的節(jié)點(diǎn),從而保證了會(huì)話一致性;表面上看該方案可能導(dǎo)致主庫(kù)壓力大,但是因?yàn)镻olarDB是物理復(fù)制,速度極快,在上述場(chǎng)景中,當(dāng)更新完成后,返回客戶端結(jié)果時(shí)復(fù)制就同步在進(jìn)行,而當(dāng)下一個(gè)讀請(qǐng)求到來(lái)時(shí)主從極有可能已經(jīng)完成,然后大多數(shù)應(yīng)用場(chǎng)景都是讀多寫少,所以經(jīng)驗(yàn)證在該機(jī)制下即保證了會(huì)話一致性,也保證了讀寫分離負(fù)載均衡的效果。

一致性級(jí)別選擇最佳實(shí)踐

PolarDB會(huì)話一致性,該級(jí)別對(duì)性能影響很小而且能滿足絕大多數(shù)應(yīng)用場(chǎng)景的需求。

如果您有有不同會(huì)話間一致性需求的可以選擇以下方案:

使用Hint將特定查詢強(qiáng)制發(fā)到主庫(kù)。

eg: /*FORCE_MASTER*/ select * from user;