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的主要内容,如果未能解决你的问题,请参考以下文章
COPY FROM .csv 文件到远程 PostgreSQL 数据库(在 Linux 服务器上运行)