天天看點

PostgreSQL如何保障資料的一緻性 PostgreSQL如何保障資料的一緻性

玩過MySQL的人應該都知道,由于MySQL是邏輯複制,從根子上是難以保證資料一緻性的。玩MySQL玩得好的專家們知道有哪些坑,應該怎麼回避。為了保障MySQL資料的一緻性,甚至會動用paxos,raft之類的終極武器建立嚴密的防護網。如果不會折騰,真不建議用MySQL存放一緻性要求高的資料。

PostgreSQL由于是實體複制,天生就很容易保障資料一緻性,而且回放日志的效率很高。 我們實測的結果,MySQL5.6的寫qps超過4000備機就跟不上主機了;PG 8核虛機的寫qps壓到2.3w備機依然毫無壓力,之是以隻壓到2.3w是因為主節點的CPU已經跑滿壓不上去了。

那麼相比于MySQL,PG有哪些措施用于保障資料的一緻性呢?

PG的備庫處于恢複狀态,不斷的回放主庫的WAL,不具備寫能力。

而MySQL的單寫是通過在備機上設定read_only或super_read_only實作的,DBA在維護資料庫的時候可能需要解除隻讀狀态,在解除期間發生點什麼,或自動化腳本出個BUG都可能引起主備資料不一緻。甚至備庫在和主庫建立複制關系之前資料就不是一緻的,MySQL的邏輯複制并不阻止兩個不一緻的庫建立複制關系。

PG的備庫以和主庫完全相同順序串行化的回放WAL日志。

MySQL中由于存在組送出,以及為了解決單線程複制回放慢而采取的并行複制,不得不在複制延遲和資料一緻性之前做取舍。 并且這裡牽扯到的邏輯很複雜,已經檢出了很多的BUG;因為邏輯太複雜了,未來出現新BUG的機率應該相對也不會低。

PG通過synchronous_commit參數設定複制的持久性級别。

下面這些級别越往下越嚴格,從remote_write開始就可以保證單機故障不丢資料了。

off

local

remote_write

on

remote_apply

MySQL通過半同步複制在很大程度上降低了failover丢失資料的機率。MySQL的主庫在等待備庫的應答逾時時半同步複制會自動降級成異步,此時發生failover會丢失資料。 

WAL檔案頭中儲存了資料庫執行個體的唯一辨別(Database system identifier),可以確定不同資料庫執行個體産生的WAL可以差別開,同一叢集的主備庫擁有相同唯一辨別。

PG提升備機的時候會同時提升備機的時間線,時間線是WAL檔案名的一部分,通過時間線就可以把新主和舊主産生的WAL差別開。 (如果同時提升2個以上的備機,就無法這樣區分WAL了,當然這種情況正常不應該發生。)

WAL記錄在整個WAL邏輯資料流中的偏移(lsn)作為WAL的辨別。

以上3者的聯合可唯一辨別WAL記錄

MySQL5.6開始支援GTID了,這對保障資料一緻性是個極大的進步。對于邏輯複制來說,GITD确實做得很棒,但是和PG實體複制的時間線+lsn相比起來就顯得太複雜了。時間線+lsn隻是2個數字而已;GTID卻是一個複雜的集合,而且需要定期清理。

MySQL的GTID是長這樣的:

在初始化資料庫時,使用-k選項可以打開資料檔案的checksum功能。(建議打開,造成的性能損失很小) 如果底層存儲出現問題,可通過checksum及時發現。

MySQL也隻支援資料檔案的checksum,沒什麼差別。

每條WAL記錄裡都儲存了checksum資訊,如果WAL的傳輸存儲過程中出現錯誤可及時發現。

MySQL的binlog記錄裡也包含checksum,沒什麼差別。

WAL可能來自歸檔的拷貝或人為拷貝,PG在讀取WAL檔案時會進行驗證,可防止DBA弄錯檔案。

檢查WAL檔案頭中記錄的資料庫執行個體的唯一辨別是否和本資料庫一緻

檢查WAL頁面頭中記錄的頁位址是否正确

其它檢查

上面第2項檢查的作用主要是應付WAL再利用。

PG在清理不需要的WAL檔案時,有2種方式,1是删除,2是改名為未來的WAL檔案名防止頻繁建立檔案。

看下面的例子,000000030000000000000015及以後的WAL檔案的修改日期比前面的WAL還要老,這些WAL檔案就是被重命名了的。

由于有上面的第2項檢查,如果讀到了這些WAL檔案,可以立即識别出來。

MySQL的binlog檔案名一般是長下面這樣的,從binlog檔案名上看不出任何和GTID的映射關系。

不同機器上産生的binlog檔案可能同名,如果要管理多套MySQL,千萬别拿錯檔案。因為MySQL是邏輯複制,這些binlog檔案就像SQL語句一樣,拿到哪裡都可以執行。