PostgreSQL 通过分割heap数据文件分拆表的hacking方法

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介:

标签

PostgreSQL , heap file , toast file , 分区 , split , 拆表 , 大表


背景

为什么要将一张大表拆成若干张小表?前面的文档给出了原因

《PostgreSQL 并行vacuum patch - 暨为什么需要并行vacuum或分区表》

《HTAP数据库 PostgreSQL 场景与性能测试之 45 - (OLTP) 数据量与性能的线性关系(10亿+无衰减), 暨单表多大需要分区》

那么如何拆呢?pg_pathman提供了一种平滑将单表拆成多张分区表的方法。这种方法涉及到数据的迁移。

《PostgreSQL 9.5+ 高效分区表实现 - pg_pathman》

本文使用一种比较HACKING的方法,将单表拆成多表。(仅供玩耍,请勿用于生产)

方法是直接针对数据文件下手。

测试

1、创建一个表,202个字段。(全部是inline存储的字段,没有用到toast)

do language plpgsql $$  
declare  
  sql text;  
begin  
  sql := 'create table aa (id int,';  
  for i in 1..100 loop  
    sql := sql||'c'||i||' int default random()*10000000,';  
  end loop;  
  for i in 101..200 loop  
    sql := sql||'c'||i||' float8 default random()*10000000,';  
  end loop;  
  sql := sql||'ts timestamp default clock_timestamp() )';  
  execute sql;  
end;  
$$;  

2、写入若干记录

postgres=# insert into aa (id) select generate_series(1,3000000);  
INSERT 0 3000000  

3、查看当前表的文件存储路径

postgres=# select pg_relation_filepath('aa');  
 pg_relation_filepath   
----------------------  
 base/20699/66326  
(1 row)  

4、在数据文件目录中,找到对应的数据文件。

postgres@host-> cd $PGDATA  
postgres@host-> ll base/20699/66326*  
-rw------- 1 postgres postgres  1.0G Mar  1 11:24 base/20699/66326  
-rw------- 1 postgres postgres  1.0G Mar  1 11:25 base/20699/66326.1  
-rw------- 1 postgres postgres  1.0G Mar  1 11:25 base/20699/66326.2  
-rw------- 1 postgres postgres  835M Mar  1 11:25 base/20699/66326.3  
-rw------- 1 postgres postgres 1000K Mar  1 11:25 base/20699/66326_fsm  

简单解释一下带后缀的意思,

4.1、数字后缀,如果数据文件超过1GB时(根据编译时指定--with-segsize=SEGSIZE set table segment size in GB [1]决定一个文件的大小),会按数字开始命名,扩展更多文件数。

4.2、fsm后缀,表示这个文件是free space mapping文件,大概意思是每个BLOCK剩余多少空间(实际上是分位数,并不是精确值)。

4.3、vm后缀,visibility mapping文件,每个BLOCK占用2个BIT位,表示是否有脏数据,是否clean等信息。

4.4、init后缀,表示它是unlogged table。

5、因为占用了4个文件,所以我们创建4个目标表,使用继承关系关联起来。

postgres=# create table bb (like aa including all);   
  
postgres=# create table bb1 (like bb including all) inherits (bb);   
  
postgres=# create table bb2 (like bb including all) inherits (bb);   
  
postgres=# create table bb3 (like bb including all) inherits (bb);   
  
postgres=# create table bb4 (like bb including all) inherits (bb);   

6、查看4个目标表的数据文件路径

postgres=# select pg_relation_filepath('bb1');  
 pg_relation_filepath   
----------------------  
 base/20699/66533  
(1 row)  
  
postgres=# select pg_relation_filepath('bb2');  
 pg_relation_filepath   
----------------------  
 base/20699/66739  
(1 row)  
  
postgres=# select pg_relation_filepath('bb3');  
 pg_relation_filepath   
----------------------  
 base/20699/66945  
(1 row)  
  
postgres=# select pg_relation_filepath('bb4');  
 pg_relation_filepath   
----------------------  
 base/20699/67151  
(1 row)  

7、停库,将数据文件一对一的拷贝到4个目标表。每个表占用一个文件。

postgres@host-> cd $PGDATA  
postgres@host-> ll base/20699/66533*  
-rw------- 1 postgres postgres 0 Mar  1 11:26 base/20699/66533  
  
cp base/20699/66326 base/20699/66533  
  
cp base/20699/66326.1 base/20699/66739  
  
cp base/20699/66326.2 base/20699/66945  
  
cp base/20699/66326.3 base/20699/67151  

8、启动数据库

pg_ctl start  

9、验证数据拆分是否准确

记录级验证

postgres=# select count(*) from bb1;  
 count    
--------  
 786432  
(1 row)  
  
postgres=# select count(*) from bb2;  
 count    
--------  
 786432  
(1 row)  
  
postgres=# select count(*) from bb3;  
 count    
--------  
 786432  
(1 row)  
  
postgres=# select count(*) from bb4;  
 count    
--------  
 640704  
(1 row)  
  
postgres=# select count(*) from aa;  
  count    
---------  
 3000000  
(1 row)  
  
postgres=# select count(*) from bb;  
  count    
---------  
 3000000  
(1 row)  

行级验证,验证HASH是否一致

postgres=# select sum(hashtext(aa.*::text)) from aa;  
      sum        
---------------  
 4322646507500  
(1 row)  
  
  
postgres=# select sum(hashtext(bb.*::text)) from bb;  
      sum        
---------------  
 4322646507500  
(1 row)  

10、小插曲,vacuum新表时报错,原因是新表的frozenxid比数据文件中XID还小。

postgres=# vacuum bb1;  
ERROR:  found xmin 366891771 from before relfrozenxid 366891776  

11、修正pg_class元数据,改成元表xid即可

postgres=# select * from pg_class where relname='bb1';  
-[ RECORD 1 ]-------+----------  
relname             | bb1  
relnamespace        | 35988  
reltype             | 66535  
reloftype           | 0  
relowner            | 10  
relam               | 0  
relfilenode         | 66533  
reltablespace       | 0  
relpages            | 0  
reltuples           | 0  
relallvisible       | 0  
reltoastrelid       | 0  
relhasindex         | f  
relisshared         | f  
relpersistence      | p  
relkind             | r  
relnatts            | 202  
relchecks           | 0  
relhasoids          | f  
relhaspkey          | f  
relhasrules         | f  
relhastriggers      | f  
relhassubclass      | f  
relrowsecurity      | f  
relforcerowsecurity | f  
relispopulated      | t  
relreplident        | d  
relispartition      | f  
relfrozenxid        | 366891776  
relminmxid          | 1  
relacl              |   
reloptions          |   
relpartbound        |   
  
  
postgres=# update pg_class set relfrozenxid='366891771'::xid where relname='bb1';  
UPDATE 1  
postgres=# update pg_class set relfrozenxid='366891771'::xid where relname='bb2';  
UPDATE 1  
postgres=# update pg_class set relfrozenxid='366891771'::xid where relname='bb3';  
UPDATE 1  
postgres=# update pg_class set relfrozenxid='366891771'::xid where relname='bb4';  
UPDATE 1  

vacuum准确

postgres=# vacuum freeze bb1;  
VACUUM  
postgres=# vacuum freeze bb2;  
VACUUM  
postgres=# vacuum freeze bb3;  
VACUUM  
postgres=# vacuum freeze bb4;  
VACUUM  

HEAP文件的关联关系

pic

一张表的数据可能有多个堆存储文件+TOAST文件组成,如果我们要直接用堆存储文件分割表,那么会涉及一些这样的关联关系:

1、索引,索引文件预HEAP文件并不是一一对应,因此分割后,需要重建索引。

2、TOAST,TOAST指那些超过1/4 PAGE SIZE的变长列,会以TOAST形式来进行组织,TOAST记录与HEAP文件并无关系,而是与HEAP文件中的每条记录中的TOAST POINT关联。所以我们在分割后,需要让数据库知道如果从TOAST POINT找到对应的TOAST中的记录(可能会涉及pg_class中TOAST元数据对应关系的修改)。

3、约束,主要指外键,当分割后,其他表REFERRENCE你这张表就可能失效。

4、vm, fsm file,这个和HEAP文件是多对一的关系,可以重建,不影响使用。

5、元数据,指表的FROZENXID(存在pg_class中)与HEAP文件中的记录的XID的前后关系,frozenxid不能大于HEAP文件中的XID。

涉及到toast表、索引时,SPLIT更加复杂。

1、TOAST表的组织形式可能和HEAP文件不一一对应,同TOAST对象的关联关系需要修正。

2、索引文件的组织形式和HEAP文件不一一对应。

有索引的话,干掉索引,重建。

小结

本文提供了一种高效SPLIT的思路,方法比较hacking,并且仅适用于没有extended, external存储(变长字段)TOAST外部存储的情况。请勿用于生产。

参考

《PostgreSQL 并行vacuum patch - 暨为什么需要并行vacuum或分区表》

《PostgreSQL 9.5+ 高效分区表实现 - pg_pathman》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
6月前
|
SQL Oracle 关系型数据库
实时计算 Flink版操作报错之往GREENPLUM 6 写数据,用postgresql-42.2.9.jar 报 ON CONFLICT (uuid) DO UPDATE SET 语法有问题。怎么解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
5月前
|
消息中间件 Java 关系型数据库
实时计算 Flink版操作报错合集之从 PostgreSQL 读取数据并写入 Kafka 时,遇到 "initial slot snapshot too large" 的错误,该怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
1013 0
|
5月前
|
DataWorks 安全 关系型数据库
DataWorks产品使用合集之使用Flink CDC读取PostgreSQL数据时如何指定编码格式
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
101 0
|
3月前
|
SQL 关系型数据库 Linux
在CentOS 6上安装和使用PostgreSQL的方法
在CentOS 6上安装和使用PostgreSQL的方法
44 2
|
3月前
|
Ubuntu 关系型数据库 数据库
在Ubuntu 18.04上安装和使用PostgreSQL的方法
在Ubuntu 18.04上安装和使用PostgreSQL的方法
65 1
|
3月前
|
Ubuntu 关系型数据库 Linux
在Ubuntu 14.04上安装和使用PostgreSQL的方法
在Ubuntu 14.04上安装和使用PostgreSQL的方法
48 1
|
3月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
403 0
|
3月前
|
SQL 关系型数据库 HIVE
实时计算 Flink版产品使用问题之如何将PostgreSQL数据实时入库Hive并实现断点续传
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
3月前
|
关系型数据库 Linux 数据库
在CentOS 7上安装和使用PostgreSQL的方法
在CentOS 7上安装和使用PostgreSQL的方法
322 0
|
4月前
|
SQL 监控 关系型数据库
实时计算 Flink版操作报错合集之在设置监控PostgreSQL数据库时,将wal_level设置为logical,出现一些表更新和删除操作报错,怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 下一篇
    无影云桌面