PolarDB PostgreSQL版(相容Oracle)支援通過pg_repack外掛程式對錶空間進行重新“封裝”,回收片段空間,有效解決因對錶大量更新、刪除等操作引起的空間膨脹問題。pg_repack擷取排它鎖的時間較短,多數時間不阻塞讀寫,相比CLUSTER或VACUUM FULL操作更加輕量化。
前提條件
支援pg_repack外掛程式的PolarDB PostgreSQL版(相容Oracle)的版本如下:
資料庫引擎版本為Oracle文法相容 2.0,且核心小版本需為2.0.14.22.0及以上。
您可通過如下語句查看PolarDB PostgreSQL版(相容Oracle)的核心小版本號碼:
SHOW polar_version;
注意事項
不支援使用
-a
/--all
選項repack所有的資料庫。不支援repack整個資料庫,必須通過
--table
/--parent-table
/--index
選項指定需要repack的表或者索引。不支援使用
-c
/--schema
選項repack整個schema。不支援使用
-s
/--tablespace
指定的資料表空間,因為PolarDB PostgreSQL版(相容Oracle)不支援您直接建立資料表空間,而是使用預設的資料表空間。pg_repack需要臨時佔用額外的儲存空間來儲存新表和日誌表,因此叢集剩餘儲存空間大小至少應為repack表大小的兩倍。
pg_repack會佔用較大的磁碟I/O,使用前請評估是否會影響業務。以PL1層級的ESSD雲端硬碟為例,對一個100 GB的表進行repack時會達到I/O吞吐上限(250 MB/s)。
使用方法
pg_repack作為安裝在PolarDB PostgreSQL版(相容Oracle)側的外掛程式作為服務端,並提供專用的用戶端給使用者,兩者需要搭配使用。
安裝外掛程式
CREATE EXTENSION pg_repack;
查看外掛程式版本。
SELECT extversion FROM pg_extension WHERE extname = 'pg_repack';
如果已經安裝了老版本外掛程式,可以通過先卸載原有外掛程式再重新建立的方式升級到最新版。
DROP EXTENSION pg_repack;
CREATE EXTENSION pg_repack;
如果服務端沒有安裝pg_repack外掛程式,直接運行pg_repack命令用戶端會報錯:ERROR:pg_repack failed with error: pg_repack x.x.x is not installed in the database
。
安裝用戶端
pg_repack用戶端工具隨PolarDB-Tools工具包發布,下載安裝PolarDB-Tools後即可使用其中的pg_repack用戶端。下載地址及安裝方法請參見PolarDB-Tools。
pg_repack用戶端的版本必須匹配pg_repack外掛程式的版本,否則執行用戶端會報錯:
ERROR:pg_repack failed with error: extension 'pg_repack x.x.x' required, found 'pg_repack y.y.y'; please drop and re-create the extension
或ERROR:pg_repack failed with error: program 'pg_repack x.x.x' does not match database library 'pg_repack y.y.y'
。請從上述連結中選擇與PolarDB叢集版本相匹配的PolarDB-Tools工具包進行下載和使用。
Repack普通表和分區表分區
pg_repack支援對普通表或者分區表的某個分區進行repack,其作用類似於CLUSTER
或VACUUM FULL
操作,清理表中多餘的空閑空間,同時重建表上的索引,適用於資料表空間膨脹的情境。
repack表必須有主鍵或唯一索引。
不支援對暫存資料表進行repack操作。
不支援對帶有
Global Index
的分區進行repack操作。
文法說明一
通過
--table
參數指定表名,預設情況下效果等同於CLUSTER
,repack過程中對之前執行過CLUSTER
操作的列進行排序:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1
如果希望對指定的列進行排序,可以使用
--order-by
參數來指定列名:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1 --order-by <列名>
如果不希望進行排序,即希望
pg_repack
的效果等同於VACUUM FULL
,可以使用--no-order
參數:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1 --no-order
如果資料庫叢集的CPU和I/O資源充裕,可以使用
--jobs
參數加速repack操作,它會啟動多個進程並發重建索引,適用於表上有多個索引的情境:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1 --jobs <並發數量>
Repack分區表和繼承表
pg_repack支援對分區表(包括聲明式分區表和繼承式分區表)進行操作,它會自動找到父表的所有分區,並對每個分區依次進行repack操作。適用於分區表的所有分區都存在空間膨脹的情境。
文法說明二
通過
--parent-table
參數指定分區表的表名:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --parent-table schema1.table1
說明除了
--parent-table
參數以外,分區表的其他參數用法與普通表基本相同。如果只是單個分區存在空間膨脹,則無需對整個分區表進行repack,使用文法說明一中(
--table
參數)對單個分區進行repack操作即可。不支援對帶有
Global Index
的分區表進行repack操作。
Repack索引
pg_repack支援僅對索引進行repack操作,它的作用是重建索引,清理索引中的空閑空間,適用於索引空間膨脹的情境。
如果索引空閑空間過多,推薦使用
REINDEX CONCURRENTLY
進行線上索引重建,無需使用pg_repack。pg_repack支援該能力的原因是老版本的PostgreSQL不支援REINDEX CONCURRENTLY
,從而只能藉助pg_repack來實現。由於pg_repack社區的特性,暫不支援對聲明式分區表進行repack索引的操作,同樣可以使用
REINDEX CONCURRENTLY
來代替。
文法說明三
使用
--index
參數指定需要repack的索引名:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --index schema1.table1
使用
--only-indexes
參數repack表上的所有索引:pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1 --only-indexes
更多用法
使用pg_repack --help
可以查看pg_repack的更多用法。
常見問題
Dry Run
正式執行pg_repack之前建議使用--dry-run
選項運行一次,該選項不動作表中的資料,僅驗證命令是否合法、流程是否可以跑通。待命令驗證成功後,再去掉該選項正式運行pg_repack。
pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --echo --table schema1.table1 --dry-run
許可權問題
必須使用高許可權帳號運行pg_repack,不能以普通帳號身份運行,否則會報錯:
must be polar_superuser or superuser to use xx function
。如果遇到
You must be a superuser to use pg_repack
報錯,則需要在pg_repack命令中增加--no-superuser-check
選項來繞過超級使用者檢查。
逾時問題
如果需要repack的表或索引過大,repack過程耗時過長,則可能遇到FATAL: terminating connection due to idle-in-transaction timeout
、FATAL: terminating connection due to idle-session timeout
之類的錯誤。這表示pg_repack用戶端與PolarDB PostgreSQL版(相容Oracle)叢集的串連因為逾時而斷開,本次repack失敗。
解決方案是在pg_repack命令之前增加PGOPTIONS
選項設定逾時相關參數來關閉逾時功能,以防止逾時導致串連斷開。
PGOPTIONS="-c idle_session_timeout=0 -c idle_in_transaction_session_timeout=0 -c statement_timeout=0" pg_repack -h <host> -p <port> -d <db> -U <user> --no-superuser-check --table schema1.table1
殘留對象清理
如果pg_repack在執行過程中異常退出,則repack失敗,被repack的表上可能殘留了repack過程中建立的對象,需要及時清理,否則可能影響表的使用:
被repack的表上可能殘留
repack_trigger
觸發器,需要使用DROP TRIGGER
命令刪除。被repack的表上可能殘留臨時索引
index_<oid>
,需要使用DROP INDEX CONCURRENTLY
命令刪除。repack
模式下殘留暫存資料表repack_<oid>
與日誌表log_<oid>
,需要使用DROP TABLE
命令刪除。repack
模式下殘留新的類型pk_<oid>
,需要使用DROP TYPE
命令刪除。
原理介紹
pg_repack外掛程式支援對全表和索引進行repack操作。
對全表進行repack的實現原理如下:
建立日誌表,記錄repack期間對原表的變更。
在原表上建立觸發器,將原表的
INSERT
、UPDATE
和DELETE
操作記錄到日誌表中。建立原表結構相同的新表並將原表資料匯入其中。
在新表中建立與原表相同的索引。
將日誌表裡的變更(即repack期間表上產生的增量資料)應用到新表。
在系統catalog交換新舊錶。
刪除舊錶。
說明pg_repack會在第1、2、6、7步短暫持有原表的排它鎖並阻塞讀寫。其餘步驟pg_repack只需要持有原表的ACCESS SHARE鎖,不阻塞對原表的
INSERT
、UPDATE
和DELETE
操作,但會阻塞DDL
操作。
對索引進行repack的實現原理如下:
以
CREATE INDEX CONCURRENTLY
方式建立新索引。在系統catalog交換新舊索引(需持有排它鎖,短暫阻塞讀寫)。
以
DROP INDEX CONCURRENTLY
的方式刪除舊索引。
說明pg_repack效果等同於
REINDEX CONCURRENTLY
,但是比REINDEX CONCURRENTLY
更為複雜,如果使用REINDEX CONCURRENTLY
,只需要一步就能完成。pg_repack支援該能力的原因是老版本的PostgreSQL不支援REINDEX CONCURRENTLY
,從而只能藉助pg_repack來實現。
相關參考
pg_repack的更多資訊可參考pg_repack官方協助文檔。