postgres數據庫表空間收縮之pg_squeeze,pg_repack

postgres數據庫表空間收縮之pg_squeeze,pg_repack

下半年一直忙於NP的sybase,經過你們的共同努力,NP年末比較穩定。好久沒有弄過pg相關的知識了,最近常常看到有人問如何用工具自動清理pg的壞元組。node

除了咱們常常手動使用vacuum以外,生產環境還有兩個比較經常使用的工具一個是pg_squeeze,另一個是pg_repackgit

pg_squeeze1.2

項目地址: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表字段說明

  • tabschema:表的模式名。
  • tabname:表名。
  • clustering_index:表示重建表時,表數據的物理順序按照該索引進行聚簇。
  • rel_tablespace:表示表重建時,移動到哪一個表空間中。
  • ind_tablespace:這個一個二維數組,表示索引和表空間的映射關係。
  • schedule:vacuum在一天中運行的時間範圍
  • free_space_extra:表示空閒空間超過多少時就會對錶進行重建,默認是50。
  • min_size:表必須佔用的最小磁盤空間(兆字節)纔有資格進行處理,默認值爲8。
  • vacuum_max_age:當進行一次vacuum後,認爲fsm是有效的最大時間,默認1小時。
  • max_retry:當重建表失敗時最大的從新嘗試的次數,默認是0.
  • skip_analyse:跳過對錶進行analyse,默認是false。

三、建立測試表

--建立表
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

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」工做時其餘事務可能進行的更改所必需的。

squeeze1.2和低版本的區別

新版本的squeeze有個更好的功能是:

  • squeeze.tables表能夠指定schedule:也就是指定氣你的時間範圍。你能夠放到晚上來運行。

低版本pg_squeeze支持時間間隔的

  • task_interval:表示檢查表膨脹是否超過閥值的時間間隔

  • first_check:表示第一次檢查時間
    相對來講直接在晚上定時執行vacuum full的方式更加簡便

pg_repack

自述文件:和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進行應用,完成切換。

具體步驟:

  1. 建立一個日誌表來記錄對原始表所作的更改
  2. 在原始表上添加觸發器,將INSERT,UPDATE和DELETE記錄到咱們的日誌表中
  3. 建立一個新表,包含舊錶中全部的行
  4. 在這個新表上創建索引
  5. 將日誌表中產生的全部更改應用到新表中
  6. 使用系統目錄交換表,包括索引和Toast表
  7. 刪除原始表

固然咱們在執行過程當中從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

  • indrelid表明表的oid,第二列indexrelid表明主鍵或者惟一索引的oid

repack.tables

  • tables表記錄了建立trigger以及捕獲的相關語句,語句按一條條的record進行記錄
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)

在線pg_repack

repack數據庫

[thunisoft5@localhost bin]$ pg_repack -p 8543 -d postgres --no-order --jobs 8  --elevel=info

repack模式

pg_repack -p 8543 -d postgres --schema=public --no-order --jobs 8  --elevel=info

repack表和索引

pg_repack -p 8543 -d postgres --no-order --table public.repack_test --elevel=info

repack全部索引

pg_repack -p 8543 -d postgres --no-order --only-indexes --table public.repack_test --elevel=info

repack指定索引

pg_repack -p 8543 -d postgres  --index public.repack_test_pkey --elevel=info

pg_repack限制

一、沒法重組臨時表

二、不能經過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的方式來清理。

相關文章
相關標籤/搜索