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的方式来清理。

相关文章
相关标签/搜索