【问题标题】:Atomically set SERIAL value when committing transaction提交事务时以原子方式设置 SERIAL 值
【发布时间】:2016-02-29 20:06:20
【问题描述】:

假设我有一个表,我想使用serial 作为主键来请求客户端进行更改。客户会问“给我密钥 X 之后的更改”。如果不使用SERIALIZABLE 隔离级别或锁定,这很容易出现竞争条件。

事务 A 可以先启动,然后进行写入,然后需要很长时间才能提交。同时,事务 B 将在 A 提交之前启动并提交。来自 B 的写入将获得比来自 A 的写入更高的主键。如果客户端现在要求更改,它将错过来自 A 的仍未提交的写入,并记下最新的最高主键。因此,即使在 A 提交之后,客户端也永远不会看到该更改,因为它的密钥低于客户端已经获得的更改。

是否可以在提交时自动确定serial(或来自计数器的类似值)的值,以便我们保证在提交时它高于所有其他值,低于所有其他值之后会犯吗?如果不是,解决此问题的最佳方法是什么?

【问题讨论】:

  • 如果没有提交时间戳来提供一致的全局排序,这实际上在并发插入的情况下很难做到。 PostgreSQL 通过跟踪最旧和最新的当前 xid 以及在其中提交的位图来在 xid 级别为快照执行此操作。不过,我认为您不能真正将其转化为序列的使用。

标签: sql postgresql transactions commit


【解决方案1】:

Postgres 9.5 引入了与此问题相关的新功能:提交时间戳

您只需在postgresql.conf 中激活track_commit_timestamp(并重新启动!)即可开始跟踪提交时间戳。然后就可以查询了:

SELECT * FROM tbl
WHERE  pg_xact_commit_timestamp(xmin) >= '2015-11-26 18:00:00+01';

阅读 Postgres Wiki 中的 "Commit timestamp tracking" 章节。
相关utility functions in the manual.

函数波动性仅为VOLATILE,因为事务 ID (xid) 可以根据定义回绕。所以您无法在其上创建功能索引
您可以在有限的时间范围内在应用程序的函数包装器中伪造IMMUTABLE 波动性,但您需要了解其中的含义。相关案例及更多解释:

对于许多只对提交序列(而不是绝对时间)感兴趣的用例(比如你的?),使用xmin 转换为bigint“直接”(xmin::text::bigint)可能更有效) 而不是提交时间戳。 (xid 在内部是一个无符号整数,其上半部分不适合有符号的integer。)同样,请注意可能的 xid 回绕的限制。

出于同样的原因,提交时间戳不会无限期保留。对于中小型数据库,xid 环绕几乎不会发生 - 但如果集群存在足够长的时间,它最终会发生。详情请阅读手册中的"Preventing Transaction ID Wraparound Failures"章节。

【讨论】:

  • 有趣。如果我添加“ORDER BY pg_xact_commit_timestamp(xmin)”,执行这样的查询是否也很有效,就好像我正在使用索引列一样?
  • @NotNull:函数本身有索引支持来检索时间戳,但恐怕你不能在结果上创建函数索引。我在上面添加了一些。
  • 感谢@ErwinBrandstetter 对此有所了解。 xmin 是不专门属于表的系统列。我不会为该值创建索引,因为它是全球性的并且可以变得非常庞大,但这是我的观点。让我们看看欧文对此有何评论。无论如何,我认为可能无法创建这样的索引。
  • @ConsiderMe 什么啊?每个表都有xmin,它是一个隐藏的系统列,但它并不是以某种方式在所有表之间共享。你不能真正索引它的原因是 xid 环绕,与它以某种方式全局无关。
  • 一般的解决方案可能是只在修改需要以这种方式查询的表的事务中使用咨询锁(在标识每个客户端感兴趣的所有行的 ID 上),在为了保护主键免受我描述的竞争条件的影响。
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 2019-06-06
  • 1970-01-01
  • 2021-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-19
相关资源
最近更新 更多