有序序列生成

时间:2013-07-06 05:32:22

标签: postgresql sequence

有没有办法为表记录生成某种有序标识符?

假设我们有两个线程在进行查询:

主题1:

begin;
insert into table1(id, value) values (nextval('table1_seq'), 'hello');
commit;

主题2:

begin;
insert into table1(id, value) values (nextval('table1_seq'), 'world');
commit;

完全可能(取决于时间)外部观察者会看到(2,'世界')记录出现在(1,'hello')之前。

这没关系,但是我想要一种方法来获取自上次外部观察者检查它以来出现的'table1'中的所有记录。

那么,有没有办法按照插入的顺序获取记录?也许OID可以提供帮助吗?

4 个答案:

答案 0 :(得分:4)

没有。由于数据库表中存在无自然顺序行,因此您只需使用表中的值。

嗯, 可能在某种程度上滥用。{/ p>

元组ID(ctid)包含文件块编号和行的块中的位置。所以这代表了磁盘上的当前物理排序。以后添加的内容会更大ctid通常。您的SELECT语句可能如下所示

SELECT *, ctid   -- save ctid from last row in last_ctid
FROM   tbl
WHERE  ctid > last_ctid
ORDER  BY ctid

ctid的数据类型为tid。示例:'(0,9)'::tid

然而,不稳定作为长期标识符,因为VACUUM或任何并发UPDATE或其他一些操作可以随时更改元组的物理位置。但是,在交易期间,它是稳定的。如果您只是插入并且没有其他,那么它应该在本地用于您的目的。

除了now()列之外,我还会添加一个默认serial的时间戳列...

我还会让列默认填充您的id列(Postgres specific system columns cmin and ctidserial列)。这将在稍后阶段从序列中检索数字,而不是显式获取然后插入它,从而最小化(但不是消除)竞争条件的窗口 - 稍后插入较低id的机会。详细说明:

答案 1 :(得分:4)

您想要的是强制事务提交(使其插入可见)的顺序与它们执行插入的顺序相同。就其他客户而言,插入在发生之前尚未发生,因为它们可能会回滚并消失。

即使您没有将插入内容包装在显式begin / commit中,也是如此。事务提交,即使隐式完成,仍然不一定以与其自身插入的行相同的顺序运行。它受操作系统CPU调度程序排序决定等的影响。

即使PostgreSQL支持脏读,这仍然是真的。仅仅因为你开始给定顺序中的三个插入并不意味着他们将按顺序完成

没有简单或可靠的方法来执行您希望保留并发性的内容。您需要按顺序对单个工作者执行插入操作 - 或者使用Tometzky建议的表锁定,这基本上具有相同的效果,因为在任何给定时间只有一个插入线程可以执行任何操作。

您可以使用建议锁定,但效果是相同的。

使用时间戳无济于事,因为如果任何两个时间戳都没有知道,那么两行之间的时间戳尚未提交。

您不能依赖于只读取第一个“间隙”行的标识列,因为由于回滚,系统生成的列中的间隙是正常的。

我认为你应该退一步看看为什么你有这个要求,并且根据这个要求,你为什么要使用单独的并发插入。

也许你会在单个会话中做小块批量插入更好吗?

答案 2 :(得分:2)

如果您的意思是每个查询都看到world行,那么它还必须显示hello行,那么您需要执行以下操作:

begin;
lock table table1 in share update exclusive mode;
insert into table1(id, value) values (nextval('table1_seq'), 'hello');
commit;

这个share update exclusive mode是最弱的锁定模式,它是自我排斥的 - 一次只能有一个会话持有它。

请注意,这不会使这个序列无差距 - 这是一个不同的问题。

答案 3 :(得分:0)

我们发现了最新的PostgreSQL服务器的另一种解决方案,类似于@erwin的答案,但带有txid。

插入行时,不使用序列,而插入txid_current()作为行ID。每个新交易的ID都会单调增加。

然后,当从表中选择行时,将其添加到WHERE子句id < txid_snapshot_xmin(txid_current_snapshot())

txid_snapshot_xmin(txid_current_snapshot())对应于最旧的未清交易的交易索引。因此,如果在行19之前提交了行20,则它将被滤除,因为事务19仍将打开。提交事务19后,第19行和第20行都将可见。

当没有事务打开时,快照xmin将是当前正在运行的SELECT语句的事务ID。

返回的交易ID是64位,高32位是一个纪元,低32位是实际ID。

以下是这些功能的文档:https://www.postgresql.org/docs/9.6/static/functions-info.html#FUNCTIONS-TXID-SNAPSHOT

该创意的积分为tux3