是否有可以跟踪PostgreSQL中长查询进度的插件或脚本?
我的意思是我需要在Java中设置与Postgres中的某些更新查询相关的进度条值。我通过互联网搜索,但我刚发现一些文件在任何RDBMS系统中都没有任何官方实现。
答案 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)
不确定这是否是人们正在寻找的确切答案,但是我做了一个简单的函数,即通过测量随时间变化的页面大小来报告表插入的当前状态。这不是正在发生的事情的直接窗口,但是可以很好地近似于正在发生什么/是否正在发生什么。这也是对底线(表格被“填充”的速度)的可靠度量。
该函数返回表名称的列表,其中包含表及其所有关联索引的当前大小(以字节为单位,以人类可读单位为单位)以及增长率。 strong>
**奖励:它还包括临时文件活动
我特别用它来查看表的加载进度以及加载速度,这对于估算需要多长时间是很有用的(尽管对于大负载而言线性度越来越低)。
这是一个可移植的功能:
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);