正在设置事务内部的时间戳



我们在数据库(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中从该表中选择并删除它们。但也许有人有更好的主意。。。

这种问题不时出现——据我所知,唯一完全可靠的方法是按照您所描述的进行,在第一个过程中将更新的ID存储在某个表中,并在第二个过程中将它们标记为已处理。基本上,这是在数据库中重新创建消息队列。您已经很好地描述了一个天真的解决方案将如何错过更新。

让导入过程标记更新的行可以很容易地完成,甚至可以使用数据表上的触发器来实现。如果您只有一个使用者进程,那么它所要做的就是delete from updated_item returning item_id来获得更新列表。这听起来要复杂得多,但事实并非如此。能够免费监控积压工作的规模等功能也会出现。

一个简单的解决方案是避免选择可能存在争用的行。选择last_run和current_timestamp之间的行-间隔为'1'分钟。您认为应该缓冲的确切时间是您必须根据事务量以及更新事务完成所需的时间来决定的。只要确保还设置了last_run=current_timestamp-interval"1"分钟,就不会出现SELECT开始前未提交的丢失行的问题。

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

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

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

最新更新