隔离级别是否仅适用于 SELECTS 而不适用于 UPDATES?
演示 SELECTS 不同隔离行为的场景
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 的记录并因此成功?
是否依赖于数据库?例如Oracle vs MySql vs PostgreSQL?
最佳答案
假设您打算展示许多 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% 确定 READ UNCOMMITTED - 大多数数据库甚至不支持它)线程 B 将不匹配任何行并且没有效果。
例如,在 PostgreSQL 中,线程 B 最初将匹配与 A 相同的行,但阻塞在行更新锁上,直到线程 A 提交或回滚。此时它将重新检查条件并发现如果线程 A 已提交则它不再匹配,因此它什么都不做。行锁定意味着序列化冲突永远不会在这种特殊情况下发挥作用。
在任何理智的数据库中,只有两个更新中的一个会成功——第二个更新要么匹配零行,要么因序列化失败而中止,这取决于隔离级别和数据库实现。至少在 5.5 中,即使在带有 InnoDB ( see detailed explanation and demo in this answer ) 的 MySQL 中也是如此。如果您使用的是 MyISAM,那么正确性和可靠性显然对您来说不是什么大问题;-)
我不知道有任何数据库对 UPDATE
和 SELECT
应用不同的隔离规则。毕竟,UPDATE
需要为其 WHERE
子句、子查询等提供与 SELECT
相同的隔离保证。 UPDATE
可以死锁,而 SELECT
不能(在 PostgreSQL 中;显然它们在 MySQL+InnoDB 中可以)。与 SELECT
不同,UPDATE
在 SERIALIZABLE
隔离模式下会出现序列化失败 - 但它们具有相同的可见性规则。
PostgreSQL 关于 concurrency control 的文档很好地解释了这一点。
关于mysql - 隔离级别是否仅适用于 SELECTS 而不适用于 UPDATES?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13431552/