【问题标题】:Transactions vs row marking for reliable SELECT + UPDATE可靠的 SELECT + UPDATE 的事务与行标记
【发布时间】:2012-09-11 07:16:35
【问题描述】:

问题:

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

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

我需要防止发生这种情况(称为场景 A),其中 table_b 最终反映了旧版本的 table_a

  • worker1table_a 获取行。
  • table_a 已更新。
  • worker2table_a 获取行。
  • worker2 更新 table_b
  • worker1 更新 table_b

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

  • worker1table_a 获取行。
  • table_a 已更新。
  • worker2table_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


【解决方案1】:

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

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

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

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

【讨论】:

  • 当第 2 步有一些应用内计算(不在数据库中)时,它怎么可能是一条语句?你能详细说明行标记的问题吗?正如我所描述的,行标记不使用事务。
  • 您可以将应用内计算移动到数据库中。至于行标记,它总是使用事务。至少在理论上,您在更新期间锁定了行以进行更新并强制将其变为串行方法。问题是,根据您如何实现这一点,您仍然可能会遇到时间问题。如果您在进行交易之前提交,那么您会产生时间问题。如果您不提交,那么您只是按照我的建议锁定了该行,但您还有可能意外提交到无效状态。
猜你喜欢
  • 2013-06-02
  • 2018-03-14
  • 2011-06-28
  • 2012-11-21
  • 2016-05-03
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多