隔离级别是否只适用于选择,而不适用于更新?
演示了用于选择的不同隔离行为的场景
1) 0:00 Thread A runs a query that returns 1000 rows that takes 5 minutes to complete
2) 0:02 Thread B runs a query that returns the same 1000 rows
3) 0:05 Thread A updates the last 1 rows in this result set and commits them
4) 0:07 Thread B's query returns* 根据隔离级别的不同,#4中的结果集要么包含线程A的更改,要么不包含。对于更新?是相同的。
下面是一个示例场景:
Thread A: UPDATE ... WHERE primary_key = 1234 AND version = 5
Thread B: UPDATE ... WHERE primary_key = 1234 AND version = 5如果线程A和线程B同时输入它们的事务,而线程B在线程A之后执行更新,那么线程B的更新会失败还是会“看到”版本5的记录,从而成功?
它依赖于数据库吗?例如甲骨文对MySql和PostgreSQL?
发布于 2012-11-18 02:28:43
假设您打算显示许多ORM使用的“乐观锁定”模式,如下所示:
Thread A: UPDATE ... SET ..., version = 6 WHERE primary_key = 1234 AND version = 5
Thread B: UPDATE ... SET ..., version = 6 primary_key = 1234 AND version = 5然后,在所有合理的隔离级别(我并不100%确定读取未提交-大多数DB甚至不支持它)线程B将不匹配任何行,也没有任何效果。
例如,在PostgreSQL中,线程B最初将与A匹配,但在行更新锁上阻塞,直到线程A提交或回滚为止。此时,它将重新检查该条件,并发现如果线程A已提交,它将不再匹配,因此它将什么也不做。行锁定意味着在这种特殊情况下永远不会发生序列化冲突。
在任何正常的数据库中,只有两个更新中的一个会成功,第二个更新将匹配零行,或者根据隔离级别和DB实现而中止序列化失败。即使在MySQL with InnoDB (请参阅本答案中的详细说明和演示。)中也是如此,至少在5.5中是这样的。如果您使用的是MyISAM,那么正确性和可靠性显然对您来说不是什么大问题;-)
我不知道有哪个数据库将不同的隔离规则应用于UPDATE和SELECT,毕竟,UPDATE需要与SELECT相同的WHERE子句、子查询等隔离保证。UPDATE可以死锁,而SELECT不能死锁(在PostgreSQL中,很明显,在MySQL+InnoDB中是可以的)。与SELECT不同,UPDATE在SERIALIZABLE隔离模式下会遇到序列化失败--但它们具有相同的可见性规则。
PostgreSQL关于并发控制的文档很好地解释了这一点。
发布于 2012-11-19 12:05:29
在甲骨文中,有一个语句级一致性:
Oracle数据库总是执行语句级的读取一致性,这保证了单个查询返回的数据相对于单个时间点是提交的和一致的。
这意味着您的select示例在Oracle中不会像这样工作:线程B将返回来自SELECT的结果,就像语句开始时的结果一样。这意味着Oracle可能会像查询开始时一样,从撤销数据中重新创建过去的块,这样长时间运行的查询的结果就有意义了。第(3)点的更新所作的更改将不会出现在结果中。
-- select查询将不会看到事务开始后所做的更改,即使它们已提交。
更新工作类似,但涉及一些额外的工作。所有更新/删除都以标准的SELECT开始,具有标准的时间点一致性,但是这些块在CURRENT MODE中被要求。这是因为必须修改的块版本是最后一个版本。此外,最后一个还包含关于块上当前锁的信息。Tom有一个用于DML的不错的类比 (对于删除和更新也是一样的):
想象一下正在处理的删除过程如下: 对于x in (从emp中选择rowid )--从emp中删除一致的GETS循环,其中rowid = x.rowid;--当前模式得到结束循环;
现在,如果我们用更新替换SELECT,您的场景中会发生什么?
首先,如果该行仍然被锁定(点(3)中的事务尚未提交),则point (4)中的更新将等待--直到(3)提交或回滚。
如果事务已经提交,并且您处于SERIALIZABLE事务隔离状态,那么当然会出现错误。我们不希望修改自事务开始以来已被更改的数据(因为这些更改是不可见的)。
在READ COMMITED中,有一个有趣的,而不是直观的开发。当Oracle获取被修改的行时,它会意识到数据在查询开始后已被修改。Oracle现在无法处理更新,因为这将是不一致的(而且,这意味着更新丢失)。因此,Oracle 重新启动其查询,如本其他askTom线程所述。
结果集是一致的--但在重新启动时它很可能是一致的。
我们得到第二个"select“,这一次(希望)以一致的方式获得所有行。由于锁是逐行放置的,所以在第一次传递中找到的所有行都应该仍然可用(它们不可能在第一次传递和第二次传递之间被另一个事务修改)。
https://stackoverflow.com/questions/13431552
复制相似问题