1

问题:

给定表table_atable_b,我需要在更新时可靠地(同时)执行这样的操作table_a

  1. SELECT一些行来自table_a.
  2. 在应用程序代码中计算一些东西。
  3. UPDATE中的一排table_b

我需要防止发生这样的事情(称之为场景 A),table_b最终反映的是旧版本table_a

  • worker1从中获取行table_a
  • table_a已更新。
  • worker2从中获取行table_a
  • worker2更新table_b
  • worker1更新table_b

但这很好(称之为场景 B),因为table_b最终处于正确的状态:

  • worker1从中获取行table_a
  • table_a已更新。
  • worker2从中获取行table_a
  • worker1更新table_b
  • worker2更新table_b

交易:

一种解决方案是将整个事物包装在REPEATABLE READ事务中。thenworker1的事务在场景 A 中worker2失败,在场景 B 中的事务失败。没有办法区分场景,唯一的选择是重试失败的事务。

但在这两种情况下都是浪费:在场景 A 中,我们宁愿不重试worker1的事务,因为table_b已经完全更新了。在场景 B 中,我们宁愿worker2一开始就不要失败的事务,因为它正在做正确的事情。

行标记:

table_b如果我们从一开始就知道行的主键( :b_id) 并且每个工作人员都有一些唯一的 ID ( :worker_id),我们可以尝试其他方法。在步骤 1 之前添加一marktable_b并让每个工作人员执行此操作:

UPDATE table_b SET mark = :worker_id WHERE id = :b_id;

然后在第 3 步中添加一个WHERE子句:

UPDATE table_b SET ... WHERE ... AND mark = :worker_id;

现在worker1,根据需要,在两种情况下都不会在步骤 3 中更新任何行。

行标记在这里是一种合理的方法吗?我缺少什么缺点?这个问题的“规范”解决方案是什么?

澄清:我正在使用 PostgreSQL。

4

1 回答 1

2

在表 b 上使用事务和 SELECT ... FOR UPDATE。

这将导致行锁定,这将阻止工人 2 更新表 b,直到工人 1 提交。如果您使用 select for update 开始您的工作流程,那么在 worker 1 提交之前,worker 2 无法启动。

第二种方法(可能更好)是将更新包装在一个执行 select 的语句中,因此它是一个语句,并且会自动处理锁定。

行标记需要作为一个想法被丢弃,因为工人 2 在工人 1 提交之前不会看到标记的行......

于 2012-09-11T12:29:22.260 回答