PostgreSQL COPY 管道输出到 gzip 然后到 STDOUT

Posted

技术标签:

【中文标题】PostgreSQL COPY 管道输出到 gzip 然后到 STDOUT【英文标题】:PostgreSQL COPY pipe output to gzip and then to STDOUT 【发布时间】:2021-06-16 19:27:08 【问题描述】:

以下命令运行良好

$ psql -c "copy (select * from foo limit 3) to stdout csv header"

# output
column1,column2
val1,val2
val3,val4
val5,val6

但以下不是:

$ psql -c "copy (select * from foo limit 3) to program 'gzip -f --stdout' csv header"

# output
COPY 3

为什么我有 COPY 3 作为此命令的输出?我希望在通过gzip 之后,输出将是压缩的 CSV 字符串。

下面的命令有效,例如:

$ psql -c "copy (select * from foo limit 3) to stdout csv header" | gzip -f -c

# output (this garbage is just the compressed string and is as expected)
߉T`M�A �0 ᆬ6�BL�I+�^E�gv�ijAp���qH�1����� FfВ�,Д���������+��

如何制作一个直接将结果通过管道传送到gzip并将压缩字符串发送到STDOUT的SQL命令?

【问题讨论】:

压缩流量是网络层的工作,而不是 SQL 语句的工作。它过去可以通过 OpenSSL 用于 postgres,直到压缩从 SSL 中针对security reasons 启动。您仍然可以使用压缩 ***,但如果远程是公共云服务,则可能不会。 gzip'ing the traffic is the job of a network layer, not the job of SQL statements -> 这是任意的。有问题的 SSL 命令完全能够输出压缩的 gzip csv 文件,我认为这不是“他们的工作”的根本原因,而不仅仅是压缩 STDOUT 输出。 SQL 客户端不像wget。在服务器关闭连接之前,它并不意味着流式传输非结构化内容。 COPY 使用逐行协议,客户端需要解释它接收到的字节。如果它们被 gzip 压缩并且协议不知道,这是不可能的。 @DanielVérité 我明白了,有道理。谢谢你的解释 【参考方案1】:

当您使用COPY ... TO PROGRAM 时,PostgreSQL 服务器进程(后端)会启动一个新进程并将文件通过管道传输到该进程的标准输入。该过程的标准输出丢失。仅当被调用程序将数据写入文件或类似文件时,使用COPY ... TO PROGRAM 才有意义。

如果您的目标是压缩通过网络传输的数据,您可以在连接字符串中使用sslmode=require sslcompression=on 以使用SSL network compression 功能I built into PostgreSQL 9.2。不幸的是,这已被弃用,并且大多数 OpenSSL 二进制文件都禁用了该功能。

目前有原生网络压缩patch under development,但能否制作v14还有待商榷。

除此之外,你目前无法得到你想要的。

【讨论】:

“没有办法实现你想要的”+ PostgreSQL 不提供内置 gzip 压缩,除非通过特定的 lib 安装(这使得它在 AWS RDS 上不可用)这很烦人.我不明白为什么没有考虑到这样一个基本要求(gzip postgresql 命令的输出)。 COPY ... TO STDOUT重定向到一个文件然后压缩该文件都将在客户端发生,因此它不会避免通过网络发送所有未压缩的数据。 我还没有看到没有 zlib 的 PostgreSQL 安装,我怀疑亚马逊会这样做。为了更详细地解释这个问题,我在答案中添加了更多信息。 有趣。谢谢你分享这个。作为理解问题,是否有任何理由为什么从未考虑过向copy to stdout 添加compression 选项,或者在copy to program 'gzip -f -c 的情况下添加一个选项来检索通过程序管道传输的输出?跨度> 我猜从来没有人会做一些奇怪的事情,比如在数据库服务器上调用一个程序,将数据输入其中,读取其标准输出并将结果转发回客户端。跨度> 【参考方案2】:

copy 正在服务器上运行 gzip,而不是将 STDOUT 从 gzip 转发到客户端。

您可以改用 \copy,它会在客户端上运行 gzip:

psql -q -c "\copy (select * from foo limit 3) to program 'gzip -f --stdout' csv header"

这与您在问题中显示的 gzip 管道基本相同。

【讨论】:

谢谢。当我这样做时,根本没有输出,这让我相信没有任何东西发送到 STDOUT。我尝试使用-o 'hello.csv.gz',但它再次将“COPY 3”发送到此文件。 其实我错了,我只是忘记了` in \copy` - 它似乎可以使用它 啊,但你说it would run gzip on the client——为什么会这样?绝对没有办法在服务器上运行它并捕获输出? 不,您不能在服务器上运行 gzip 并捕获输出,除非您将输出保存到文件并复制该文件。用例是什么?你想减少网络输出吗? 我确实在尝试减少网络输出。用例是在 Amazon RDS PostgreSQL 上执行的查询,它返回大量负载(除了 gzip 之外,我们无法将其变小)。因为查询的目标是 RDS,所以我们无法访问服务器的 psql 创建的文件。【参考方案3】:

如果目标是压缩副本的输出,使其在网络上传输得更快,那么...

psql "postgresql://ip:port/dbname?sslmode=require&sslcompression=1"

如果启用,它应该显示“压缩活动”。不过,这可能需要启用一些服务器配置变量。

或者你可以简单地使用 ssh:

ssh user@dbserver "psql -c \"copy (select * from foo limit 3) to stdout csv header\" | gzip -f -c" >localfile.csv.gz

但是...当然,您需要通过 ssh 访问 db 服务器。

如果你没有 ssh 到 db 服务器,也许你有 ssh 到同一个数据中心的另一个盒子,它有一个快速的网络链接到 db 服务器,在这种情况下,你可以 ssh 到它而不是 db 服务器.数据将在该盒子和数据库之间未压缩地传输,在盒子上压缩,并通过 ssh 管道传输到您的本地计算机。这甚至会节省数据库服务器上的 cpu,因为它不会进行压缩。

如果这不起作用,那么,为什么不将 ssh 命令放入“to program”并让服务器通过 ssh 将其发送到您的计算机?您必须设置路由器并打开一个端口,但您可以这样做。当然,您必须找到一种将密码放入 ssh 命令行的方法,这通常是一个很大的禁忌,但可能只是一次。或者直接使用 netcat,不需要密码。

另外,如果你想要速度,请使用 zstd 而不是 gzip。

这是一个使用 netcat 的例子。我刚刚测试了它,它确实有效。

在 192.168.0.1 的目标机器上:

nc -lp 65001 | zstd -d >file.csv

在另一个终端:

psql -c "copy (select * from foo) to program 'zstd -9 |nc -N 192.168.0.1 65001' csv header" test

注意 netcat 的 -N 选项。

【讨论】:

【参考方案4】:

您可以使用复制到程序:

COPY foo_table to PROGRAM 'gzip > /tmp/foo_table.csv' delimiters',' CSV HEADER;

【讨论】:

我希望将压缩输出发送到 STDOUT,而不是文件 如果您需要从 psql 执行此操作,您可以使用 \copy,这是一个 psql 命令,不同于 copy,这是一个 sql 命令

以上是关于PostgreSQL COPY 管道输出到 gzip 然后到 STDOUT的主要内容,如果未能解决你的问题,请参考以下文章

postgresql----COPY之表与文件之间的拷贝

Postgresql 锁和 copy_expert

使用 PostgreSQL 时使用 COPY 命令处理错误

COPY FROM .csv 文件到远程 PostgreSQL 数据库(在 Linux 服务器上运行)

postgresql copy from 字符串转换为时间类型

postgresql数据备份小结