进阶课 18 pg_bulkload适配PFS
PolarDB 进阶课程18 让pg_bulkload适配PFS
第一个酱香味的BUG诞生了。
穷鬼玩PolarDB RAC一写多读集群系列已经写了几篇:
《在Docker容器中用loop设备模拟共享存储》 《如何搭建PolarDB容灾(Standby)节点》 《共享存储在线扩容》 《计算节点 Switchover》 《在线备份》 《在线归档》 《实时归档》 《时间点恢复(PITR)》 《读写分离》 《主机全毁, 只剩共享存储的PolarDB还有救吗?》 《激活容灾(Standby)节点》 《将“共享存储实例”转换为“本地存储实例”》 《将“本地存储实例”转换为“共享存储实例”》 《升级vector插件》 《使用图数据库插件AGE》 《接入私有化大模型服务》 《接入PostGIS插件全功能》 《接入pg_duckdb支持数据湖功能,且OLAP性能数量级提升》
本篇文章介绍一下如何修改pg_bulkload, 适配PFS, 加速批量导入PolarDB数据库. 实验环境依赖 《在Docker容器中用loop设备模拟共享存储》 , 如果没有环境, 请自行参考以上文章搭建环境.
pg_bulkload是PostgreSQL加速逻辑数据导入的工具, 可以绕过wal, shared_buffer, 并且支持并行、支持使用binary方式(绕过PG datatype序列化和反序列化过程)直接写入PostgreSQL数据文件. 实现几倍于copy的导入性能. 但是PolarDB的数据文件存放于PFS内, 读写接口都改成了pfs接口, pg_bulkload无法直接使用, 在PolarDB 11版本内通过如下方式进行修改, 支持了本地盘使用pg_bulkload, 但还没有在pfs上尝试过, 本文将使用PolarDB 15进行尝试.
《PolarDB 100 问 | PolarDB 11 编译 pg_bulkload 插件报错》 《PolarDB 100 问 | PolarDB 11 使用 pg_bulkload 插件导入数据报错》 《PostgreSQL 数据传输&存储 数据的序列化和反序列化 (serialization/deserialization)》
pg_bulkload 适配PFS
b站视频链接
Youtube视频链接
有了前面的背景介绍, 我们可以直接修改writer_direct.c, 参看如下pfs头文件:
$ ll PolarDB-for-PostgreSQL/src/include/storage/polar*
-rw-r--r-- 1 digoal staff 3.3K Dec 20 15:24 polar_bufmgr.h
-rw-r--r-- 1 digoal staff 3.9K Dec 20 15:24 polar_copybuf.h
-rw-r--r-- 1 digoal staff 13K Dec 20 15:24 polar_fd.h
-rw-r--r-- 1 digoal staff 3.4K Dec 20 15:24 polar_flush.h
-rw-r--r-- 1 digoal staff 5.6K Dec 20 15:24 polar_io_stat.h
-rw-r--r-- 1 digoal staff 3.4K Dec 20 15:24 polar_lock_stats.h
-rw-r--r-- 1 digoal staff 7.9K Dec 20 15:24 polar_procpool.h
-rw-r--r-- 1 digoal staff 5.3K Dec 20 15:24 polar_rsc.h
-rw-r--r-- 1 digoal staff 5.5K Dec 20 15:24 polar_xlogbuf.h
$ ll PolarDB-for-PostgreSQL/src/include//polar_vfs/*
-rw-r--r-- 1 digoal staff 1.2K Dec 20 15:24 polar_bufferio.h
-rw-r--r-- 1 digoal staff 2.3K Dec 20 15:24 polar_directio.h
-rw-r--r-- 1 digoal staff 1.5K Dec 20 15:24 polar_pfsd.h
-rw-r--r-- 1 digoal staff 2.2K Dec 20 15:24 polar_vfs_fe.h
-rw-r--r-- 1 digoal staff 3.4K Dec 20 15:24 polar_vfs_interface.h
进入pb1 容器
docker exec -ti pb1 bash
克隆代码
cd /data
git clone -c core.symlinks=true --depth 1 -b VERSION3_1_22 https://github.com/ossc-db/pg_bulkload
修改如下, 替换了一些write,BasicOpenFilePerm,fsync,lseek接口为pfs的对应接口.
# 你修改时可以打开vi number
# :set number
$ diff pg_bulkload_for_polar/lib/writer_direct.c pg_bulkload/lib/writer_direct.c
8d7
< #include "storage/polar_fd.h"
223c222
< self->lsf_fd = polar_open(self->lsf_path,
---
> self->lsf_fd = BasicOpenFilePerm(self->lsf_path,
233,234c232,233
< if (polar_write(self->lsf_fd, ls, sizeof(LoadStatus)) != sizeof(LoadStatus) ||
< polar_fsync(self->lsf_fd) != 0)
---
> if (write(self->lsf_fd, ls, sizeof(LoadStatus)) != sizeof(LoadStatus) ||
> pg_fsync(self->lsf_fd) != 0)
629c628
< int len = polar_write(loader->datafd, buffer + written, total);
---
> int len = write(loader->datafd, buffer + written, total);
695c694
< fd = polar_open(fname, O_CREAT | O_WRONLY | PG_BINARY, S_IRUSR | S_IWUSR);
---
> fd = BasicOpenFilePerm(fname, O_CREAT | O_WRONLY | PG_BINARY, S_IRUSR | S_IWUSR);
702c701
< ret = polar_lseek(fd, BLCKSZ * (blknum % RELSEG_SIZE), SEEK_SET);
---
> ret = lseek(fd, BLCKSZ * (blknum % RELSEG_SIZE), SEEK_SET);
726c725
< if (polar_fsync(loader->datafd) != 0)
---
> if (pg_fsync(loader->datafd) != 0)
729c728
< if (polar_close(loader->datafd) < 0)
---
> if (close(loader->datafd) < 0)
750,751c749,750
< polar_lseek(loader->lsf_fd, 0, SEEK_SET);
< ret = polar_write(loader->lsf_fd, ls, sizeof(LoadStatus));
---
> lseek(loader->lsf_fd, 0, SEEK_SET);
> ret = write(loader->lsf_fd, ls, sizeof(LoadStatus));
756c755
< if (polar_fsync(loader->lsf_fd) != 0)
---
> if (pg_fsync(loader->lsf_fd) != 0)
编译修改后的pg_bulkload
cd /data/pg_bulkload
sudo chown -R postgres:postgres /home/postgres/tmp_polardb_pg_15_base
USE_PGXS=1 make install
安装pg_bulkload插件
$ psql
psql (PostgreSQL 15.10 (PolarDB 15.10.2.0 build d4f5477d debug) on aarch64-linux-gnu)
Type "help"forhelp.
postgres=# create extension pg_bulkload ;
使用pg_bulkload导入PolarDB 15 on PFS举例
1、参考满哥的这篇文章( https://www.modb.pro/db/1754101551677394944 ), 创建一张foo表, 生成一些测试数据, 并使用pg_bulkload命令行往PolarDB foo表进行导入.
进入pb1 容器
docker exec -ti pb1 bash
2、在PolarDB中建表
create table foo(a int, b varchar);
3、生成点数据
cd /data/pg_bulkload
seq 100000| awk '{print $0",foo"}' > foo.csv
4、使用pg_bulkload命令行往PolarDB 15 foo表进行导入
$ pg_bulkload -i ./foo.csv -O foo -l test_foo.log -p 5432 -o "TYPE=csv" -o "DELIMITER=," -d postgres -U postgres
NOTICE: BULK LOAD START
NOTICE: BULK LOAD END
0 Rows skipped.
100000 Rows successfully loaded.
0 Rows not loaded due to parse errors.
0 Rows not loaded due to duplicate errors.
0 Rows replaced with new rows.
5、pg_bulkload通过direct模式导入的数据需要重启才能被看到, 和PolarDB relation size cache mode有关(11 对应这个参数 polar_nblocks_cache_mode).
postgres=# select count(*) from foo;
count
-------
0
(1 row)
# 重启PolarDB
pg_ctl restart -m fast -D ~/primary
postgres=# select count(*) from foo;
count
--------
100000
(1 row)
PolarDB 15可以通过polar_enable_rel_size_cache参数来控制.
postgres=# alter system set polar_enable_rel_size_cache=off;
ALTER SYSTEM
# 重启PolarDB
pg_ctl restart -m fast -D ~/primary
关闭polar_enable_rel_size_cache后, pg_bulkload导入的数据可以立即被看见.
6、检查一下数据文件是不是写在pfs里?
postgres=# select pg_relation_filepath('foo');
pg_relation_filepath
------------------------------------
/nvme1n1/shared_data//base/5/24692
(1 row)
没错
$ pfs -C disk stat /nvme1n1/shared_data//base/5/24692
file: /nvme1n1/shared_data//base/5/24692
size: 3629056 blocks: 8192
device: pbd-0 inode: 658 links: 1
access: 0, Thu Jan 1 08:00:00 1970
modify: 1735801813, Thu Jan 2 15:10:13 2025
change: 1735801813, Thu Jan 2 15:10:13 2025
7、使用1000万数据对比, pg_bulkload速度是COPY 3倍多.
$ time pg_bulkload -i ./foo.csv -O foo -l test_foo.log -p 5432 -o "TYPE=csv" -o "DELIMITER=," -d postgres -U postgres
NOTICE: BULK LOAD START
NOTICE: BULK LOAD END
0 Rows skipped.
10000000 Rows successfully loaded.
0 Rows not loaded due to parse errors.
0 Rows not loaded due to duplicate errors.
0 Rows replaced with new rows.
real 0m4.172s
user 0m0.002s
sys 0m0.011s
postgres=# \copy foo from './foo.csv' with (format csv);
COPY 10000000
Time: 14377.082 ms (00:14.377)
参考
《穷鬼玩PolarDB RAC一写多读集群系列 | 在Docker容器中用loop设备模拟共享存储》
《穷鬼玩PolarDB RAC一写多读集群系列 | 如何搭建PolarDB容灾(Standby)节点》
《穷鬼玩PolarDB RAC一写多读集群系列 | 共享存储在线扩容》
《穷鬼玩PolarDB RAC一写多读集群系列 | 计算节点 Switchover》
《穷鬼玩PolarDB RAC一写多读集群系列 | 在线备份》
《穷鬼玩PolarDB RAC一写多读集群系列 | 在线归档》
《穷鬼玩PolarDB RAC一写多读集群系列 | 实时归档》
《穷鬼玩PolarDB RAC一写多读集群系列 | 时间点恢复(PITR)》
《穷鬼玩PolarDB RAC一写多读集群系列 | 读写分离》
《穷鬼玩PolarDB RAC一写多读集群系列 | 主机全毁, 只剩共享存储的PolarDB还有救吗?》
《穷鬼玩PolarDB RAC一写多读集群系列 | 激活容灾(Standby)节点》
《穷鬼玩PolarDB RAC一写多读集群系列 | 将“共享存储实例”转换为“本地存储实例”》
《穷鬼玩PolarDB RAC一写多读集群系列 | 将“本地存储实例”转换为“共享存储实例”》
《穷鬼玩PolarDB RAC一写多读集群系列 | 升级vector插件》
《穷鬼玩PolarDB RAC一写多读集群系列 | 使用图数据库插件AGE》
《穷鬼玩PolarDB RAC一写多读集群系列 | 接入私有化大模型服务》
《穷鬼玩PolarDB RAC一写多读集群系列 | 接入PostGIS插件全功能》
《穷鬼玩PolarDB RAC一写多读集群系列 | 接入pg_duckdb支持数据湖功能,且OLAP性能数量级提升》