下半年一直忙於NP的sybase,經過你們的共同努力,NP年末比較穩定。好久沒有弄過pg相關的知識了,最近常常看到有人問如何用工具自動清理pg的壞元組。node
除了咱們常常手動使用vacuum以外,生產環境還有兩個比較經常使用的工具一個是pg_squeeze,另一個是pg_repackgit
項目地址:https://github.com/cybertec-postgresql/pg_squeezegithub
pg_squeeze是一個擴展,它從表中刪除未使用的空間,而且能夠選擇根據特定索引對元組進行排序,通常當一個表膨脹時通常使用vacuum full或者cluster進行表重建,在這一過程當中會加排他鎖,致使該表沒法進行讀寫,只有等整個過程完成後才能夠進行正常使用sql
相比pg_repack或者pg_reorg,pg_squeeze不須要建觸發器,因此在重組時對原表的DML幾乎沒有性能影響。pg_squeeze支持自動重組,能夠設置定時清理時間以及根據空閒空間比例來進行清理表。該過程會自動啓動worker進程,將數據複製到重組表,而後加鎖,切換filenode。數據庫
一、下載安裝包後,解壓後修改MakeFile,在MakeFile中加入pg_config後端
PG_CONFIG =/home/thunisoft5/arterybase/5.0/bin/pg_config
二、安裝數組
make && make install
三、修改postgresql.conf配置文件服務器
wal_level = logical max_replication_slots = 1 # 大於等於1 shared_preload_libraries = 'pg_squeeze'
四、重啓數據庫架構
一、建立擴展工具
postgres=# create extension pg_squeeze; CREATE EXTENSION postgres=# \dx List of installed extensions Name | Version | Schema | Description ------------+---------+------------+------------------------------------------------ pg_squeeze | 1.2 | squeeze | A tool to remove unused space from a relation. plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (2 rows)
二、安裝完成後會有一個對應的系統表
postgres=# \d squeeze.tables Table "squeeze.tables" Column | Type | Collation | Nullable | Default ------------------+-----------------------+-----------+----------+-------------------------------------------- id | integer | | not null | nextval('squeeze.tables_id_seq'::regclass) tabschema | name | | not null | tabname | name | | not null | clustering_index | name | | | rel_tablespace | name | | | ind_tablespaces | name[] | | | schedule | time with time zone[] | | not null | free_space_extra | integer | | not null | 50 min_size | real | | not null | 8 vacuum_max_age | interval | | not null | '01:00:00'::interval max_retry | integer | | not null | 0 skip_analyze | boolean | | not null | false Indexes: "tables_pkey" PRIMARY KEY, btree (id) "tables_tabschema_tabname_key" UNIQUE CONSTRAINT, btree (tabschema, tabname) Check constraints: "tables_free_space_extra_check" CHECK (free_space_extra >= 0 AND free_space_extra < 100) "tables_min_size_check" CHECK (min_size > 0.0::double precision) Referenced by: TABLE "squeeze.tables_internal" CONSTRAINT "tables_internal_table_id_fkey" FOREIGN KEY (table_id) REFERENCES squeeze.tables(id) ON DELETE CASCADE TABLE "squeeze.tasks" CONSTRAINT "tasks_table_id_fkey" FOREIGN KEY (table_id) REFERENCES squeeze.tables(id) ON DELETE CASCADE Triggers: tables_internal_trig AFTER INSERT ON squeeze.tables FOR EACH ROW EXECUTE PROCEDURE squeeze.tables_internal_trig_func()
squeeze.tables表字段說明
三、建立測試表
--建立表 postgres=# create table test(n_id int,c_name varchar(300),primary key(n_id)); CREATE TABLE --初始化數據 postgres=# insert into test select generate_series(1,4000000),'zhangsan'; INSERT 0 4000000 --查看錶大小:169MB postgres=# \dt+ test List of relations Schema | Name | Type | Owner | Size | Description --------+------+-------+-------+--------+------------- public | test | table | sa | 169 MB | (1 row)
四、給表test建立squeeze任務
--須要在表squeeze.tables插入一條記錄。添加後,將按期檢查表的統計信息。只要知足‘壓縮’的太偶見,就會將‘任務’添加到隊列中,任務按照建立愛女順序依次處理 --schedule標識該任務在晚上八點到24點執行,而且free_space_extra表示空閒空間超過10時就會對錶進行重建 postgres=# insert into squeeze.tables (tabschema, tabname, schedule, free_space_extra) values ('public', 'test', '{20:00, 24:00}', '10'); INSERT 0 1 --若是須要取消註冊表,只須要從‘squeeze.tables’表刪除響應的行便可 --查看任務 postgres=# select * from squeeze.tables; id | tabschema | tabname | clustering_index | rel_tablespace | ind_tablespaces | schedule | free_space_extra | min_size | vacuum_max_age | max_retry | skip_analyze ----+-----------+---------+------------------+----------------+-----------------+---------------------------+------------------+----------+----------------+-----------+-------------- 2 | public | test | | | | {20:00:00+08,24:00:00+08} | 10 | 8 | 01:00:00 | 0 | f (1 row)
五、啓動和關閉pg_squeeze進程
select squeeze.start_worker(); select squeeze.stop_worker();
六、驗證
--更新數據 postgres=# update test set c_name = '張三-1' where n_id <2000000; UPDATE 1999999 --更新後表大小 postgres=# \dt+ test List of relations Schema | Name | Type | Owner | Size | Description --------+------+-------+-------+--------+------------- public | test | table | sa | 253 MB | (1 row) --查看空閒空間已經30 postgres=# select * from squeeze.tables_internal; table_id | class_id | class_id_toast | free_space | last_task_created | last_task_finished ----------+----------+----------------+------------------+-------------------------------+-------------------- 2 | 16528 | 0 | 30.2095497833996 | 2021-01-05 20:57:10.874252+08 | (1 row) --啓動pg_squeeze postgres=# select squeeze.start_worker(); start_worker -------------- 53433 (1 row) --清理完成後查看錶大小: postgres=# \dt+ test List of relations Schema | Name | Type | Owner | Size | Description --------+------+-------+-------+--------+------------- public | test | table | sa | 169 MB | (1 row) --處理的結束時間last_task_finished時間已經更新了 postgres=# select * from squeeze.tables_internal; table_id | class_id | class_id_toast | free_space | last_task_created | last_task_finished ----------+----------+----------------+------------+-------------------------------+------------------------------- 2 | | | | 2021-01-05 20:57:10.874252+08 | 2021-01-05 20:57:10.916349+08 (1 row)
刪除200w數據
--會自動清理 postgres=# \dt+ test List of relations Schema | Name | Type | Owner | Size | Description --------+------+-------+-------+-------+------------- public | test | table | sa | 85 MB | (1 row)
若是執行vacuum full表還會變小嗎?
postgres=# vacuum full test; VACUUM postgres=# \dt+ test List of relations Schema | Name | Type | Owner | Size | Description --------+------+-------+-------+-------+------------- public | test | table | sa | 84 MB | (1 row)
執行vacuum full後表的大小沒有實質性改變,說明pg_squeeze清理比較完全。
pgstattuple返回一個關係的物理長度、"死亡"元組的百分比以及其餘信息。
列 | 類型 | 描述 |
---|---|---|
table_len | bigint | 物理關係長度,以字節計 |
tuple_count | bigint | 存活元組的數量 |
tuple_len | bigint | 存活元組的總長度,以字節計 |
tuple_percent | float8 | 存活元組的百分比 |
dead_tuple_count | bigint | 死亡元組的數量 |
dead_tuple_len | bigint | 死亡元組的總長度,以字節計 |
dead_tuple_percent | float8 | 死亡元組的百分比 |
free_space | bigint | 空閒空間總量,以字節計 |
free_percent | float8 | 空閒空間的百分比 |
postgres=# create extension pgstattuple; CREATE EXTENSION postgres=# select * from pgstattuple('test'); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percen t | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+------------------ --+------------+-------------- 88563712 | 2000001 | 74000037 | 83.56 | 0 | 0 | 0 | 260960 | 0.29 (1 row)
還能夠手動「壓縮」表,而無需註冊,跳過任什麼時候間和膨脹檢查。
功能簽名: squeeze.squeeze_table(tabchema name, tabname name, clustering_index name, rel_tablespace name, ind_tablespaces name[])
示例執行:
SELECT squeeze.squeeze_table('public', 'test', null, null, null);
'squeeze.log'表在每一個成功壓縮的表中包含一個條目。 'squeeze.errors'包含在壓縮期間發生的錯誤。這裏報告的一個常見問題是有人更改了正在處理表的定義(例如,添加或刪除的列)。
pg_squeeze須要使用logical replication,因此須要設置足夠的slots,並且必須注意可能與standby或者使用了邏輯複製功能爭搶slots,要保證slots夠用。
pg_squeeze能夠自動收縮,對於比較繁忙的數據庫,建議不要在業務高峯期啓用,避免帶來性能風險
首先,確保您的表具備主鍵或惟一約束。這是處理「 pg_squeeze」工做時其餘事務可能進行的更改所必需的。
新版本的squeeze有個更好的功能是:
低版本pg_squeeze支持時間間隔的
task_interval:表示檢查表膨脹是否超過閥值的時間間隔
first_check:表示第一次檢查時間
相對來講直接在晚上定時執行vacuum full的方式更加簡便
自述文件:和pg_squeeze同樣pg_repack也是一個擴展,能夠從表和索引中消除膨脹,而且能夠選擇恢復簇索引的物理順序,與cluster和vacuum full不一樣,該工具能夠在線工做,而且在處理過程當中不須要在表上面持有排它鎖(vacuum full工做須要access exclusive lock,致使任何操做都不能執行),pg_repack的啓動效率很高,其性能與直接使用cluster至關
pg_repack老版本叫pg_reorg
pg_repack原理和vacuum full原理相似,都是新建一個文件,而後將老文件拷貝過來,而後進行文件切換。不阻塞讀寫的祕訣就是新建文件和拷貝的過程是在線作的,在沒有完成拷貝以前,原來的文件仍是能夠讀寫,只有在切表的一瞬間會有影響。
源庫的數據文件一直在變化,pg_repack是如何拷貝的呢?表文件分爲兩部分,一部分是基礎數據,一部分是增量數據,基礎數據的拷貝是正常拷貝,增量數據是經過建立觸發器來捕獲該表上的讀寫操做來實現,基礎數據拷貝完以後再將觸發器捕獲的增量sql進行應用,完成切換。
具體步驟:
固然咱們在執行過程當中從pg_stat_activity中也能夠看到一些
執行過程當中會給對應的表加上ACCESS SHARE MODE
而後執行數據拷貝的工做:INSERT INTO repack.table_16588 SELECT n_id,c_name FROM ONLY public.repack_test
最後建立索引:CREATE UNIQUE INDEX index_16595 ON repack.table_16588 USING btree (n_id) TABLESPACE pg_default
wget https://github.com/reorg/pg_repack/archive/ver_1.4.4.zip [thunisoft5@localhost pg_repack-ver_1.4.4]$ make && make install create extension pg_repack;
選項:
參數 | 描述 |
---|---|
-a, --all | 從新包裝全部數據庫 |
-t, --table=TABLE | 僅從新包裝特定表 |
-I, --parent-table=TABLE | 從新打包特定的父表及其繼承者 |
-c, --schema=SCHEMA | 僅在特定架構中從新打包表 |
-s, --tablespace=TBLSPC | 將從新打包的表移動到新表空間 |
-S, --moveidx | 將從新打包的索引也移動到TBLSPC |
-o, --order-by=COLUMNS | 按列而不是集羣鍵排序 |
-n, --no-order | 真空吸塵而不是吸塵 |
-N, --dry-run | 打印將從新包裝的內容並退出 |
-j, --jobs=NUM | 每一個表使用這麼多並行做業 |
-i, --index=INDEX | 僅移動指定的索引 |
-x, --only-indexes | 僅移動指定表的索引 |
-T, --wait-timeout=SECS | 超時以取消衝突中的其餘後端 |
-D, --no-kill-backend | 超時時不要殺死其餘後端 |
-Z, --no-analyze | 最後不要分析 |
-k, --no-superuser-check | 跳過客戶端中的超級用戶檢查 |
-C, --exclude-extension | 不要從新打包屬於特定擴展名的表 |
鏈接選項:
參數 | 描述 |
---|---|
-d, --dbname=DBNAME | 數據庫鏈接 |
-h, --host=HOSTNAME | 數據庫服務器主機或套接字目錄 |
-p, --port=PORT | 數據庫服務器端口 |
-U, --username=USERNAME | 鏈接的用戶名 |
-w, --no-password | 從不提示輸入密碼 |
-W, --password | 強制輸入密碼提示 |
通用選項:
參數 | 描述 |
---|---|
-e, --echo | 回顯查詢 |
-E, --elevel=LEVEL | 設置輸出消息級別 |
--help | 顯示此幫助,而後退出 |
--version | 輸出版本信息,而後退出 |
postgres=# create table repack_test(n_id int,c_name varchar(3000)); CREATE TABLE --初始化數據 postgres=# insert into repack_test select generate_series(1,4000000),'張三'; INSERT 0 4000000 --使用pg_stattuple查看錶狀況 postgres=# select * from pgstattuple('repack_test'); table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percen t | free_space | free_percent -----------+-------------+-----------+---------------+------------------+----------------+------------------ --+------------+-------------- 177127424 | 4000000 | 140000000 | 79.04 | 0 | 0 | 0 | 522008 | 0.29 (1 row) --查看錶大小 postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') ); pg_size_pretty ---------------- 169 MB (1 row) --查看錶文件路徑 postgres=# select pg_relation_filepath('repack_test'); pg_relation_filepath ---------------------- base/13214/16588 (1 row)
--表必須有主鍵或者惟一約束,這快和pg_squeeze同樣 [thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --table repack_test WARNING: relation "public.repack_test" must have a primary key or not-null unique keys --添加主鍵 postgres=# alter table repack_test add primary key(n_id); ALTER TABLE --更新200w數據 postgres=# update repack_test set c_name = '張三-1' where n_id <=2000000; UPDATE 2000000 更新後表達小變大了 postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') ); pg_size_pretty ---------------- 425 MB (1 row) --再次執行pg_repack [thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --table repack_test --elevel=info INFO: repacking table "public.repack_test" --更新後查看錶大小,表已經縮小了 postgres=# select pg_size_pretty(pg_total_relation_size('repack_test') ); pg_size_pretty ---------------- 255 MB (1 row) --而且數據文件的路徑也發生了變化 postgres=# select pg_relation_filepath('repack_test'); pg_relation_filepath ---------------------- base/13214/16659 (1 row)
repack.primary_keys
repack.tables
postgres=# select * from repack.primary_keys limit 10; indrelid | indexrelid ----------+------------ 826 | 828 1136 | 1137 1213 | 2697 1247 | 2703 1249 | 2658 1255 | 2690 1259 | 2662 1260 | 2677 1261 | 2694 1262 | 2672 (10 rows)
[thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --jobs 8 --elevel=info
pg_repack -p 8543 -d postgres --schema=public --no-order --jobs 8 --elevel=info
pg_repack -p 8543 -d postgres --no-order --table public.repack_test --elevel=info
pg_repack -p 8543 -d postgres --no-order --only-indexes --table public.repack_test --elevel=info
pg_repack -p 8543 -d postgres --index public.repack_test_pkey --elevel=info
一、沒法重組臨時表
二、不能經過gist索引集羣表
三、若是使用1.1.8或者更早的版本,則在運行pg_repack時,切勿嘗試在目標表上面執行任何ddl命令。許多狀況下,pg_repack會失敗並正確回滾,可是在早期版本中,有一些狀況可能會致使數據損壞
pg_squeeze和pg_repack都須要表有主鍵或者非空惟一約束才行
pg_repack重組時,觸發器會帶來必定的開銷,對被重組的表,有必定的DML性能影響。
pg_squeeze不須要建觸發器,因此在重組時對原表的DML幾乎沒有性能影響。
pg_squeeze支持自動的重組,即經過設置閾值、比較用戶表與閾值,自動啓動WORKER進程,將數據複製到重組表,最後加鎖,切換FILENODE。
pg_squeeze須要清理的表都須要在squeeze.tables表中插入對應的記錄,而且能夠對不一樣的表設置閾值和清理時間段。pg_repack能夠針對庫,schema以及表和索引分別清理
兩個工具均可圈可點,pg_squeeze對系統的性能影響更小一些。固然也能夠在晚上系統空閒時間直接使用vacuum full的方式來清理。