我如何找出我的 PostgreSQL 查询有多远?

Mar*_*iot 39 postgresql

我很清楚我的 SELECT...INTO 查询将实际处理多少行(例如,我知道有多少行会实现)。

我知道 Postgres 不会告诉我百分比完整性,有没有办法(深埋在日志、系统表或其他地方)我可以找出有多少行已被泵入目标表或已被 SELECT 查询读取?

小智 40

正如 Daniel Vérité 所提到的,似乎没有通用的解决方案。将数据从文件加载到表中时,可以使用以下技术来获取加载进度。

COPY 命令控制台进度条

创建一个空表。

CREATE TABLE mytest (n int);
Run Code Online (Sandbox Code Playgroud)

创建一个包含 1000 万行的数据文件以加载到表中。

$ seq 10000000 > /tmp/data.txt
Run Code Online (Sandbox Code Playgroud)

将文件中的数据加载到表中并显示进度条。

$ pv /tmp/data.txt | psql -c "COPY mytest FROM STDIN;"
Run Code Online (Sandbox Code Playgroud)

演示

在此处输入图片说明

这是如何工作的

通过使用复制命令 STDIN 选项,我们可以为来自另一个进程的复制操作提供数据。pv 命令将输出一个文件并跟踪它的进度,显示进度条、预计到达时间、总时间和数据传输速率。

COPY 命令图形进度条

使用相同的通用技术,我们可以在图形应用程序或基于 Web 的应用程序中显示进度条。例如,使用 python,psycopg2模块允许您使用您选择的文件对象调用复制命令。然后,您可以跟踪读取了多少文件对象并显示进度条。

  • 我以前没有遇到过 `pv` 命令,默认情况下它没有安装在我的 Debian 服务器上,但它在 repo 中。描述说“pv(管道查看器)可以插入两个进程之间的任何正常管道中,以直观地指示数据通过的速度”。一个非常有用的命令! (4认同)

Dan*_*ité 28

似乎没有通用的、受支持的方法,但有一些技巧可用于在有限的上下文中评估单个查询的进度。这里是其中的一些。

序列

当 SELECT 或 UPDATE 查询包含 any nextval(sequence_name),或者 INSERT 具有目标列的nextval默认值时,可以在另一个会话中使用 重复查询当前序列值SELECT sequence_name.last_value。它有效是因为序列不受事务的限制。当执行计划使得序列在查询期间线性递增时,它可以用作进度指示器。

pgstattuple

所述pgstattuple等的contrib模块提供了可以在数据页直接偷看功能。看起来,当元组被插入到一个空表中并且尚未提交时,它们会被计入函数的dead_tuple_count字段中pgstattuple

9.1 演示:创建一个空表

CREATE TABLE tt AS (n numeric);
Run Code Online (Sandbox Code Playgroud)

让我们在其中插入 10M 行:

INSERT INTO tt SELECT * FROM random() from generate_series(1,10000000);
Run Code Online (Sandbox Code Playgroud)

在另一个会话中,在插入期间每秒检查 pgstattuple:

$ while true;
   do psql -Atc "select dead_tuple_count from pgstattuple('tt')";
   sleep 1;
  done
Run Code Online (Sandbox Code Playgroud)

结果:

0
69005
520035
1013430
1492210
1990415
2224625
2772040
3314460
3928660
4317345
4743770
5379430
6080950
6522915
7190395
7953705
8747725
9242045
0

当插入完成时,它回落到 0(所有元组都变得可见和活跃)。

当表不是新创建的时候也可以使用这个技巧,但初始dead_tuple_count值可能具有非零值,并且如果其他写入活动如 autovacuum 正在进行,它也可能同时更改(大概?不确定什么级别的与 autovacuum 预期的并发性)。

但是,如果表是由语句本身(CREATE TABLE ... AS SELECTSELECT * INTO newtable)创建的,则不能使用它,因为创建是事务处理的。解决方法是创建没有行的表 (add LIMIT 0) 并在下一个事务中填充它。

请注意,pgstattuple这不是免费的:它在每次调用时都会扫描整个表。它也仅限于超级用户。

定制柜台

在 Pavel Stehule 的博客中,他提供了一个 用 C 实现的计数器函数,该函数在指定的执行次数时引发 NOTICE。您必须以某种方式将该函数与查询结合起来,以便让执行程序调用它。通知在查询期间发送,它们不需要单独的会话,只需要一个显示它们的 SQL 客户端(psql显然是候选对象)。

INSERT INTO 返工以引发通知的示例:

/* transformation */
INSERT INTO destination_table
   SELECT (r).*
  FROM (SELECT counter(to_destination_table(_source), 1000, true) r
           FROM source _source) x
Run Code Online (Sandbox Code Playgroud)

stackoverflow 上的相关问题,对于函数:
How to report progress from long-running PostgreSQL function to client

未来的选择?

截至 2017 年 5 月,向开发者社区提交了一个有希望的补丁: [PATCH v2] 进度命令,用于监控长时间运行的 SQL 查询的进度

这可能最终成为 PostgreSQL 11 或更高版本中的通用解决方案。想要参与正在进行的功能的用户可以应用最新版本的补丁并尝试建议的PROGRESS命令。


pet*_*erh 6

正如 @AmirAliAkbari 在他的回答中提到的,在进度报告功能不会扩展之前,这里有一个操作系统级别的解决方法。

这仅适用于 Linux,但可能对于任何操作系统都可以轻松通过谷歌搜索类似的解决方案。

PostgreSQL 的最大优点和缺点是,它的所有后端都是简单的单线程进程,使用lseek()read()write()操作它们的表文件,同时它们在共享内存和锁上进行交互。

结果,它的所有后端进程始终在单个查询上工作,该查询可以很容易地找到并且很容易strace找到。

首先,您可以从 a 中看到后端 PID SELECT * FROM pg_stat_activity;

29805270 | dbname  | 20019 |    16384 | username  |                  |             |                 |          -1 | 2018-09-19 21:31:57.68234+02  | 2018-09-19 21:31:59.435376+02 | 2018-09-\
20 00:34:30.892382+02 | 2018-09-20 00:34:30.892386+02 | Client          | ClientRead | active              |       92778 |        92778 |  INSERT INTO ...something...
Run Code Online (Sandbox Code Playgroud)

第三列是 pid。在PostgreSQL中,它与后端的Linux进程pid相同。

接下来,您可以对其进行跟踪,例如通过strace -p 20019 -s 8192: (-s 8192很有用,因为 postgresql 使用 8192 字节长的块)。

sendto(10, "C\0\0\0\17INSERT 0 1\0Z\0\0\0\5T", 22, 0, NULL, 0) = 22
recvfrom(10, "Q\0\0\1\267 INSERT <removed by @peterh>", 8192, 0, NULL, NULL) = 440
sendto(10, "C\0\0\0\17INSERT 0 1\0Z\0\0\0\5T", 22, 0, NULL, 0) = 22
lseek(298, 343634345)...
read(298, "<block data which was read in>"....
write(298, "<block data which was written out>"...
Run Code Online (Sandbox Code Playgroud)

含义:

  • sendto如果后端向客户端回复某些内容,就会发生这种情况。在示例中,它回答查询的结果INSERT
  • recvfrom如果后端从客户端获取某些内容,就会发生这种情况。它通常是一个新查询,在示例中是另一个INSERT.
  • lseek如果后端在表文件中切换位置,就会发生这种情况。
  • read如果后端从表文件中读取一个块,就会发生这种情况。
  • write如果后端将块写入表文件中,就会发生这种情况。

对于readwrite,您还可以在表中看到该块的内容。它对于理解它在做什么以及在哪里有很大帮助。

在 的情况下recvfrom,您可以看到后端实际查询到的内容。