3

由于 PostgreSQL 文档https://www.postgresql.org/docs/current/ddl-system-columns.html

xmin此行版本的插入事务的标识(事务 ID)(行版本是行的单独状态;行的每次更新都会为同一逻辑行创建新的行版本)。

我们正在使用它(不要问为什么,只是发生)同步数据并从 PostgreSQL 源数据库中提取(ETL 中的 E)更改,我们通过间隔扫描,特别是 xmin 间隔来实现它,例如我们已经同步了 xmin 间隔0 到 10002,当我们进行下一次同步时,在这种情况下,我们将从 10003 开始​​搜索 xmin。如果每个已提交且可见的事务都按顺序编号,则没有问题,所有数据更改都会按顺序编号,但如果事务在它们初始化的那一刻进行编号,则可能会发生下一种情况:

  • 交易 10001 开始于 15:01
  • 交易 10002 开始于 15:02
  • 事务 10002 在 15:02 提交
  • 事务 10001 在 15:03 提交

如果我们在 15:02 进行了同步,并且在目标 DB 中获得了 max xmin:10002,在这种情况下,在从 xmin 10003 开始​​的下一次同步中,我们将跳过 xmin 10001 并且将丢失更改。

那么 PostgreSQL 事务 id (xmin) 是否顺序出现在提交的版本中?


同一个文档中还有 xmax :

xmax删除事务的标识(事务 ID),或零表示未删除的行版本。在可见行版本中,此列可能不为零。这通常表明删除事务尚未提交,或者尝试的删除已回滚。

所以我们可以看到计划删除行的事务(如果它将被提交),所以也许 xmin 也显示了将更改行的事务?但由于 xmin 描述,这是不可能的:

...对于此行版本。(行版本是行的单个状态;行的每次更新都会为同一逻辑行创建一个新的行版本。)

因为,正如所写,它必须与我们读取的行版本匹配,这可能只有脏读(当我们看到未提交的数据时)才能匹配,但这在 PostgreSQL https://www.postgresql.org/docs/current中不会发生/transaction-iso.html

脏读:允许,但在 PG 中不允许

4

2 回答 2

0

你的想法很好(除了你应该txid_snapshot_xmin作为一个分界点),但它有两个缺点:

  • 交易 ID 是从一个 4 字节的无符号整数计数器生成的,它会在某个时候回绕。然后比较xmin将不再起作用。

  • 为了避免这个问题,PostgreSQL 会在某个时候在旧行上设置“冻结”标志,这在 SQL 中是不可见的。对于冻结的行,xmin必须xmax忽略。

所以我认为这种可爱的技术注定要失败。

您应该研究逻辑解码,尤其是wal2json插件。这将使您能够可靠地捕获对数据的所有更改。

于 2020-02-12T15:15:31.207 回答
0

在写这个问题的过程中,我发现了这张幻灯片:https ://momjian.us/main/writings/pgsql/mvcc.pdf并且由于它的部分“MVCC Snapshot Timeline”我认为 xmin 在快照中的出现不能依次。

但我在这里找到了解决方案:https ://www.postgresql.org/docs/9.6/functions-info.html#FUNCTIONS-TXID-SNAPSHOT

txid_snapshot_xip(txid_snapshot) [setof bigint] 在快照中获取正在进行的事务 ID

所以我可以获得一组尚未提交的事务,并检查它们是否包含小于关闭当前同步过程间隔的最大 xid 的 xid,如果包含则需要等待一段时间,然后再次检查并继续提取数据如果同步间隔中的所有事务都已完成,否则将退出进程并出错。

于 2020-02-12T13:30:40.193 回答