天天看點

PostgreSQL 10.0 邏輯複制原理與最佳實踐

postgresql , logical replication , 邏輯複制 , 最佳實踐

postgresql 從2010年釋出的9.0開始支援流式實體複制,備庫可以作為隻讀庫打開,提供給使用者使用。

1. 實體層面完全一緻,這是許多商業資料庫的慣用手段。例如oracle的dg。

2. 延遲低,事務執行過程中産生redo record,實時的在備庫apply,事務結束時,備庫立馬能見到資料。不論事務多大,都一樣。

3. 實體複制的一緻性、可靠性達到了金融級的需求,不必擔心資料邏輯層面不一緻。

但是實體複制要求主備塊級完全一緻,是以有一些無法覆寫的應用場景,例如備庫不僅要隻讀,還要可寫。又比如備庫不需要完全和主庫一緻,隻需要複制部分資料,或者備庫要從多個資料源複制資料,等等。

1. 資料庫執行個體的部分,例如單個資料庫或者某些表的複制需求。

例如某個遊戲業務,賬号體系是一套資料庫,如果全國各地有多個接入點,全部都連到中心資料庫進行認證可能不太科學。那麼就希望将登陸需要用到的一些資料表同步到多個資料中心,而不是整個資料庫執行個體。

2. 資料到達subcriber後,針對不同資料,設定觸發器。

3. 将多個資料庫執行個體的資料,同步到一個目标資料庫。

例如多個資料庫同步到一個大的資料倉庫。

4. 在不同的資料庫版本之間,複制資料

5. 将一個資料庫執行個體的不同資料,複制到不同的目标庫。

例如省級資料庫的資料,按地區劃分,分别複制到不同的地區。

6. 在多個資料庫執行個體之間,共享部分資料。

例如某個業務按使用者id哈希,拆分成了8個資料庫,但是有些小的次元表,需要在多個資料庫之間共享。

以上場景是實體複制無法覆寫的。

邏輯複制應運而生,實際上,從2014年釋出的9.4版本開始,postgresql就支援邏輯複制了,隻是一直沒有将其引入核心。

2017年即将釋出的10.0,将會在核心層面支援基于redo流的邏輯複制。

另一個好消息是,你可以針對同一個資料庫執行個體,同時使用邏輯複制和實體複制,因為他們都是基于redo的。

下面我們來看一下邏輯複制的概念、架構、監控、安全、最佳實踐。

postgresql 邏輯複制是事務級别的複制,引入了幾個概念

釋出者指資料上遊節點,你需要将哪些表釋出出去?

上遊節點需要配置這些東西

1. 需要将資料庫的redo的wal_level配置為logical。

2. 需要釋出邏輯複制的表,必須配置表的replica identity,即如何标示老的記錄。

被複制的表,建議有pk限制。

解釋

什麼是system table?

指catalog或者user_catalog_table = true的表,不允許修改列的資料類型。

3. output plugin

釋出者還需要一個output plugin,将redo按釋出的定義,解析成需要的格式,等待訂閱者的訂閱。

<a href="https://www.postgresql.org/docs/devel/static/logicaldecoding-output-plugin.html">https://www.postgresql.org/docs/devel/static/logicaldecoding-output-plugin.html</a>

是不是有點像這個呢?

<a href="https://github.com/digoal/blog/blob/master/201408/20140828_01.md">《postgresql 閃回 - flash back query emulate by trigger》</a>

建立釋出

修改釋出

1. 目前僅僅支援釋出表,不允許釋出其他對象。

2. 同一張表,可以釋出多次。

3. 在同一個資料庫中,可以建立多個publication,但是不能重名,通過系統表檢視已建立的publication

4. 允許使用all tables釋出所有表。

5. 一個publication允許有多個訂閱者。

6. 目前publication僅支援insert, update, delete。

7. 允許釋出時,選擇釋出insert、update、delete,比如隻釋出insert,而不釋出update, delete。

8. 當釋出了表的update, delete時,表必須設定replica identity,即如何标示old tuple,通過pk或者uk或者full。如果設定了nothing,則執行update,delete時會報錯

報錯例子

9. create publication或者alter publication,釋出或者修改釋出内容中添加或者删除表時,都是事務級别,不會出現複制了部分事務的情況。 so the table will start or stop replicating at the correct snapshot once the transaction has committed。

10. 釋出者需要設定wal_level=logical,同時開啟足夠的worker,設定足夠大的replication slot,設定足夠多的sender。

因為每一個訂閱,都要消耗掉一個replication slot,需要消耗一個wal sender,一個worker程序。

釋出者的pg_hba.conf需要設定replication條目,允許訂閱者連接配接。

釋出者的資料庫中,必須有replication角色的使用者,或者超級使用者,并且訂閱者要使用它通過流複制協定連接配接到釋出者。

訂閱者,需要指定釋出者的連接配接資訊,以及 publication name,同時指定需要在publication資料庫中建立的slot name。

在同一個資料庫中,可以建立多個訂閱。

訂閱者和釋出者的角色可以同時出現在同一個執行個體的同一個資料庫中。

建立訂閱

修改訂閱

1. 訂閱者需要通過流複制協定連接配接到釋出者,同時需要在釋出者建立replication slot。

是以釋出者的pg_hba.conf中需要配置相應的replication條目,允許訂閱者通過流複制協定連接配接。

同時連接配接釋出者的使用者,必須具備replication權限,或者具備超級使用者權限。

2. 同一個資料庫中,可以建立多個subscription,這些subscription可以連自一個或多個釋出者。

3. 當同一個資料庫中有多個subscription時,如果這些subscriptions是來自同一個釋出者,那麼他們之間釋出的表不能重疊。

也就是說,訂閱者的同一張表,不能接受來自同一個源的多個釋出。

例如

釋出者

訂閱者

a表接受了同一個源的多次釋出,會報錯。

4. 每一個訂閱,都需要在釋出端建立一個slot,可以使用slot name = ?指定,或者預設為subscription name。

即使是同一個釋出端,隻要訂閱了多次,就需要建立多個slot,因為slot中記錄了同步的lsn資訊。

這種情況,對于這個訂閱者,建議合并成一個,例如

5. pg_dump導出資料庫邏輯資料時,預設不會導出subscription的定義,除非使用選項 --include-subscriptions

6. 在建立subscription或者alter subscription時,可以使用enable來啟用該訂閱,或者使用disable暫停該訂閱。

7. 如果要完全删除訂閱,使用drop subscription,注意,删除訂閱後,本地的表不會被删除,資料也不會清除,僅僅是不在接收該訂閱的上遊資訊。

這個也很好了解,因為同一個表可能接收多個訂閱。删訂閱和删表是兩碼事。

8. 删除訂閱後,如果要重新使用該訂閱,資料需要resync,比如訂閱的上遊節點有100萬資料,resync會将這100萬資料同步過來。随後進入增量同步。

将來10.0正式釋出時,也許會提供一個選項,選擇要不要resync。

(目前來說,一次訂閱,意味着這些被訂閱的表會和釋出端一模一樣(隻要釋出端釋出了insert,update,delete語句)。如果釋出端隻釋出了insert,那麼源表的update和delete不會被訂閱)

9. 訂閱時,不會自動建立釋出端的表,是以表需要在訂閱端先建立好。

将來10.0正式釋出時,也許會填補這個功能。

目前釋出端和訂閱端的表定義必須完全一緻,包括

schema,表名必須一緻。

字段名和字段類型必須一緻。

字段順序可以不一緻。

除了表,其他對象都不能被訂閱,例如你不能将表訂閱到一張視圖中。

10. 必須使用超級使用者建立訂閱

邏輯複制,本質上是事務層級的複制,需要在訂閱端執行sql。

如果訂閱端執行sql失敗(或者說引發了任何錯誤,包括限制等),都會導緻該訂閱暫停。

注意,update, delete沒有比對的記錄時,不會報錯,也不會導緻訂閱暫停。

使用者可以在訂閱端資料庫日志中檢視錯誤原因。

1. 通過修改訂閱端的資料,解決沖突。例如insert違反了唯一限制時,可以删除訂閱端造成唯一限制沖突的記錄先delete掉。然後使用alter subscription name enable讓訂閱繼續。

2. 在訂閱端調用pg_replication_origin_advance(node_name text, pos pg_lsn)函數,node_name就是subscription name,pos指重新開始的lsn,進而跳過有沖突的事務。

目前的lsn通過pg_replication_origin_status.remote_lsn檢視。

<a href="https://www.postgresql.org/docs/devel/static/view-pg-replication-origin-status.html">https://www.postgresql.org/docs/devel/static/view-pg-replication-origin-status.html</a>

PostgreSQL 10.0 邏輯複制原理與最佳實踐
PostgreSQL 10.0 邏輯複制原理與最佳實踐

1. 在建立subscription後,訂閱者會在釋出端建立一個快照,同時将釋出端的資料,在同一個快照内的視角,發送給訂閱端。

例如訂閱了釋出端的a,b,c三張表,那麼這三張表的目前快照下的資料,會發送給訂閱端。

2. 訂閱端接收完快照後,釋出端會從快照的這個lsn開始,從wal(redo)日志中,根據釋出定義的表以及過濾條件(insert\update\delete),按事務組裝複制的消息包,通過流複制協定發送給訂閱端的apply(wal receiver)程序。

3. 訂閱端接收到消息包之後,對于同一個訂閱(wal reciever或applyer程序)來說,會按照事務的先後順序,按事務apply。是以在訂閱端,apply也是事務一緻的。

将來可能會考慮組複制,提高并發性。

ps

其實你可以把不同的表分别放在不同的訂閱中,這樣就是并行的了。但是消耗的wal sender程序與連接配接會多一些。

4. 在訂閱端,wal receiver(applyer)的session_replication_role會設定為replica。

這個影響資料庫的trigger和rule。

參考

<a href="https://github.com/digoal/blog/blob/master/201506/20150615_01.md">《postgresql trigger/rule based replication configure, disable/enable [ replica | always ] trigger | rule》</a>

<a href="https://github.com/digoal/blog/blob/master/201102/20110209_01.md">《can session_replication_role used like mysql's blackhole engine?》</a>

邏輯複制依舊使用的是2010年推出的流複制協定,是以監控手段差别不大。

訂閱端視圖pg_stat_subscription

每一個subcription有一條記錄,一個訂閱可能有多個active subscription workers。

對于一個已激活(enabled)的訂閱,對應有1個apply程序,是以在這個視圖中有一條記錄。

一個暫停或者crash的訂閱,在這個視圖中不會有記錄。

釋出端

1. 必須設定pg_hba.conf,允許訂閱端通過流複制連接配接釋出端

2. wal_level必須設定為logical,記錄邏輯複制的一些額外資訊

3. 訂閱端配置的conninfo中,釋出端的角色必須具備replication權限,或者超級使用者權限

4. 使用某個使用者在某個資料庫中建立publication,這個使用者必須對該資料庫具備create權限。

訂閱端

1. 訂閱端建立subscription的使用者,必須是超級使用者

權限檢測僅僅在連接配接釋出端的時候,後期不會檢測,比如從釋出端擷取資料,或者apply資料時,不再檢測是否為超級使用者。

1. wal_level=logical

2. max_replication_slots,每一個訂閱需要消耗一個slot,每一個指定了slot的流式實體複制也要消耗一個slot。

3. max_wal_senders,每一個slot要使用一個wal sender,每一個流式實體複制也要使用一個wal sender。

4. max_worker_processes,必須大于等于max_wal_senders加并行計算程序,或者其他插件需要fork的程序數。

1. max_replication_slots,大于等于該執行個體總共需要建立的訂閱數

2. max_logical_replication_workers,大于等于該執行個體總共需要建立的訂閱數

3. max_worker_processes, 大于等于max_logical_replication_workers + 1 + cpu并行計算 + 其他插件需要fork的程序數.

1. 部署postgresql 10.0

2. 規劃資料目錄

3. 配置環境變量

4. 初始化資料庫

5. 配置postgresql.conf

6. 配置pg_hba.conf

7. 配置釋出端

檢視目前資料庫有哪些釋出

訂閱端建立成功後,可以檢視釋出端的slot

8. 配置訂閱端

檢視整個資料庫叢集有哪些訂閱

9. 小事務壓測

觀測延遲

釋出端decode, output plugin壓力較大,寫入tps達到18萬時,延遲較嚴重

訂閱端還可以檢視pg_replication_origin_status,

remote_lsn指已複制到訂閱者的釋出者的lsn。

local_lsn指本地已持久化redo的lsn,隻有在這個lsn之前的dirty page才能flush到磁盤。

對應的資料結構

延遲較大

實體流複制幾乎0延遲,而邏輯複制帶來的延遲是比較大的。

10. 大事務壓測

實際上postgresql的邏輯複制也是流式的,事務沒有結束時,釋出端事務過程中産生的變更會實時的同步并在訂閱端apply。

但是邏輯複制始終是基于行的,延遲和塊級複制還是差很大。

訂閱端如果開啟了異步送出,那麼哪些髒頁(shared buffer)能flush到磁盤呢?

檢視pg_replication_origin_status,

local_lsn指本地已持久化redo的lsn,隻有在這個lsn之前的dirty page才能flush到磁盤。(這樣做可以保證可靠性和一緻性)

1. 如果被訂閱的資料有主外鍵限制,請将其作為一個訂閱。否則可能會有限制的問題

2. 因為邏輯複制在訂閱端實際上是邏輯寫操作,請盡量避免鎖沖突。比如truncate , alter table.

3. 在訂閱端,delete, update 不存在記錄,不報錯

<a href="https://www.postgresql.org/docs/devel/static/logical-replication.html">https://www.postgresql.org/docs/devel/static/logical-replication.html</a>