基本 SQL 原子性“UPDATE ... SET .. WHERE ...”
Posted
技术标签:
【中文标题】基本 SQL 原子性“UPDATE ... SET .. WHERE ...”【英文标题】:basic SQL atomicity "UPDATE ... SET .. WHERE ..." 【发布时间】:2010-04-19 14:28:10 【问题描述】:关于“UPDATE ... SET .. WHERE ...”语句的原子性,我有一个相当基本和一般性的问题。
有一个表(没有额外的约束),
+----------+
| id | name|
+----------+
| 1 | a |
+----+-----+
现在,我将“同时”(同时)执行以下 4 条语句。
UPDATE table SET name='b1' WHERE name='a'
UPDATE table SET name='b2' WHERE name='a'
UPDATE table SET name='b3' WHERE name='a'
UPDATE table SET name='b4' WHERE name='a'
表更新时是否只会执行一条 UPDATE 语句? 或者,是否有可能不止一个 UPDATE 语句可以真正更新表?
我应该需要额外的事务或锁来让只有一个 UPDATE 将值写入表吗?
谢谢
[编辑] 4 个 UPDATE 语句从不同的进程并行执行。 [编辑] 使用 Postgresql
【问题讨论】:
在第一个语句之后,其他人都不会做任何事情,因为您已将name
从 a 更改为 b1。跨度>
【参考方案1】:
其中一个语句将锁定记录(或页面,或整个表,具体取决于您的引擎和锁定粒度)并将被执行。
其他人将等待资源被释放。
当幸运语句提交时,其他人要么重新读取表并且什么都不做(如果你的事务隔离模式设置为READ COMMITTED
)或者无法序列化事务(如果事务隔离级别是SERIALIZABLE
) .
【讨论】:
【参考方案2】:如果您一次性运行这些更新,它将按顺序运行它们,因此它将所有内容更新到 b1,但其他 3 个将无法更新任何内容,因为没有 A 可以更新。
【讨论】:
【参考方案3】:每个周围都会有一个隐式事务,它将在队列中保存其他更新。在这种情况下,只有第一个通过会获胜,因为每次后续更新都不会看到名为“a”的名称。
编辑:我在这里假设您从单独的进程调用每个 UPDATE。如果在单个脚本中调用它们,它们将按出现的顺序连续运行。
【讨论】:
【参考方案4】:只有一个 UPDATE 语句可以访问记录。在它运行之前,它会启动一个事务并锁定表(或者更准确地说,是记录所在的页面,或者甚至只是记录本身——这取决于许多因素)。然后它进行更改并再次解锁表,在此过程中提交事务。
对某个记录的更新/删除/插入本质上是单线程的,因此需要确保表的完整性。这并不意味着对许多记录(位于不同页面上)的更新不能并行运行。
【讨论】:
“或更准确地说,记录所在的页面” - 不一定。在 Oracle 中,只有行被锁定,而不是整个页面。这真的取决于数据库。 @dcp:是的,关于有效的锁定策略,任何查询提示等等。我的意思是 - 它不一定会锁定整个表。【参考方案5】:使用您发布的声明,您最终会遇到错误,因为在第一次更新后找不到“a”。你想用这个来达到什么目的?
【讨论】:
不会出现错误:您只会得到“0 行受影响”。【参考方案6】:即使不指定begin transaction
和commit transaction
,单个SQL 语句也始终是事务性的。这意味着只允许其中一个更新同时修改该行。其他查询将阻塞第一个查询拥有的update lock
。
【讨论】:
【参考方案7】:我相信这可能是您正在寻找的(在 T-SQL 中):
UPDATE [table]
SET [table].[name] = temp.new_name
FROM
[table],
(
SELECT
id,
new_name = 'B'+ cast(row_number() over (order by [name]) as varchar)
FROM
[table]
WHERE
[table].name = 'A'
) temp
WHERE [table].id = temp.id
应该有与其他 SQL 风格中的 row_number 等效的函数。
【讨论】:
以上是关于基本 SQL 原子性“UPDATE ... SET .. WHERE ...”的主要内容,如果未能解决你的问题,请参考以下文章