13

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

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

线程 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, 'world') 记录出现在 (1, 'hello') 之前。

这很好,但我想要一种方法来获取自上次外部观察者检查以来出现的“table1”中的所有记录。

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

4

4 回答 4

6

不。由于数据库表中的行没有自然顺序,因此您只需处理表中的值即可。

嗯,有Postgres 特定的系统列cminctid可能会在某种程度上滥用。

元组 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列(aserialIDENTITY列)。这会在稍后阶段从序列中检索数字,而不是显式获取然后插入它,从而最小化(但不消除)竞争条件的窗口 -id稍后插入较低的机会。详细说明:

于 2013-07-06T12:42:33.830 回答
5

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

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

即使 PostgreSQL 支持脏读,这仍然是正确的。仅仅因为您以给定的顺序开始三个插入并不意味着它们将按该顺序完成

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

您可以使用咨询锁定,但效果是一样的。

使用时间戳将无济于事,因为您不知道对于任何两个时间戳是否有一行带有尚未提交的两个时间戳。

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

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

也许您最好从单个会话中进行小块批量插入?

于 2013-07-07T23:31:15.883 回答
2

如果您的意思是每个查询如果看到worldrow 它也必须看到hellorow 那么您需要执行以下操作:

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

share update exclusive mode是最弱的自排锁模式——一次只能持有一个会话。

请注意,这不会使这个序列没有间隙——这是一个不同的问题。

于 2013-07-06T08:26:11.663 回答
2

我们找到了另一个使用最新 PostgreSQL 服务器的解决方案,类似于@erwin 的答案,但使用 txid。

插入行时,不使用序列,而是txid_current()作为行 ID 插入。此 ID 在每个新事务上单调递增。

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

txid_snapshot_xmin(txid_current_snapshot())对应于最旧的仍处于打开状态的事务的事务索引。因此,如果第 20 行在第 19 行之前提交,它将被过滤掉,因为事务 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的想法。

于 2018-09-24T09:23:47.213 回答