【发布时间】:2012-09-11 07:16:35
【问题描述】:
问题:
给定表 table_a 和 table_b,我需要在 table_a 更新时可靠地(并同时)执行这样的操作:
-
SELECT来自table_a的一些行。 - 在应用程序代码中计算一些东西。
-
UPDATEtable_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 事务中。那么worker1的事务在场景A中失败,worker2的事务在场景B中失败。没有办法区分场景,唯一的选择就是重试失败的事务。
但这在两种情况下都是浪费:在情况 A 中,我们宁愿不重试worker1 的事务,因为table_b 已经完全更新。在场景 B 中,我们一开始就不想让worker2 的事务失败,因为它做的事情是正确的。
行标记:
如果我们从一开始就知道table_b 中行的主键(:b_id)并且每个worker 都有一个唯一的ID(:worker_id),我们可以尝试其他方法。在table_b 中添加mark 列,并让每个工作人员在第1 步之前执行此操作:
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。
【问题讨论】:
-
我建议你标记你的问题(被迁移到dba.stackexchange.com)
-
计算是否必须在应用程序代码中完成,和/或是否必须存储?最好的方法通常是避免存储计算的数据,或者让服务器自动计算(如果可能的话)值。
-
@ypercube 这比 DBA 编码要多得多;它不是在运行数据库,而是正确的并发编程。
-
@Craig:名称 (dba.stackexchange.com) 是一个相当糟糕的选择:What kind of questions can I ask here?
-
@Damien_The_Unbeliever 对于这个问题,是的,它必须在应用程序代码中,是的,它必须被存储。我认为,基本问题是一样的。
标签: sql postgresql concurrency transactions