- 数据工程
- ETL
- 数据集成
- 数据库
【免费下载链接】pgloader
Migrate to PostgreSQL in a single command!
本文是 pgloader 中LOAD ARCHIVE命令的完整技术指南。该命令允许一条命令同时完成「下载归档 → 解压 → 匹配归档内的数据文件 → 加载进 PostgreSQL」,目前官方文档承诺的归档格式为 ZIP,且归档可以直接来自 HTTP URL。读完本文,你将掌握LOAD ARCHIVE的完整语法骨架、FROM归档源规范、FILENAME MATCHING正则匹配子命令、BEFORE LOAD/FINALLY DO钩子,并结合仓库源码理解下载、解压与文件匹配的底层实现。
本文内容以仓库文档 docs/ref/archive.rst 为主体骨架,源码与测试佐证来自 src/utils/archive.lisp、src/parsers/command-archive.lisp 及 test/archive.load 等文件。
一、LOAD ARCHIVE能做什么
LOAD ARCHIVE让 pgloader 从一个归档文件中加载一个或多个数据文件的内容。官方文档明确说明:目前受支持的归档格式是 ZIP,且归档可以通过 HTTP URL 下载。它最典型的应用场景是:
- 数据源以 ZIP 打包发布,且定期更新(如 GeoIP 地理库、行政区划编码库);
- 数据文件不在本地,需要 pgloader 自行从网络抓取;
- 一个归档里包含多个同构或异构的数据文件,需要一次命令全部入库。
从源码结构看,src/utils/archive.lisp 中实际定义的支持类型比文档更宽:
(defparameter *supported-archive-types* '(:tar :tgz :gz :zip))也就是说,底层实现对tar、tgz、gz、zip四种类型都有展开路径,但官方文档承诺并重点讲解的是 ZIP 场景,日常使用建议以文档为准。
二、命令总体骨架与语法规则
LOAD ARCHIVE的命令结构由源码 src/parsers/command-archive.lisp 中的语法规则定义,完整骨架如下:
LOAD ARCHIVE FROM <归档路径或 HTTP URL> INTO <postgresql 连接 URI> -- 可选,归档级目标库 BEFORE LOAD DO ... / EXECUTE ... -- 可选,加载前 SQL <子命令 1> AND <子命令 2> AND ... FINALLY DO ... -- 可选,加载后 SQL其中:
LOAD ARCHIVE FROM <源>是命令起始(源码archive-source规则:src/parsers/command-archive.lisp);- 子命令之间用
AND连接,构成archive-command-list; BEFORE LOAD与FINALLY均为可选子句。
源码中有一个值得注意的硬性约束(src/parsers/command-archive.lisp):
"When using a BEFORE LOAD DO or a FINALLY block, you must provide an archive level target database connection."
只要命令里写了BEFORE LOAD DO或FINALLY块,就必须同时提供归档级的INTO postgresql://...目标连接,否则解析阶段直接报错。原因很直观:BEFORE LOAD/FINALLY里的 SQL 要在归档级目标库上执行,没有连接无从谈起。
三、完整配置示例:一条命令加载 GeoLiteCity
官方文档给出的示范命令保存在.load命令文件中,通过pgloader archive.load执行:
$ pgloader archive.loadarchive.load的内容如下(出自 docs/ref/archive.rst,完整的可运行版本位于 test/archive.load):
LOAD ARCHIVE FROM /Users/dim/Downloads/GeoLiteCity-latest.zip INTO postgresql:///ip4r BEFORE LOAD DO $$ create extension if not exists ip4r; $$, $$ create schema if not exists geolite; $$, EXECUTE 'geolite.sql' LOAD CSV FROM FILENAME MATCHING ~/GeoLiteCity-Location.csv/ WITH ENCODING iso-8859-1 ( locId, country, region null if blanks, city null if blanks, postalCode null if blanks, latitude, longitude, metroCode null if blanks, areaCode null if blanks ) INTO postgresql:///ip4r?geolite.location ( locid,country,region,city,postalCode, location point using (format nil "(~a,~a)" longitude latitude), metroCode,areaCode ) WITH skip header = 2, fields optionally enclosed by '"', fields escaped by double-quote, fields terminated by ',' AND LOAD CSV FROM FILENAME MATCHING ~/GeoLiteCity-Blocks.csv/ WITH ENCODING iso-8859-1 ( startIpNum, endIpNum, locId ) INTO postgresql:///ip4r?geolite.blocks ( iprange ip4r using (ip-range startIpNum endIpNum), locId ) WITH skip header = 2, fields optionally enclosed by '"', fields escaped by double-quote, fields terminated by ',' FINALLY DO $$ create index blocks_ip4r_idx on geolite.blocks using gist(iprange); $$;这个示例一气呵成地展示了LOAD ARCHIVE的四个核心环节:
3.1BEFORE LOAD:加载前的数据库准备
BEFORE LOAD接受两种写法:
DO子句:直接跟一组dollar-quoted(以$$包裹)的 SQL 语句,语句之间用逗号分隔,例如创建扩展、建 schema;EXECUTE 'xxx.sql':从 SQL 文件批量读取语句执行,实现上支持 PostgreSQL 的 dollar-quoting 以及\i/\ir包含指令(与 psql 批处理行为一致)。
示例中先确保ip4r扩展与geoliteschema 存在,再执行geolite.sql完成建表。BEFORE LOAD的通用语义见 docs/command.rst 中Common Clauses一节的说明。
3.2 归档级目标库INTO
归档级INTO postgresql:///ip4r是BEFORE LOAD/FINALLY中 SQL 的执行目标,也是前面提到的硬性约束要求必须提供的连接。
3.3 两个 CSV 子命令
归档内有两个 CSV 数据文件,分别用LOAD CSV ... AND LOAD CSV ...声明:
Location文件被投影进geolite.location表,其中经纬度两列在INTO阶段通过USING表达式(format nil "(~a,~a)" longitude latitude)动态拼成 PostgreSQL 的point类型输入串;Blocks文件把 IP 段的起止整数通过(ip-range startIpNum endIpNum)变换成ip4r扩展的区间类型。
这正是 pgloaderUSING投影的典型用法:每条USING表达式都是合法的 Common Lisp 形式,在pgloader.transforms包环境下读取,并在运行时编译为原生代码(参见 docs/command.rst 中INTO一节),因此可以在加载过程中就地完成数据类型变换,而不是事后在数据库里二次加工。
3.4FINALLY DO:加载完成后的收尾 SQL
FINALLY DO中的 SQL 在所有数据子命令都成功导入后执行。示例用它创建 GiST 索引以加速 IP 区间查询——这是把"加载"与"建索引"编排进同一条命令的典型做法。
四、Archive Source Specification:FROM 归档源
FROM子句指定数据来源,可以是:
- 本地文件路径:直接指向一个 ZIP 文件;
- HTTP/HTTPS URI:pgloader 先把文件下载到本地,再进入解压流程。
下载实现位于 src/utils/archive.lisp 的http-fetch-file:
- 使用 Drakma 以二进制流方式请求(
:force-binary t :want-stream t),按 4096 字节缓冲区循环写盘; - 只有 HTTP 状态码为 200 才继续,否则记录
fatal日志并报错; - 从 URL 推导临时文件名时会先剥离 query string 与 fragment(
?之后的部分),避免文件名被参数污染; - 文件写入
$TMPDIR(默认临时目录)下,并返回下载后的路径名。
解压逻辑在expand-archive(src/utils/archive.lisp):
- ZIP 归档调用系统命令
unzip -o <归档> -d <展开目录>(见 src/utils/archive.lisp); - 展开目录为
$TMPDIR下的<归档名>/子目录;$TMPDIR未设置或指向不存在的目录时,回退到/tmp; - 解压完成后,后续所有子命令都从该顶层目录出发工作。
从源码看,tar、tgz归档同样通过tar xf展开,gz单文件归档则用gunzip -c输出为普通文件。解压前会校验归档文件存在性(probe-file),不存在直接报错。
五、Archive Sub Commands:归档子命令
5.1 支持哪些子命令
官方文档说明,目前归档上下文只支持三类数据命令:CSV、FIXED、DBF。这与源码规则一致(src/parsers/command-archive.lisp):
(defrule archive-command (or load-csv-file load-dbf-file load-fixed-cols-file))也就是说,你可以在一个归档里混合加载 CSV、定宽文本(FIXED)和 DBF 三类文件,命令间用AND串接。
5.2FROM FILENAME MATCHING:按正则匹配归档内文件
子命令的FROM支持FILENAME MATCHING子句,让命令不依赖归档目录的具体文件名——只要文件符合正则就命中,从而天然兼容"目录结构随版本变化"的发布包。
官方规定的matching子句语法为:
FROM [ ALL FILENAMES | [ FIRST ] FILENAME ] MATCHINGFROM FILENAME MATCHING ~/regex/:匹配第一个命中的文件(单个文件加载);FROM FIRST FILENAME MATCHING ~/regex/:与上者等价,FIRST可省略;FROM ALL FILENAMES MATCHING ~/regex/:匹配所有命中的文件(批量加载同名模式的文件)。
语法解析位于 src/parsers/command-csv.lisp:first-filename-matching与all-filename-matching两条规则分别生成:regex :first/:regex :all标记,正则本身用引号包裹。
正则匹配的底层实现在 src/utils/archive.lisp 的get-matching-filenames:用cl-ppcre的scan对展开目录中的每个文件路径做正则扫描,递归遍历(fad:walk-directory)后返回命中文件列表。
此外,matching子句还可以追加IN DIRECTORY <路径>限定搜索目录(src/parsers/command-csv.lisp),未指定时默认从当前工作目录搜索。
5.3 子命令如何定位归档内的文件
这是理解归档加载的关键实现细节:LOAD ARCHIVE在执行时会动态绑定全局变量*fd-path-root*为解压目录(src/parsers/command-archive.lisp),随后所有子命令中的相对文件名都会以该目录为根解析(见 src/sources/common/files-and-pathnames.lisp 与 src/sources/csv/csv-database.lisp)。所以子命令里只需要写"文件名模式",pgloader 会自动把它锚定到归档解压后的目录树上。
六、Archive Final SQL Commands:FINALLY DO
FINALLY DO:在所有数据加载完成后执行的 SQL 查询,典型用途是CREATE INDEX、加约束、重建触发器等收尾工作。
与BEFORE LOAD DO相同,FINALLY DO也使用 dollar-quoted、逗号分隔的 SQL 列表。运行统计中该阶段以finally行单独呈现(见下节运行输出)。
顺带一提:仓库测试文件 test/archive.load 中的收尾子句写作AFTER LOAD DO $$ create index ... $$;,这是同一语义在实际测试中的另一种措辞变体,最终效果一致。
七、通用子句(Common Clauses)
LOAD ARCHIVE内部各数据命令(CSV / DBF / FIXED)的WITH、SET、BEFORE/AFTER LOAD DO/EXECUTE、INTO列投影等均继承 pgloader 的通用子句体系,详见 docs/command.rst:
WITH:命令级选项,如skip header = 2、fields terminated by ','、fields optionally enclosed by '"'、fields escaped by double-quote,以及所有数据源通用的on error stop、batch rows = R、batch size = ... MB、prefetch rows = ...等批处理选项;SET:为 pgloader 打开的每个会话设置 PostgreSQL 会话参数;BEFORE LOAD DO/BEFORE LOAD EXECUTE:加载前执行 SQL 或 SQL 文件;AFTER LOAD DO/AFTER LOAD EXECUTE:加载完成后执行 SQL 或 SQL 文件(创建索引、约束、重开触发器的最佳时机);INTO列投影:目标列可以是源字段名,也可以是"列名 + PostgreSQL 类型 +USING表达式"的组合,支持加载时动态变换数据类型。
八、实战运行与结果解读
以 GeoLiteCity 为例,运行pgloader archive.load的完整输出记录在 docs/tutorial/geolite.rst(该教程与本文档配套,可交叉阅读)。关键输出如下:
... LOG Fetching 'http://geolite.maxmind.com/.../GeoLiteCity-latest.zip' ... LOG Extracting files from archive '.../T/pgloader//GeoLiteCity-latest.zip' table name read imported errors time ----------------- --------- --------- --------- -------------- download 0 0 0 11.592s extract 0 0 0 1.012s before load 6 6 0 0.019s ----------------- --------- --------- --------- -------------- geolite.location 470387 470387 0 7.743s geolite.blocks 1903155 1903155 0 16.332s ----------------- --------- --------- --------- -------------- finally 1 1 0 31.692s输出里的几个阶段与本文介绍的实现一一对应:
download:HTTP 抓取阶段(源码中以with-stats-collection ("download" :section :pre)统计,src/parsers/command-archive.lisp);extract:归档解压阶段(同样以:pre节统计);before load:BEFORE LOAD中的 6 条 SQL;- 两个表:两个 CSV 子命令的导入结果;
finally:FINALLY DO中的建索引语句。
加载完成后即可用 ip4r 扩展做 IP 归属查询验证数据质量:
select * from geolite.location l join geolite.blocks b using(locid) where iprange >>= '8.8.8.8';九、更多真实案例:从 HTTP 下载 DBF 归档
归档加载不限于 CSV。仓库测试 test/dbf-zip.load 展示了一条LOAD ARCHIVE变体:直接LOAD DBF从 HTTPS 下载一个 ZIP 归档,解压后加载其中的 DBF 文件,并在BEFORE LOAD DO里创建目标 schema:
LOAD DBF FROM https://www.insee.fr/fr/statistiques/fichier/2114819/france2016-dbf.zip with encoding cp850 INTO postgresql:///pgloader TARGET TABLE dbf.france2016 WITH truncate, create table BEFORE LOAD DO $$ create schema if not exists dbf; $$;它演示了两个要点:归档源可以来自 HTTPS;DBF 归档配合WITH ENCODING cp850处理非 UTF-8 的字符集数据。注意单文件加载(非LOAD ARCHIVE编排)时,pgloader 也会自动完成 HTTP 下载与归档展开,再按TARGET TABLE建表入库。
十、小结与进阶路径
LOAD ARCHIVE的核心价值是把「下载、解压、匹配文件、投影变换、加载、建索引」整条链路收敛为一条幂等的命令文件,非常适合定期更新的打包数据源(GeoIP、行政区划、行业统计年鉴等)。
继续深入可参考:
- 官方参考:docs/ref/archive.rst(本文档源文件);
- 配套教程:docs/tutorial/geolite.rst(含完整运行输出与查询验证);
- 可运行测试:test/archive.load(GeoLiteCity 全流程)、test/dbf-zip.load(HTTPS 下载 DBF 归档);
- 实现源码:src/utils/archive.lisp(下载/解压/匹配)、src/parsers/command-archive.lisp(语法规则);
- 通用子句:docs/command.rst(Common Clauses 完整说明)。
只要归档内文件命名规律稳定(哪怕是子目录嵌套),一条LOAD ARCHIVE命令就能全自动地把整个数据包搬进 PostgreSQL,这正是 pgloader「Migrate to PostgreSQL in a single command!」理念在文件型数据源上的集中体现。
- 数据工程
- ETL
- 数据集成
- 数据库
【免费下载链接】pgloader
Migrate to PostgreSQL in a single command!
相关推荐
pgloader 组合实战:LOAD ARCHIVE + LOAD FIXED 加载美国人口普查固定宽度文本(census-places 测试全解析)
pgloader 组合实战:LOAD ARCHIVE + LOAD FIXED 加载美国人口普查固定宽度文本(census places 测试全解析) 导读 本
数据工程ETL数据集成数据库华为TCX转换器:3步解决运动数据跨平台同步难题
华为TCX转换器:3步解决运动数据跨平台同步难题 你是否为华为手表记录的跑步数据无法在Strava、Garmin等主流平台分享而烦恼?华为TCX转换器正是为解决
数据工程ETL数据集成数据库Johnny-Five 使用 CD74HC4067 16 通道模拟输入扩展板(Arduino Nano Backpack)实战指南
Johnny Five 使用 CD74HC4067 16 通道模拟输入扩展板(Arduino Nano Backpack)实战指南 导读 本文基于 Johnny
数据工程ETL数据集成数据库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考