【问题标题】:Transactions, locks, isolation levels事务、锁、隔离级别
【发布时间】:2011-09-17 20:39:37
【问题描述】:

我对标题中的主题有几个问题。首先,假设我们使用 JDBC,并且我们有 2 个事务 T1 和 T2。在 T1 中,我们在一个特定的行上执行 select 语句。然后我们对该行执行更新。在事务 T2 中,我们在同一行上执行 select 语句。

以下是问题:

1) 事务 T1 何时获取上述行的锁?我假设它发生在 select 语句执行期间?

2) 事务 T1 持有锁多长时间?它会一直持有它直到事务被提交/回滚,还是在此之前释放锁?

3) 隔离级别是否控制使用哪种类型的锁?例如:

a) 如果我们在事务 T2 上使用 readmitted 隔离级别,这是否意味着 T2 将对 select 语句使用共享读锁,这样如果 T1 更新了已经 T2 的行,T2 将不会拥有访问该行(避免脏读),如果 T1 没有更新行但 T2 将有权读取该行?

b) 如果我们在事务 T2 上使用 read uncommitted 隔离级别,是否意味着 T2 将对 select 语句使用无锁,因此即使它正在被 T1 修改它也可以读取数据(允许脏读)。

那么,最让我烦恼的问题是,谁可以决定应用哪种类型的锁?是事务的隔离级别,还是有其他方式?

4) 如果对问题 3 的回答是肯定的(隔离级别控制使用什么锁),那么如果我们在 mysql 数据库上使用 jdbc,并且我们在共享模式结构中使用 select 进行更新或选择锁,会发生什么?我记得第一个是排他锁,第二个是共享读锁。它将如何反映我们的事务隔离级别?

5) 在可重复读隔离级别的情况下获取什么样的锁?假设我们的 T2(具有可重复的读取隔离级别)在同一行上有两个 select 语句,而 T1 与以前相同。首先在 T2 中执行一条 select 语句,然后执行 T1 并提交,然后在 T2 中执行第二条 select。这种情况甚至可能吗?如果事务在提交/回滚之前一直保持锁定,我假设 T1 在 T2 完成之前将无法获得更新的排他锁?

编辑:还有一个问题:

6) 在多版本并发控制系统中,当我们设置 serializable 隔离级别时,事务 A 尝试更新由另一个事务 B 更新的某些行(B 在 A 启动后更新了该行)将被回滚。我想问在乐观锁定场景中发生的事情不是一样吗?

提前致谢。

【问题讨论】:

  • 这是很多问题...答案取决于使用的特定数据库/版本...那么我们在谈论什么数据库?
  • 好吧,假设它是 mysql 5.5 :)
  • 好的——这还不够,因为 MySQL 可以与不同的存储引擎一起使用,而这些引擎在事务方面的表现也大不相同,那么:什么存储引擎?
  • 看,我可以说 InnoDB,但我认为它使用多版本并发控制,我的问题更多与经典的基于锁定的并发控制有关。但是好的,可以说它是 InnoDB 存储引擎。我什至还有一个关于 mvcc 及其与基于乐观的锁定的关系的问题:)
  • 对于一些一般观点,请参阅dev.mysql.com/doc/refman/5.1/en/… - 虽然我认为这个问题属于 serverfault.com

标签: transactions locking isolation-level


【解决方案1】:

你的问题很好。了解获取什么样的锁有助于深入了解 DBMS。在 SQL Server 中,在所有隔离级别(未提交读、已提交读(默认)、可重复读、可序列化)下,都会为写操作获取排他锁。

无论隔离级别如何,事务结束时都会释放独占锁。

隔离级别之间的差异是指获取/释放共享(读取)锁的方式。

Read Uncommitted 隔离级别下,不会获取共享锁。在此隔离级别下,可能会发生称为“脏读”的并发问题。

Read Committed隔离级别下,为相关记录获取共享锁。当前指令结束时释放共享锁。此隔离级别可防止“脏读”,但由于其他并发事务可以更新记录,因此可能会发生“不可重复读”或“幻读”。

Repeatable Reads 隔离级别下,在事务期间获取共享锁。 “脏读”和“不可重复读”被阻止,但“幻读”仍然可能发生。

Serializable 隔离级别下,在事务期间获取范围共享锁。上述并发问题均未发生,但性能大幅下降,并且存在发生死锁的风险。

【讨论】: