事务总是原子性的吗?

4
我正在尝试更好地理解SQL Server事务的细微差别。假设我有一个查询,更新1000个现有行,将其中一列的值更新为1到1000。可以执行此查询,并且当完成时,这些行可能不会按顺序编号。这是因为在我的查询完成之前,另一个查询可能会修改其中一行。
另一方面,如果我将这些更新包装在一个事务中,那么如果任何一个更新失败,我可以使所有更新都失败。但这是否意味着在完成时这些行将保证是连续的?
换句话说,事务是否总是原子的?

2
你为什么想要它们按顺序? - HLGEM
由于这是我对原子性定义的理解,因此事务始终是原子性的(换句话说 - 它要么全部成功,要么不更新)。 - KevinDTimm
5个回答

5
但这是否意味着在完成后,这些行将保证是连续的呢?
不是的。这与事务无关,因为您所要求的根本不存在:关系表没有顺序,要求“连续行”是错误的问题。您可以将问题重新表述为“这1000个更新的行是否包含从1到1000的整个序列而没有间隙?”很可能是的,但事实是,根据您进行更新的方式,可能会存在间隙。这些间隙不会出现在更新后修改更新行之前,而是因为更新将是一个无操作(不会更新任何行),这是读取-修改-写回类型更新的常见问题(由于并发操作,该行在读取和写回之间“消失”)。
要更准确地回答您的问题,无论您的代码是否正确,都必须发布您使用的确切更新代码以及确切的表结构,包括所有索引。

当我谈到行是否按顺序排列时,我指的是我正在编写的连续值,而不是行的物理顺序。换句话说,如果没有其他人访问数据库,则我保证具有顺序值。问题在于如果其他人在我的更新期间修改了一行或多行。 - Jonathan Wood
您的更新不是瞬时的。无论您如何操作,这1000个更新都将在时间轴上“分散”。在执行任何写操作(插入、删除、更新)之前,SQL Server将使用排他锁定锁定正在修改的行。这个排他锁总是保持到事务提交(或回滚),并防止其他任何事务修改该行。但是,正如我所说,这并不能保证结果会是1000个连续值。 - Remus Rusanu
1
关于锁定,有一些细节需要注意:锁定可以升级为页面/分区/表的独占锁,锁定可能适用于行的键而不是行本身,还有其他更细微的点。虽然现在我们不应该深入探讨这些细节,但我上面的解释基本上应该涵盖了您的情况。 - Remus Rusanu

4

原子意味着在事务中进行的操作要么全部执行,要么全部不执行。

如果其中的1000个语句之一失败了,那么整个事务中的所有操作都不会提交。一个事务中语句数量越少,比如只有100条语句,那么在错误发生前的块(比如在第501个语句处)中的前400个块可以被提交(第500个块不行,而600及以上的块可以)。

但这是否意味着在完成后,这些行将保证是连续的呢?

要想让这些行“连续”,您需要提供更多有关事务操作的上下文信息。


也许“原子性”这个词不太准确。我想知道在我提交事务时,是否有保证没有其他人会修改任何受影响的行。至于如何使它们连续,那并不重要。如果有帮助的话,一种方法是循环执行1,000个UPDATE查询。 - Jonathan Wood
@JNK:是的,这正是我所问的。 - Jonathan Wood
@OMG Ponies:是的,我熟悉锁定。我在询问与事务相关的特定点。换句话说,当提交事务时,是否会锁定所有相关行? - Jonathan Wood
如果您确实需要顺序编号,您应该考虑添加一个可控制编号的字段... 如果您使用的是自动增量字段,则真的无法保证顺序编号。 - Leslie
1
@Leslie:不,自己编写自增序列并不像使用数据库替代方案那样安全(值得庆幸的是,现在序列已经成为ANSI标准,而SQL Server在下一个版本中也支持它们)。 - OMG Ponies
显示剩余5条评论

2

SQL事务与所有数据库平台上的事务一样,将数据隔离以涵盖整个ACID缩写(原子性、一致性、隔离性和持久性)。所以答案是肯定的。


2

这两个点是无关的。

顺序性

如果你插入值从1到1000,它将是按顺序排列的,使用WHERE和ORDER BY在某些列中限制你只能查看这1000行。除非有重复项,否则你需要一个唯一约束。

如果你依赖于IDENTITY,那就不能保证: 插入的记录总是接收连续的标识值吗?.

原子性

所有事务都是原子性的:


你可能是对的,但你的第一个观点似乎与我谈论的内容无关。假设,我不是存储连续的值,而是将相同的值写入到1,000行中的一列中。当我完成后,这些行是否在更新的列中包含相同的值?如果在我的操作过程中另一个用户正在写入其中一行,那么答案是否定的。那么,如果我将相同的值作为事务的一部分写入1,000行呢? - Jonathan Wood
所有1000行都被独占锁定,只有你完成或回滚之前,没有人可以更改它们。这是ACID中的隔离级别。在你的例子中,取决于谁先到达。你们其中一个会覆盖另一个,但是你们分开的更新都是原子和独占的。对于一张有超过1000行的表,不同的进程可以更新不同的行(除非锁粒度或隔离级别被更改以防止这种情况,例如表锁:在这里超出范围,因为这不是默认行为)。 - gbn
只是为了明确,我并没有显式地创建锁。你的意思是作为交易的结果,对吗?如果是这样,看起来我的问题的答案包括“是”,“不是”和“可能”。 :-) - Jonathan Wood
更新操作所涉及的事务,是的。所有的DML都使用锁(选择可以更改为具有脏读取(不在此范围内)。 - gbn

0

事务保证了原子性,这就是重点。

你的问题在于,在插入完成后,它们只是“连续”的,直到下一个操作接触到其中一条新记录。

如果你的另一个步骤需要它们仍然保持连续,则该步骤也需要在你最初的事务内执行。


我现在询问的是它们在交易提交的那一刻是否是连续的。是的,显然后来有人可以更改它们。 - Jonathan Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接