Answers:
正如DanielVérité提到的那样,似乎没有通用的解决方案。从文件将数据加载到表中时,可以使用以下技术来获取加载进度。
COPY命令控制台进度条
创建一个空表。
CREATE TABLE mytest (n int);
创建一个具有1000万行的数据文件以加载到表中。
$ seq 10000000 > /tmp/data.txt
将文件中的数据加载到表中并显示进度条。
$ pv /tmp/data.txt | psql -c "COPY mytest FROM STDIN;"
演示版
如何运作
通过使用复制命令STDIN选项,我们可以从另一个进程中输入用于复制操作的数据。pv命令将输出一个文件并跟踪它的进度,显示进度条,ETA,已用的总时间和数据传输速率。
COPY命令图形进度条
使用相同的通用技术,我们可以在图形应用程序或基于Web的应用程序中显示进度条。例如,使用python的psycopg2模块,您可以使用所选的文件对象调用copy命令。然后,您可以跟踪已读取了多少文件对象并显示进度条。
似乎没有通用的,受支持的方法,但是在有限的上下文中可以使用一些技巧来评估单个查询的进度。这里是其中的一些。
当SELECT或UPDATE查询包含any nextval(sequence_name)
,或者INSERT的目标列nextval
默认为时,可以在另一个会话中使用重复查询当前序列值SELECT sequence_name.last_value
。之所以有效,是因为序列不受事务限制。当执行计划使得该序列在查询过程中线性增加时,它可以用作进度指示器。
所述pgstattuple等的contrib模块提供了可以在数据页直接偷看功能。看起来,当元组插入到空表中并且尚未提交时,它们将在函数的dead_tuple_count
字段中计数pgstattuple
。
演示9.1:创建一个空表
CREATE TABLE tt AS (n numeric);
让我们在其中插入1000万行:
INSERT INTO tt SELECT * FROM random() from generate_series(1,10000000);
在另一个会话中,在插入过程中每秒检查一次pgstattuple:
$ while true;
do psql -Atc "select dead_tuple_count from pgstattuple('tt')";
sleep 1;
done
结果:
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 SELECT
或SELECT * INTO newtable
)创建的,则不能使用它,因为创建是事务处理的。解决方法是创建没有行的表(add LIMIT 0
)并在下一个事务中填充它。
请注意,pgstattuple
这不是免费的:每次调用时它都会扫描整个表。而且它仅限于超级用户。
在Pavel Stehule的博客中,他提供了 用C实现的计数器功能,该功能以指定的执行次数引发NOTICEs。您必须以某种方式将函数与查询结合起来,以使执行程序将其调用。通知是在查询过程中发送的,它们不需要单独的会话,只需一个显示它们的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
有关函数的stackoverflow的相关问题:
如何从长期运行的PostgreSQL函数向客户端报告进度
截至2017年5月,有一个很有希望的补丁已提交给开发者社区: [PATCH v2] Progress命令可监视长时间运行的SQL查询的进度
最终可能会成为PostgreSQL 11或更高版本中的通用解决方案。希望参与正在进行的功能的用户可以应用最新版本的补丁并尝试建议的PROGRESS
命令。
就像@AmirAliAkbari在回答中提到的那样,在进度报告功能不会得到扩展之前,这是一种OS级别的解决方法。
这仅在Linuxes上有效,但是对于任何操作系统,可能都有容易找到的类似解决方案。
最大的优势和劣势也将PostgreSQL的,其所有后端的是简单的单线程程序,使用lseek()
,read()
并write()
操纵其表文件,而他们是在共享MEM和锁交互。
结果,它的所有后端过程始终在单个查询上运行,可以轻松找到它,并且很容易strace
d。
首先,您可以从中查看后端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...
第三列是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>"...
含义:
sendto
如果后端对客户端进行响应,则会发生这种情况。在示例中,它回答INSERT
查询结果。recvfrom
如果后端从客户那里得到东西,则会发生这种情况。在示例中,它通常是一个新查询,而另一个查询INSERT
。lseek
如果后端切换表文件中的位置,则会发生这种情况。read
如果后端从表文件中读取一个块,则会发生这种情况。write
如果后端将一个块写出到表文件中,则会发生这种情况。对于read
和write
,您还可以在表中查看该块的内容。它可以帮助您理解,做什么以及在哪里。
在的情况下recvfrom
,您可以查看实际查询中后端的功能。
正如在其他答案中所说的那样,当前一般没有直接的进度报告方法。
PostgreSQL可以在命令执行期间报告某些命令的进度。当前,唯一支持进度报告的命令是VACUUM。将来可能会扩大。
但是,从9.6开始,无论何时VACUUM
运行,该pg_stat_progress_vacuum
视图将为当前正在清理的每个后端(包括自动清理工作进程)包含一行。有关更多详细信息,请pg_stat_progress_vacuum
参见文档:27.4进度报告。
pv
命令,并且默认情况下它没有安装在我的Debian服务器上,但是它在仓库中。描述说:“可以将pv(管道查看器)插入两个进程之间的任何常规管道中,以直观方式指示数据通过的速度”。一个非常有用的命令!