如何在PostgreSQL中跟踪查询进度?

时间:2014-10-06 08:32:14

标签: postgresql

是否有可以跟踪PostgreSQL中长查询进度的插件或脚本?

我的意思是我需要在Java中设置与Postgres中的某些更新查询相关的进度条值。我通过互联网搜索,但我刚发现一些文件在任何RDBMS系统中都没有任何官方实现。

5 个答案:

答案 0 :(得分:18)

我在这里找到了一个很好的答案:Tracking progress of an update statement

诀窍是先创建一个序列(根据需要命名):

CREATE SEQUENCE query_progress START 1;

然后附加到您的查询的WHERE部分:

AND NEXTVAL('query_progress')!=0

现在您可以查询进度:

SELECT NEXTVAL('query_progress');

最后不要忘记摆脱序列:

DROP SEQUENCE query_progress;

请注意,这很可能会使您的查询运行得更慢,每次检查进度时它都会增加值。上面的链接建议创建一个临时序列,但PostgreSQL似乎并没有让它们在会话中可见。

答案 1 :(得分:2)

我想出了一种可能有用的方法。但是,如果您想将其实现到Java等代码中,则可能需要进一步处理。

方法是检查页面内容以跟踪进度。

Postgresql具有一个名为pageinspect的扩展,可以检查特定表的页面信息。

详细信息在这里: https://www.postgresql.org/docs/current/pageinspect.html

也在这里花一些时间了解postgresql的页面布局

https://www.postgresql.org/docs/current/storage-page-layout.html

特别要注意xmin,xmax和ctid

我假设行插入遵循特定顺序的表。就像桌子的pkey一样。而且任何长时间的更新都可能会附加新页面。

我还假设主键ID大部分是连续的,几乎没有间隙。既然只是估计,我认为在这种情况下还可以。

但是,您无法通过进行SELECT relname, relpages FROM pg_class来查找总页数,因为它没有更新。

如果strage中不存在页面索引,您将遇到异常(但是即使在pg_class中没有更新页面,您也会找到该页面),因此请对“ page_index”进行一些“二进制搜索” ”以查找您拥有的最大页面。不需要很精确。

使用

SELECT backend_xid FROM pg_stat_activity WHERE pid = process-id

查找当前的交易ID。

使用

SELECT lp,t_xmin,t_xmax,t_ctid,t_bits,t_data FROM heap_page_items(get_raw_page('relation_name', page_index));

在我正在研究的样本中,它可能看起来像这样

  

SELECT lp,t_xmin,t_xmax,t_ctid,t_bits,t_data fromheap_page_items(get_raw_page('foo',3407000));

     

lp | t_xmin | t_xmax | t_ctid | t_bits | t_data

     

1 | 592744 | 592744 | (3407000,1)| 110000000111000000000000 | \ xd1100000000000000e4400000000000054010000611b0000631b0000

     

2 | 592744 | 592744 | (3407000,2)| 110000000111000000000000 | \ xd110000000000000104400000000000040010000611b0000631b0000

     

3 | 592744 | 592744 | (3407000,3)| 110000000111000000000000 | \ xd11000000000000011440000000000007c010000611b0000631b0000

t_data是数据。 lp是项目列表中的元组索引。 t_xmin和t_xmax是交易ID。 t_ctid是元组本身内指向元组的点。如果元组中有空值,则t_bits是NULL位图。

首先检查t_min = t_max,以及t_ctid(page_index,tuple_id)和lp是否相同。如果是这样,请检查t_xmin是否与您的交易ID相同。如果是这样,请检查数据。

请注意字节序和NULL位图。就我而言,它是big-endian(首先是LSB)。

在我的示例中,第一行有效。第一个BIGINT(8字节16进制数)是我要查找的已排序ID。因此,第一行的数据是

  

\ xd110000000000000

转换为0x101d(检查字节序)-> 4305

我知道我的最大ID是18209,最小ID是2857。我将工作分为8个部分,所以

  

(18209-2857)/ 8 = 1919

     

这是我运行的第一部分。所以

     

2857 + 1919 = 4776

这意味着我的子工作以2857 id开始,当前为4305。如果命中4776,则此线程完成!

这是

  

(4305-2857)/ 1919 =完成75.5%


限制

这不适用于哈希值更新。在我的情况下,id恰好像pkey一样顺序排序。然后计划者触发顺序读取。如果计划人员正在进行某种btree索引扫描以进行更新,这也应该起作用。

如果您有兴趣按索引顺序对物理行进行排序,请查看CLUSTER

同样,此方法不准确。并以上面强调的假设为前提。如果在程序中使用,应稀疏使用,以防止磁盘I / O产生额外开销

答案 2 :(得分:0)

没有。没有办法跟踪" live"查询进度。理论上,系统可以比较顶级进度与查询计划,并发出某种百分比读数。在实践中,我怀疑它会非常准确,我怀疑性能影响是否值得。

答案 3 :(得分:0)

您可以在表格中添加update_time列,并保留上次更新的值。如果您知道哪些记录应该受到影响,那么您也可以将update_time设置为当前时间,当您检查进度并知道受影响的行数时,您可以选择受影响的记录数update_time比您开始更新时更新。受影响的行数" new" update_time /要更新的记录数* 100为您提供进度百分比。

答案 4 :(得分:0)

不确定这是否是人们正在寻找的确切答案,但是我做了一个简单的函数,即通过测量随时间变化的页面大小来报告表插入的当前状态。这不是正在发生的事情的直接窗口,但是可以很好地近似于正在发生什么/是否正在发生什么。这也是对底线(表格被“填充”的速度)的可靠度量。

该函数返回表名称的列表,其中包含表及其所有关联索引的当前大小(以字节为单位,以人类可读单位为单位)以及增长率。

**奖励:它还包括临时文件活动

我特别用它来查看表的加载进度以及加载速度,这对于估算需要多长时间是很有用的(尽管对于大负载而言线性度越来越低)。

这是一个可移植的功能:

CREATE OR REPLACE FUNCTION table_build_monitor(
    IN table_or_schema_list TEXT[] DEFAULT NULL
,   IN sample_period INT DEFAULT 10
)
RETURNS TABLE (
    table_name TEXT
,   table_size TEXT
,   index_size TEXT
)
AS
$$
DECLARE
    table_list TEXT[];
    schema_list TEXT[];
BEGIN

DROP TABLE IF EXISTS table_sizes_loop;
CREATE TEMP TABLE table_sizes_loop (
    table_name_loop TEXT
,   table_size_bytes BIGINT
,   indexes_size_bytes BIGINT
)
;

select
    array_remove(array_agg(case when split_part(poo, '.',2) = '*' then split_part(poo, '.',1) else NULL end), NULL::TEXT)
,   array_remove(array_agg(case when split_part(poo, '.',2) = '*' then NULL else poo end), NULL::TEXT)
FROM unnest(array[table_or_schema_list]) poo
INTO schema_list, table_list
;

INSERT INTO table_sizes_loop

SELECT
    pg_tables.schemaname||'.'|| pg_tables.tablename as table_name
,   pg_relation_size(pg_tables.schemaname||'.'|| pg_tables.tablename) AS table_size_bytes
,   pg_indexes_size(pg_tables.schemaname||'.'|| pg_tables.tablename) AS indexes_size_bytes
FROM pg_tables
WHERE
    pg_tables.schemaname = ANY(schema_list)
OR  (pg_tables.schemaname||'.'|| pg_tables.tablename)::text = ANY(table_list)

UNION

SELECT
    'temp_files'
,   temp_bytes
,   NULL
FROM pg_stat_database
WHERE
    datname = current_database()
;

PERFORM pg_sleep(sample_period);

RETURN QUERY

with
    base AS
(
SELECT
    pg_tables.schemaname||'.'|| pg_tables.tablename as table_name_loop
,   pg_relation_size(pg_tables.schemaname||'.'|| pg_tables.tablename) AS table_size_bytes
,   pg_indexes_size(pg_tables.schemaname||'.'|| pg_tables.tablename) AS indexes_size_bytes

FROM pg_tables
WHERE
    pg_tables.schemaname::text = ANY(schema_list)
OR  (pg_tables.schemaname||'.'|| pg_tables.tablename)::text = ANY(table_list)

UNION

SELECT
    'temp_files'
,   temp_bytes
,   NULL
FROM pg_stat_database
WHERE
    datname = current_database()

)
SELECT
    table_name_loop
,   CASE WHEN table_name_loop = 'temp_files' THEN
        pg_size_pretty((base.table_size_bytes - tsl.table_size_bytes)/sample_period) || '/s'
    ELSE
            base.table_size_bytes
        || ' (' || pg_size_pretty((base.table_size_bytes))
        || ') - ' || pg_size_pretty((base.table_size_bytes - tsl.table_size_bytes)/sample_period) || '/s'
    END as table_size
,       base.table_size_bytes
    || ' (' || pg_size_pretty((base.indexes_size_bytes))
    || ') - ' || pg_size_pretty((base.indexes_size_bytes - tsl.indexes_size_bytes)/sample_period) || '/s'
    as table_size
FROM table_sizes_loop tsl
JOIN base USING (table_name_loop)
ORDER BY base.table_size_bytes DESC
;

END
$$
LANGUAGE plpgsql
;

要查看它,请使用如下所示的select语句,为整个模式传递一个模式限定的表列表或诸如“ schema。*”之类的内容-以及可选的采样周期(默认为10s)。

select * from table_build_monitor('{public.*}', 3);