我想出了一个可能有帮助的方法。但如果您想将其实现到 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 找到总页码,因为它没有更新。
如果页面索引在stage中不存在,你会遇到异常(但你会找到页面,即使它没有在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 FROM heap_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 相同。如果是,请检查数据。
注意字节序和空位图。就我而言,它是大端(LSB 优先)。
在我的示例中,第一行是有效的。第一个 BIGINT(8 字节 16 十六进制数)是我正在查找的排序 ID。所以第一行的数据是
\xd110000000000000
转换为 0x101d(检查字节序)--> 4305
我知道我最大的 id 是 18209,smallest_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 的额外开销