事务与行标记可靠的SELECT + UPDATE

时间:2012-09-11 07:16:36

标签: sql postgresql concurrency transactions

问题:

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

  1. SELECT来自table_a的一些行。
  2. 计算应用程序代码中的内容。
  3. UPDATE table_b中的一行。
  4. 我需要防范这种情况发生(请调用此方案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事务中。然后事件A中worker1的事务失败,而事件B中worker2的事务失败。如果没有区分方案的方法,唯一的选择是重试失败的事务。

    但在两种情况下都是浪费:在方案A中,我们宁愿不重试worker1的交易,因为table_b已经完全更新。在方案B中,我们首先不会失败worker2的交易,因为它做的是正确的。

    行标记:

    如果我们从开始(table_b)知道:b_id中行的主键,并且每个工作人员都有一些唯一ID(:worker_id),我们可以尝试其他方法。向mark添加table_b列,让每个工作人员在第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。

1 个答案:

答案 0 :(得分:2)

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

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

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

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