【问题标题】:Atomicity of SQL UPDATE statement with WHERE clause in DerbyDerby 中带有 WHERE 子句的 SQL UPDATE 语句的原子性
【发布时间】:2014-06-11 20:10:49
【问题描述】:

我正在编写一个 (Java) 函数,它将在多个进程中运行,它从 Derby SQL 数据库中选择一个随机用户名。我不想一次使用多个进程使用相同的用户名,所以我想添加一个“标志”列来充当锁。我读到the documentation 的事务说每个语句都是原子的,但我对我的逻辑/知识并不是 100% 有信心。

我的策略是首先选择一个随机用户名(假设我有一个表usernames,列有usernameflag):

SELECT username FROM usernames WHERE flag='0' ORDER BY RANDOM() FETCH FIRST 1 ROW ONLY;

然后将检索到的用户名传递给此(通过准备好的语句):

UPDATE usernames SET flag='1' WHERE username=? AND flag='0';

并保存返回值(更新的行数)。如果更新了 1 行,则继续使用选定的用户名。如果没有更新行,请重新开始并重试。

这是一种可靠的锁定机制来确保没有两个进程最终使用相同的用户名吗?例如。如果两个进程从SELECT 语句返回相同的用户名,是否保证只有一个进程会从UPDATE 获得结果为 1?还是我误解了声明是原子的含义?

【问题讨论】:

  • 你不应该需要设置一个标志,听起来你想要做的是锁定记录,数据库应该为你处理这个。我不熟悉德比,但从快速谷歌产生https://db.apache.org/derby/docs/10.6/ref/rrefsqlj31783.html
  • @TI 我确实需要设置标志,因为一旦进程获得用户名,它可能会使用它 20 多秒,我仍然不希望 5 秒后出现另一个 proc 并使用它。
  • 所有其他进程如何知道检查标志。听起来像是实现和执行这种逻辑的皮塔饼。我认为您可能会混淆原子性和并发性。锁处理并发。事务处理原子性(工作单元)。
  • @TI 所有进程都运行相同的功能,所以很容易实现。我想确保我不会遇到这样的情况:procA 找到与 where 匹配的行,procB 找到与 where 匹配的同一行,procA 进行更新,然后 procB 进行更新,并且都不知道另一个是在那里。

标签: sql concurrency sql-update derby atomic


【解决方案1】:

首先,我会使用布尔值以外的东西,例如当前使用该名称的进程的进程 ID。这样就更容易调试和释放由进程崩溃导致的虚假 flag=true 锁。

Derby 支持满足 ACID 属性的事务。一个事务可以包含任意数量的语句。例如:

conn.setAutoCommit(false); // true by default

try {
  // execute select
  // execute update
} catch (SQLException sqle) {
  conn.rollback() // abort transaction and undo all effects
}
conn.commit();    // commit transaction, make changes visible 

请注意,确切的行为由为连接设置的isolation level 控制。默认为已提交读。

如果你这样做,应该不需要测试更新是否成功,因为事务机制设置了必要的锁来确保你选择的用户名在事务结束之前不会被其他人修改。

请注意,在您的情况下,可能不需要回滚,因为只有更新具有需要撤消的可见效果,并且更新语句本身是原子的。

【讨论】:

    猜你喜欢
    • 2012-11-20
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-29
    • 2020-01-14
    相关资源
    最近更新 更多