在事务中设置时间戳

时间:2012-02-10 10:30:51

标签: sql database postgresql transactions

我们在数据库(PostgreSQL)上有以下两个重复且独立运行的任务:

会话1在事务中执行一些更新并设置更新数据集的时间戳:

BEGIN;
...
UPDATE table SET ..., timestamp = current_timestamp WHERE ...;
... // (A)
COMMIT;

会话2选择自上次运行以来更新的所有数据集:

SELECT * FROM table WHERE timestamp BETWEEN last_run AND current_timestamp;
last_run = current_timestamp;
...

如果会话2在会话1处于(A)时启动,则它将不会看到更改,因为在提交之前不会设置时间戳,而是设置为更早的值。 此外,后续会话2将不会选择更改,因为last_run已经大于时间戳。 所以问题是会话1分别在错误的时间将时间戳设置为错误的值,因此可能会“忘记”更改。

可能的解决方法是将更新的数据集ID存储在会话1中的另一个表中,并在会话2中从此表中选择并删除它们。 但也许有人有更好的想法...

4 个答案:

答案 0 :(得分:1)

一个简单的解决方案是避免选择可能存在争用的行。选择last_run和current_timestamp之间的行 - 间隔“1”分钟。根据事务量以及更新事务完成所需的时间,您需要确定应该缓冲多少时间。只要确保你也设置last_run = current_timestamp - interval'1'分钟,你就不应该在SELECT开始之前没有提交丢失的行。

答案 1 :(得分:1)

This sort of question不时出现 - 据我所知,唯一完全可靠的方法是做你所描述的,在第一个过程中在某些表中存储更新的ID并标记它们在第二个处理。基本上这是重新发明数据库中的消息队列。您已经很好地描述了一个天真的解决方案将如何错过更新。

导入过程标记更新的行可以非常轻松地完成,甚至可以使用数据表上的触发器实现。如果您只有一个消费者流程,那么它只需要delete from updated_item returning item_id来获取更新列表。这听起来好像很复杂,但恕我直言,它不是真的。能够监控积压的大小是免费出现的功能。

答案 2 :(得分:1)

IMHO session2必须select where zetimestamp > lastrun,并将last_run设置为MAX(timestamp of processed_items)。正在运行但在session2运行期间有未提交数据的会话将在 session2之前具有时间戳,如果将last_run设置为current_timestamp,则会在后续运行session2时隐藏。

此外:在大多数情况下,不需要使用current_timestamp。 自然时间戳的值不能大于current_timestamp,因此每个现有的时间戳都是< = current_timestamp,并且与之比较是没用的。

答案 3 :(得分:0)

current_timestamp 返回当前事务的开始时间,而不是当前时钟时间。检查clock_timestamp(),这将在当前交易中更改。