备用数据库上的可重复读取事务 运行 如何看到瞬态?

How a Repeatable Read transaction run on the standby could see a transient state?

在关于t运行saction隔离级别的PostgreSQL文档中,我们可以阅读到以下内容:

While performing all permanent database writes within Serializable transactions on the master will ensure that all standbys will eventually reach a consistent state, a Repeatable Read transaction run on the standby can sometimes see a transient state which is inconsistent with any serial execution of serializable transactions on the master.

The above text is at the bottom of http://www.postgresql.org/docs/9.4/static/transaction-iso.html

在什么情况下,热备份上的可重复读取 t运行saction 运行 会看到不一致的 t运行sient 状态?

看起来批处理和类似应用程序可能会发生这种情况。

我在关于 SSI 的 wiki 页面中找到了一些关于该主题的信息,并从 postgresql-hackers 邮件列表中找到了一些背景详细信息,他们在其中讨论了如何处理热备份上的序列化。

Kevin Grittner writes in his post:

The most common and alarming situation where this occurs, in my opinion, is batch processing. This is extremely common in financial applications, and tends to show up in a lot of other places, too.

[..]

Queries on the standby can, however, see transient anomalies when they run queries which would cause a serialization failure if run on the master at the same point in the transaction stream. This can only occur when, of two concurrent transactions, the one which appears to run second because the other can't read what it wrote, commits first.

[..]

Under SSI, one of these transactions will be canceled to prevent this. Our implementation will always allow the update which closes the batch to complete, and either the insert or the select of the detail will be rolled back with a serialization failure, depending on the timing the actions inside those transactions. If the insert fails, it can be retried, and will land in the new batch -- making the list of the batch which omits it OK. If the listing of the batch details is canceled, it will be because the insert into the old batch committed before it recognized the problem, so an immediate retry of the select will see the complete batch contents.

A hot standby can't really take part in the predicate locking and transaction cancellation on the master.

Geir Bostad的回答是正确的;我的回应只是为了提供一个例子和可能的解决方法,因为人们常常难以理解这个问题。除了另一个答案中引用的 Wiki 页面之外,还有 a separate Wiki page just for examples which includes a "Deposit Report" example 看到一个批次已关闭但还无法看到该批次的最终详细信息。

热备是只读的,所以它只能看到瞬态序列化异常;数据最终将在一些后续事务中进入一致状态。这与主服务器上的 repeatable 读取事务可能发生的情况相同,而主服务器上的可序列化事务则不可能发生。为防止混淆,热备用不允许可序列化事务,因为您不能(还)保证在那里看到真正可序列化的事务行为。已经讨论过向 WAL 流添加信息以识别可以启动可序列化事务的点,而不会有看到异常的风险,类似于 START TRANSACTIONDEFERRABLE 选项;这个(或类似的东西)可能会在未来的某个版本中添加。

对于使用快照隔离的只读事务(包括在 Oracle 或 9.1 之前的 PostgreSQL 版本中标识为可序列化的事务),最常见的异常类型似乎是 SELECT 列表或总结 batch 在事务中显示为 "closed",但没有看到应包含在该批次中的所有工作。要看到这一点,关闭批处理的事务必须 "overlap"(运行 同时)一些修改批处理细节的事务,关闭批处理的事务必须先提交,然后(在关闭批处理的事务提交后)在事务修改批详细信息提交之前)只读事务必须启动并获取其快照。

为了防止这种情况,最简单的技术是 "promote the conflict" 在主数据库上从读写依赖到写冲突。例如,在上面引用的 "Deposit Report" 示例中,可以将 deposit_total 列添加到控件 table 中,当 deposit_no 递增时,它由 AFTER 触发器设置为零,并且可以将 AFTER 触发器添加到收据 table 以根据每张收据的金额更新金额。这将导致基于控制记录的写入冲突的序列化失败,从而阻止异常传播到副本。