PostgreSQL postgres_fdw

2021-09-16 17:17 更新
F.33.1. postgres_fdw 的 FDW 選項(xiàng)
F.33.2. 連接管理
F.33.3. 事務(wù)管理
F.33.4. 遠(yuǎn)程查詢優(yōu)化
F.33.5. 遠(yuǎn)程查詢執(zhí)行環(huán)境
F.33.6. 跨版本兼容性
F.33.7. 例子

postgres_fdw模塊提供了外部數(shù)據(jù)包裝器postgres_fdw,它可以被用來訪問存儲在外部PostgreSQL服務(wù)器中的數(shù)據(jù)。

這個模塊提供的功能大體上覆蓋了較老的dblink模塊的功能。但是postgres_fdw提供了更透明且更兼容標(biāo)準(zhǔn)的語法來訪問遠(yuǎn)程表,并且可以在很多情況下給出更好的性能。

要使用postgres_fdw來為遠(yuǎn)程訪問做準(zhǔn)備:

  1. 使用CREATE EXTENSION來安裝postgres_fdw擴(kuò)展。

  2. 使用CREATE SERVER創(chuàng)建一個外部服務(wù)器對象,它用來表示你想連接的每一個遠(yuǎn)程數(shù)據(jù)庫。指定除了userpassword之外的連接信息作為該服務(wù)器對象的選項(xiàng)。

  3. 使用CREATE USER MAPPING創(chuàng)建一個用戶映射,每一個用戶映射都代表你想允許一個數(shù)據(jù)庫用戶訪問一個外部服務(wù)器。指定遠(yuǎn)程用戶名和口令作為用戶映射的userpassword選項(xiàng)。

  4. 為每一個你想訪問的遠(yuǎn)程表使用CREATE FOREIGN TABLE或者IMPORT FOREIGN SCHEMA創(chuàng)建一個外部表。外部表的列必須匹配被引用的遠(yuǎn)程表。但是,如果你在外部表對象的選項(xiàng)中指定了正確的遠(yuǎn)程名稱,你可以使用不同于遠(yuǎn)程表的表名和/或列名。

現(xiàn)在你只需要從一個外部表SELECT來訪問存儲在它的底層的遠(yuǎn)程表中的數(shù)據(jù)。你也可以使用INSERT、UPDATEDELETE修改遠(yuǎn)程表(當(dāng)然,你在你的用戶映射中已經(jīng)指定的遠(yuǎn)程用戶必須具有做這些事情的權(quán)限)。

注意當(dāng)前postgres_fdw缺少對于帶ON CONFLICT DO UPDATE子句的INSERT語句的支持。不過,它支持ON CONFLICT DO NOTHING子句,已提供的唯一索引推斷說明會被省略。 另請注意postgres_fdw支持 UPDATE語句在分區(qū)表上執(zhí)行的行移動調(diào)用,但是它目前不處理這樣的情況:選擇將移動行插入到的遠(yuǎn)程分區(qū)也是稍后將更新的UPDATE目標(biāo)分區(qū)。

我們通常推薦一個外部表的列被聲明為與被引用的遠(yuǎn)程表列完全相同的數(shù)據(jù)類型和排序規(guī)則(如果可用)。盡管postgres_fdw目前已經(jīng)能夠容忍在需要時執(zhí)行數(shù)據(jù)類型轉(zhuǎn)換,但是當(dāng)類型或排序規(guī)則不匹配時可能會發(fā)生奇怪的語義異常,因?yàn)檫h(yuǎn)程服務(wù)器解釋WHERE子句時可能會與本地服務(wù)器有所不同。

注意一個外部表可以被聲明比底層的遠(yuǎn)程表較少的列,或者使用一種不同的列序。與遠(yuǎn)程表的列匹配是通過名字而不是位置進(jìn)行的。

F.33.1. postgres_fdw 的 FDW 選項(xiàng)

F.33.1.1. 連接選項(xiàng)

一個使用postgres_fdw外部數(shù)據(jù)包裝器的外部服務(wù)器可以使用和libpq在連接字符串中能接受的選項(xiàng),如第 33.1.2 節(jié)所述,除了這些選項(xiàng)不被允許或有特殊處理:

  • user、passwordsslpassword(應(yīng)該在用戶映射中指定這些,或者使用服務(wù)文件)

  • client_encoding(這是自動從本地服務(wù)器編碼設(shè)置)

  • fallback_application_name(總是設(shè)置為postgres_fdw

  • sslkeysslcert - 這些可能出現(xiàn)在either or both連接和用戶映射中。如果兩者都存在,則用戶映射設(shè)置會覆蓋連接設(shè)置。

只有超級用戶可以使用sslcertsslkey設(shè)置創(chuàng)建或修改用戶映射。

只有超級用戶可以在不經(jīng)過口令認(rèn)證的情況下連接到外部服務(wù)器,因此應(yīng)總是為屬于非超級用戶的用戶映射指定password選項(xiàng)。

超級用戶可以通過設(shè)置用戶映射選項(xiàng)password_required 'false'在每個用戶映射的基礎(chǔ)上覆蓋此檢查,例如,

ALTER USER MAPPING FOR some_non_superuser SERVER loopback_nopw
OPTIONS (ADD password_required 'false');

為了防止非特權(quán)用戶利用正在運(yùn)行的 postgres 服務(wù)器升級到超級用戶權(quán)限的 unix 用戶的身份驗(yàn)證權(quán)限,只有超級用戶可以在用戶映射上設(shè)置此選項(xiàng)。

需要注意確保這不允許映射用戶能夠根據(jù) CVE-2007-3278 和 CVE-2007-6601 作為超級用戶連接到映射數(shù)據(jù)庫。 不要在public角色上設(shè)置password_required=false。 請記住,映射的用戶可能會使用 postgres 服務(wù)器運(yùn)行的系統(tǒng)用戶的 unix 主目錄中的任何客戶端證書, .pgpass、 .pg_service.conf等。 他們還可以使用由諸如peerident身份驗(yàn)證等身份驗(yàn)證模式授予的任何信任關(guān)系。

F.33.1.2. 對象名稱選項(xiàng)

這些選項(xiàng)可以被用來控制使用在被發(fā)送到遠(yuǎn)程PostgreSQL服務(wù)器的 SQL 語句中使用的名稱。當(dāng)一個外部表被使用不同于底層遠(yuǎn)程表的名稱創(chuàng)建時,就需要這些選項(xiàng)。

schema_name

這個選項(xiàng)給出用在遠(yuǎn)程服務(wù)器之上的外部表的模式名稱,它可以為一個外部表指定。如果這個選項(xiàng)被忽略,該外部表的模式名稱將被使用。

table_name

這個選項(xiàng)給出用在遠(yuǎn)程服務(wù)器上的外部表給出表名,它可以為一個外部表指定。如果這個選項(xiàng)被忽略,該外部表的名字將被使用。

column_name

這個選項(xiàng)給出用在遠(yuǎn)程服務(wù)器上列的列名,它可以為一個外部表的一個列指定。如果這個選項(xiàng)被忽略,該列的名字將被使用。

F.33.1.3. 代價估計(jì)選項(xiàng)

postgres_fdw通過在遠(yuǎn)程服務(wù)器上執(zhí)行查詢來檢索遠(yuǎn)程數(shù)據(jù),因此理想的掃描一個外部表的估計(jì)代價應(yīng)該是在遠(yuǎn)程服務(wù)器上完成它的花銷,外加一些通信開銷。得到這樣一個估計(jì)的最可靠的方法是詢問遠(yuǎn)程服務(wù)器并加上一些通信開銷 — 但是對于簡單查詢,不值得為獲得一個代價估計(jì)而額外使用一次遠(yuǎn)程查詢。因此postgres_fdw提供了下列選項(xiàng)來控制如何完成代價估計(jì):

use_remote_estimate

這個選項(xiàng)控制postgres_fdw是否發(fā)出EXPLAIN命令來獲得代價估計(jì),它可以為一個外部表或一個外部服務(wù)器指定。一個外部表的設(shè)置會覆蓋它的服務(wù)器的任何設(shè)置,但是只用于這個表。默認(rèn)值是false。

fdw_startup_cost

這個選項(xiàng)是一個要被加到那個服務(wù)器上所有外部表掃描的估計(jì)啟動代價的數(shù)字值。這表示建立一個連接、在遠(yuǎn)端解析和規(guī)查詢的額外負(fù)荷等。默認(rèn)值是100

fdw_tuple_cost

這個選項(xiàng)是一個數(shù)字值,它被用作那個服務(wù)器上外部表掃描的每元組額外代價,它可以為一個外部服務(wù)器指定。這表示在服務(wù)器之間數(shù)據(jù)傳輸?shù)念~外負(fù)荷。你可以增加或減少這個數(shù)來反映到遠(yuǎn)程服務(wù)器更高或更低的網(wǎng)絡(luò)延遲。默認(rèn)值是0.01。

當(dāng)use_remote_estimate為真時,postgres_fdw從遠(yuǎn)程服務(wù)器獲得行計(jì)數(shù)和代價估計(jì),然后在代價估計(jì)上加上fdw_startup_costfdw_tuple_cost。當(dāng)use_remote_estimate為假時, postgres_fdw執(zhí)行本地行計(jì)數(shù)和代價估計(jì),并且接著在代價估計(jì)上加上fdw_startup_costfdw_tuple_cost。這種本地估計(jì)不會很準(zhǔn)確,除非有遠(yuǎn)程表統(tǒng)計(jì)數(shù)據(jù)的本地拷貝可用。在外部表上運(yùn)行ANALYZE是更新本地統(tǒng)計(jì)數(shù)據(jù)的方法,這將執(zhí)行遠(yuǎn)程表的一次掃描并接著計(jì)算和存儲統(tǒng)計(jì)數(shù)據(jù),就好像表在本地一樣。保留本地統(tǒng)計(jì)數(shù)據(jù)可能是一種有用的方法來減少一個遠(yuǎn)程表的預(yù)查詢規(guī)劃負(fù)荷 — 但是如果遠(yuǎn)程表被頻繁更新,本地統(tǒng)計(jì)數(shù)據(jù)將很快就被廢棄。

F.33.1.4. 遠(yuǎn)程執(zhí)行選項(xiàng)

默認(rèn)情況下,只有使用了內(nèi)建操作符和函數(shù)的WHERE子句才會被考慮在遠(yuǎn)程服務(wù)器上執(zhí)行。涉及非內(nèi)建函數(shù)的子句將會在取完行后在本地進(jìn)行檢查。如果這類函數(shù)在遠(yuǎn)程服務(wù)器上可用并且可以用來產(chǎn)生和本地執(zhí)行時一樣的結(jié)果,則可以通過將這種WHERE子句發(fā)送到遠(yuǎn)程執(zhí)行來提高性能。可以用下面的選項(xiàng)控制這種行為:

extensions

這個選項(xiàng)是一個用逗號分隔的已安裝的PostgreSQL擴(kuò)展名稱列表,這些擴(kuò)展在本地和遠(yuǎn)程服務(wù)器上具有兼容的版本。屬于一個該列表中擴(kuò)展的 immutable 函數(shù)和操作符將被考慮轉(zhuǎn)移到遠(yuǎn)程服務(wù)器上執(zhí)行。這個選項(xiàng)只能為外部服務(wù)器指定,無法逐個表指定。

在使用extensions選項(xiàng)時,用戶應(yīng)該負(fù)責(zé)確保列出的擴(kuò)展在本地和遠(yuǎn)程服務(wù)器上都存在且保持一致。否則,遠(yuǎn)程查詢可能失敗或者行為異常。

fetch_size

這個選項(xiàng)指定在每次獲取行的操作中postgres_fdw應(yīng)該得到的行數(shù)??梢詾橐粋€外部表或者外部服務(wù)器指定這個選項(xiàng)。在表上指定的選項(xiàng)將會覆蓋在服務(wù)器級別上指定的選項(xiàng)。默認(rèn)值為100。

F.33.1.5. 可更新性選項(xiàng)

默認(rèn)情況下,所有使用postgres_fdw的外部表都被假定是可更新的。這可以使用下列選項(xiàng)覆蓋:

updatable

這個選項(xiàng)控制postgres_fdw是否允許外部表被使用INSERT、UPDATEDELETE命令更新。它可以為一個外部表或一個外部服務(wù)器指定。一個表級選項(xiàng)會覆蓋一個服務(wù)器級選項(xiàng)。默認(rèn)值是true。

當(dāng)然,如果遠(yuǎn)程表實(shí)際上并非可更新的,將產(chǎn)生一個錯誤。這個選項(xiàng)的使用主要是允許在不查詢遠(yuǎn)程服務(wù)器的情況下在本地拋出錯誤。但是要注意information_schema視圖會根據(jù)這個選項(xiàng)的設(shè)置報告一個postgres_fdw外部表是可更新的(或者不可更新),而不需要遠(yuǎn)程服務(wù)器的任何檢查。

F.33.1.6. 導(dǎo)入選項(xiàng)

postgres_fdw能使用IMPORT FOREIGN SCHEMA導(dǎo)入外部表定義。這個命令會在本地服務(wù)器上創(chuàng)建外部表定義,這個定義能匹配存在于遠(yuǎn)程服務(wù)器上的表或者視圖。如果要被導(dǎo)入的遠(yuǎn)程表有用戶自定義數(shù)據(jù)類型的列,本地服務(wù)器上也必須具有相同名稱的兼容類型。

導(dǎo)入行為可以用下列選項(xiàng)自定義(在IMPORT FOREIGN SCHEMA命令中給出):

import_collate

這個選項(xiàng)控制是否在從外部服務(wù)器導(dǎo)入的外部表定義中包括列的COLLATE選項(xiàng)。默認(rèn)是true。如果遠(yuǎn)程服務(wù)器具有和本地服務(wù)器不同的排序規(guī)則名集合,可能需要關(guān)閉這個選項(xiàng),在不同的操作系統(tǒng)上運(yùn)行時很可能就是這樣。

import_default

這個選項(xiàng)控制是否在從外部服務(wù)器導(dǎo)入的外部表定義中包括列的DEFAULT表達(dá)式。默認(rèn)是false。如果啟用這個選項(xiàng),要當(dāng)心在遠(yuǎn)程服務(wù)器和本地服務(wù)器上計(jì)算表達(dá)式的方式不同,nextval()常會導(dǎo)致這類問題。如果導(dǎo)入的默認(rèn)值表達(dá)式使用了一個本地不存在的函數(shù)或者操作符,IMPORT將整個失敗。

import_not_null

這個選項(xiàng)控制是否在從外部服務(wù)器導(dǎo)入的外部表定義中包括列的NOT NULL約束。默認(rèn)是true。

注意除NOT NULL之外的約束將不會從遠(yuǎn)程表中導(dǎo)入。雖然PostgreSQL確實(shí)支持外部表上的CHECK約束,但不會自動導(dǎo)入它們,因?yàn)榇嬖诒镜睾瓦h(yuǎn)程服務(wù)器計(jì)算約束表達(dá)式方式不同的風(fēng)險。CHECK約束中的任何這類不一致都可能導(dǎo)致查詢優(yōu)化中很難檢測的錯誤。因此,如果你希望導(dǎo)入 CHECK約束,你必須手工來做,并且你應(yīng)該仔細(xì)地驗(yàn)證每一個這種約束的語義。有關(guān)處理外部表上CHECK約束的更多細(xì)節(jié),請見CREATE FOREIGN TABLE。

自動排除作為其他表的分區(qū)的表或外部表。分區(qū)表被導(dǎo)入,除非它們是其他表的分區(qū)。 由于所有數(shù)據(jù)都可以通過作為分區(qū)層次根的分區(qū)表來訪問, 所以這種方法應(yīng)該允許訪問所有數(shù)據(jù)而不創(chuàng)建額外的對象。

F.33.2. 連接管理

postgres_fdw在第一個使用關(guān)聯(lián)到外部服務(wù)器的外部表的查詢期間建立一個到外部服務(wù)器的連接。這個連接會被保持,并被重用于同一個會話中的后續(xù)查詢。但是,如果使用了多個用戶實(shí)體(用戶映射)來訪問外部服務(wù)器,會為每一個用戶映射建立一個連接。

F.33.3. 事務(wù)管理

在一個引用外部服務(wù)器上任何遠(yuǎn)程表的查詢期間,如果還沒有根據(jù)當(dāng)前的本地事務(wù)打開一個遠(yuǎn)程事務(wù),postgres_fdw將在遠(yuǎn)程服務(wù)器上打開一個事務(wù)。當(dāng)本地事務(wù)提交或中止時,遠(yuǎn)程事務(wù)也被提交或中止。保存點(diǎn)也相似地采用創(chuàng)建相應(yīng)的遠(yuǎn)程保存點(diǎn)來管理。

當(dāng)本地事務(wù)為SERIALIZABLE隔離級別時,遠(yuǎn)程事務(wù)使用SERIALIZABLE隔離級別;否則它使用REPEATABLE READ隔離級別。如果一個查詢在遠(yuǎn)程服務(wù)器上執(zhí)行多個表查詢,這種選擇保證它將為所有掃描得到快照一致的結(jié)果。一種后果是在單一事務(wù)中的后繼查詢將會看到來自遠(yuǎn)程服務(wù)器的相同數(shù)據(jù),即便由于其他活動在遠(yuǎn)程服務(wù)器上發(fā)生了其他并發(fā)更新。如果本地事務(wù)使用 SERIALIZABLEREPEATABLE READ隔離級別,這種行為也是可以預(yù)期的,但是對于一個READ COMMITTED本地事務(wù)它是奇怪的。一個未來的PostgreSQL發(fā)布可能會修改這些規(guī)則。

請注意postgres_fdw當(dāng)前不支持為兩階段提交準(zhǔn)備遠(yuǎn)程事務(wù)。

F.33.4. 遠(yuǎn)程查詢優(yōu)化

postgres_fdw嘗試優(yōu)化遠(yuǎn)程查詢來減少從外部服務(wù)器傳來的數(shù)據(jù)量。這可以通過把查詢的WHERE子句發(fā)送給遠(yuǎn)程服務(wù)器執(zhí)行來完成,并且還可以不檢索當(dāng)前查詢不需要的表列。為了降低查詢被誤執(zhí)行的風(fēng)險,除非WHERE子句使用的數(shù)據(jù)類型、操作符和函數(shù)都是內(nèi)建的或者屬于列在該外部服務(wù)器的extensions選項(xiàng)中的一個擴(kuò)展,將不會把 WHERE子句發(fā)送到遠(yuǎn)程服務(wù)器。這些子句中的操作符合函數(shù)也必須是IMMUTABLE。對于UPDATE或者DELETE查詢, 如果沒有不能發(fā)送給遠(yuǎn)程服務(wù)器的WHERE子句、 沒有查詢的本地連接、目標(biāo)表上沒有本地的行級BEFOREAFTER觸發(fā)器或存儲生成的列, 并且沒有來自父視圖的CHECK OPTION約束,postgres_fdw會嘗試通過將整個查詢發(fā)送給遠(yuǎn)程服務(wù)器來優(yōu)化查詢的執(zhí)行。在UPDATE中,賦值給目標(biāo)列的表達(dá)式只能使用內(nèi)建數(shù)據(jù)類型、IMMUTABLE操作符或者 IMMUTABLE操作符,這樣能降低查詢被誤執(zhí)行的風(fēng)險。

當(dāng)postgres_fdw碰到同一個外部服務(wù)器上的外部表之間的連接時,它會把整個連接發(fā)送給外部服務(wù)器,除非由于某些原因它認(rèn)為逐個從每一個表取得行的效率更高或者涉及的表引用屬于不同的用戶映射。在發(fā)送JOIN子句時,它也會采取和上述WHERE子句相同的預(yù)防措施。

實(shí)際被發(fā)送到遠(yuǎn)程服務(wù)器執(zhí)行的查詢可以使用EXPLAIN VERBOSE來檢查。

F.33.5. 遠(yuǎn)程查詢執(zhí)行環(huán)境

postgres_fdw開啟的遠(yuǎn)程會話中,search_path參數(shù)只被設(shè)置為pg_catalog,因此只有內(nèi)建對象可以在無模式限定時可見。這對于postgres_fdw本身產(chǎn)生的查詢來說不是問題,因?yàn)樗偸菚峁┻@樣的限定。不過,這可能會對在遠(yuǎn)程服務(wù)器上通過觸發(fā)器或者遠(yuǎn)程表上的規(guī)則執(zhí)行的函數(shù)帶來災(zāi)難。例如,如果一個遠(yuǎn)程表實(shí)際是一個視圖,任何在該視圖中使用的函數(shù)都將被在這個受限的搜索路徑中執(zhí)行。我們推薦在這類函數(shù)中用模式限定所有名稱,或者為這類函數(shù)附著 SET search_path選項(xiàng)(見CREATE FUNCTION)來建立它們所期望的搜索路徑環(huán)境。

postgres_fdw同樣為各種參數(shù)建立遠(yuǎn)程會話設(shè)置:

這些不如search_path有那么多問題,但是如果需要也可以使用函數(shù) SET選項(xiàng)來處理。

我們推薦通過更改這些參數(shù)的會話級設(shè)置來推翻這種行為,這很可能會導(dǎo)致postgres_fdw故障。

F.33.6. 跨版本兼容性

postgres_fdw能夠與最老是PostgreSQL 8.3 的遠(yuǎn)程服務(wù)器一起使用。只讀能力則最低可以在 8.1 中使用。但是一個限制是postgres_fdw通常假定不變的內(nèi)建函數(shù)和操作符是安全的,如果它們出現(xiàn)在一個外部表的WHERE子句中,它們可以發(fā)送給遠(yuǎn)程服務(wù)器執(zhí)行。因此,由于一個由于遠(yuǎn)程服務(wù)器的發(fā)布可能被發(fā)送給它來執(zhí)行而被增加的內(nèi)建函數(shù),會導(dǎo)致 function does not exist或一個類似的錯誤。這類錯誤可以通過重寫查詢來解決,例如通過嵌入在一個帶OFFSET 0的子SELECT中引用的外部表作為一種優(yōu)化墻,并且把出問題的函數(shù)或操作符放在子SELECT的外部。

F.33.7. 例子

這里是一個用postgres_fdw創(chuàng)建外部表的例子。首先安裝該擴(kuò)展:

CREATE EXTENSION postgres_fdw;

然后使用CREATE SERVER創(chuàng)建一個外部服務(wù)器。在這個例子中我們希望連接到一個位于主機(jī)192.83.123.89上并且監(jiān)聽5432端口的PostgreSQL服務(wù)器。在該遠(yuǎn)程服務(wù)器上要連接的數(shù)據(jù)庫名為 foreign_db

CREATE SERVER foreign_server
        FOREIGN DATA WRAPPER postgres_fdw
        OPTIONS (host '192.83.123.89', port '5432', dbname 'foreign_db');

需要用CREATE USER MAPPING定義一個用戶映射來標(biāo)識在遠(yuǎn)程服務(wù)器上使用哪個角色:

CREATE USER MAPPING FOR local_user
        SERVER foreign_server
        OPTIONS (user 'foreign_user', password 'password');

現(xiàn)在就可以使用CREATE FOREIGN TABLE創(chuàng)建外部表了。在這個例子中我們希望訪問遠(yuǎn)程服務(wù)器上名為some_schema.some_table的表。它的本地名稱是foreign_table

CREATE FOREIGN TABLE foreign_table ( id integer NOT NULL, data text ) SERVER foreign_server OPTIONS (schema_name 'some_schema', table_name 'some_table');

CREATE FOREIGN TABLE中聲明的列數(shù)據(jù)類型和其他性質(zhì)必須要匹配實(shí)際的遠(yuǎn)程表。列名也必須匹配,不過也可以為個別列附上column_name選項(xiàng)以表示它們在遠(yuǎn)程服務(wù)器上對應(yīng)哪個列。在很多情況中,要手工構(gòu)造外部表定義,使用 IMPORT FOREIGN SCHEMA 會更好。

 

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號