关于 Postgres track_commit_timestamp (pg_xact_commit_timestamp) 的问题

Mor*_*ryx 5 postgresql transactions rollup

我正在设计一个并发安全的增量聚合汇总系统,track_commit_timestamp (pg_xact_commit_timestamp) 听起来很完美。但是我发现很少有关于它的评论,并且无法从源代码中弄清楚它是如何工作的。

希望有人知道我的一个或多个问题的答案:

  • 提交时间戳功能是否有可能产生乱序时间?我所追求的是一种识别自特定时间以来已更改的记录的方法,以便我可以获取任何以后的更改以进行处理。如果有相同的时间戳,我不需要它们以完美的提交顺序。

  • 最终实现中每行添加多少字节?我看到的讨论似乎在 12-24 个字节之间。曾讨论过为“以防万一”添加额外字节。这是 9.5 之前的版本,所以是一个世界之前的版本。

  • 时间戳是否在内部编入索引?用 B 树?我询问容量规划的原因。

  • 我在 StackOverflow 和设计讨论中看到时间戳不是无限期保留的,但无法找到它们存储时间的详细信息。

  • 关于启用 track_commit_timestamp 的性能影响的任何经验法则?我不需要所有表上的数据,但是,在我需要的地方,听起来它可能工作得很好。

  • 有什么问题吗?我尝试在测试表上运行 VACUUM FULL 并且 pg_xact_commit_timestamp 都没有改变。看起来像 VACUUM 这样的物理操作不应该改变任何东西,但很容易有一些我没有想到的东西。而且,老实说,我的快速 VACUUM 测试甚至可能没有任何意义。

非常感谢您的帮助!


我编辑了我的问题以阐明我要完成的任务,我希望根据更新标记跟踪已处理和未处理的数据。

select max(pg_xact_commit_timestamp(xmin)) from scan;--   2019-07-07 20:46:14.694288+10

update scan set quantity = 5 where quantity = 1; --       Change some data.

select max(pg_xact_commit_timestamp(xmin)) from scan; --  2019-07-10 09:38:17.920294+10

-- Find the changed row(s):
select * 
  from scan 
 where pg_xact_commit_timestamp(xmin) > '2019-07-07 20:46:14.694288+10'; 
Run Code Online (Sandbox Code Playgroud)

这个想法是对行进行增量和定期汇总。所以,

-- 跟踪上次汇总的时间戳。-- 等待 5 分钟(或其他时间)。 -- 查找当前的最大提交时间戳。-- 搜索提交时间戳在最后处理的时间戳和最大值之间的行。——卷起来。

单独的事务 ID 是行不通的,因为它们很容易乱序提交。这个时间戳系统不一定是 100% 完美的,但我的目标是非常接近完美。因此,对于重叠的开始/结束时间,有点时钟摆动甚至有点混乱可能是可以容忍的。

这个计划有明显的缺陷吗?

Mor*_*ryx 13

由于这个主题在档案中似乎并不多见,我想在继续之前添加一些细节。我在多个列表、论坛和直接交流中询问了相关问题。有几个人很友好地审查了源代码,提供了历史背景,并为我澄清了这一点。希望在这里留下一些细节会帮助其他人走上正轨。错误都是我的,显然,更正和改进更受欢迎。

  • 当事务的工作完成时分配提交时间戳,但这与提交时不同。WAL 编写器不会更新图章以保持它们按时间顺序排列。

  • 因此,提交时间戳绝对不是按顺序查找更改行的可靠机制。

  • 多个时钟。自动调整时钟。哦人类!

  • 如果您确实需要按顺序更改的序列,则可以选择逻辑解码或复制。(几周前我通过实验尝试了逻辑复制。最酷。事情。有史以来。)

  • 时间戳跟踪的成本是每个事务12 字节,而不是每行。所以,没那么糟糕。(时间戳为 8 个字节,事务 ID 为 4 个字节。)

  • 这都是现有交易系统的一部分,因此交易 ID 回滚的现实也适用于此。(在我的情况下并不可怕。)见:

    https://www.postgresql.org/docs/current/routine-vacuuming.html

  • 作为记录,您可以通过参数组设置在 RDS 上启用此选项。只需将 track_commit_timestamp 设置为 1 并重新启动。(该设置在 postgres.conf 中为“on”。)

  • “提交时间戳是在事务工作完成时分配的,但这与提交时不同。WAL 编写者不会更新标记来保持它们按时间顺序排列。” 这是否意味着您甚至不能依赖于此来确定发生的订单交易(即使您不关心确切的时间)? (3认同)

Lau*_*lbe 5

很多问题。

作为参考,源代码在src/backend/access/transam/commit_ts.c.

  1. 我不确定是否可以保证较晚的提交日志序列号意味着较晚的时间戳。如果系统时钟由于时间调整而向后跳,我当然不会完全依赖它。

  2. 时间戳根本不存储在行中,而是存储在pg_commit_ts数据目录的子目录中。每条记录占用 10 个字节:

    /*
     * We need 8+2 bytes per xact.  Note that enlarging this struct might mean
     * the largest possible file name is more than 5 chars long; see
     * SlruScanDirectory.
     */
    typedef struct CommitTimestampEntry
    {
        TimestampTz time;
        RepOriginId nodeid;
    } CommitTimestampEntry;
    
    Run Code Online (Sandbox Code Playgroud)

    事务日志中还有关于提交时间戳的信息,因此可以对其进行恢复。

  3. 不需要索引,因为时间戳的位置由事务号决定(每个事务都有一个固定的提交时间戳位置)。见TransactionIdToCTsPage

  4. 如果我正确理解代码,时间戳与交易号一样长。

  5. 我不知道开销是多少,但它可能不是很大。

  6. 为什么要VACUUMVACUUM (FULL)更改提交时间戳?那将是一个错误。

现在我明白你想用提交时间戳实现什么,一句话(我希望人们能马上说出真正的问题):

提交时间戳不是适合您的工具。您无法索引表达式,因为pg_xact_commit_timestamp它不是一成不变的。

选择简单明了的解决方案,并添加一个timestamp with time zone带有BEFORE触发器的额外列,将其设置为current_timestamponINSERTUPDATE。那可以编入索引。

一位名人说过,过早优化是万恶之源。