当前位置:  首页>> 技术小册>> MySQL 实战 45 讲

03 | 事务隔离:为什么你改了我还看不见?

在数据库管理系统中,事务(Transaction)是一个不可分割的工作单位,它确保了一组数据库操作要么全部执行成功,要么在遇到错误时全部撤销,以保持数据的一致性和完整性。然而,在多用户并发访问数据库的场景下,事务之间的相互影响可能会引发一系列问题,如脏读(Dirty Read)、不可重复读(Non-Repeatable Read)和幻读(Phantom Read)。为了解决这些问题,数据库系统引入了事务隔离级别(Transaction Isolation Levels)的概念。本章将深入探讨事务隔离的概念、重要性以及MySQL中支持的四种隔离级别,并解释为什么在某些情况下,“你改了我还看不见”的现象会发生。

一、事务隔离的基本概念

事务隔离性是数据库事务处理中的一项重要特性,它要求事务在并发执行时,系统能保证一个事务内部的操作对其他并发执行的事务是隔离的,即一个事务的执行不能被其他事务干扰。事务的隔离性是通过锁(Locks)和事务日志(Transaction Logs)等机制来实现的,但直接操作这些底层机制对于大多数开发者来说并不直观,因此,数据库管理系统提供了不同级别的事务隔离设置,允许开发者根据应用的需求选择合适的事务隔离级别。

二、事务隔离级别

SQL标准定义了四种事务隔离级别,从低到高依次为:

  1. 读未提交(Read Uncommitted)

    • 在这个级别下,一个事务可以读取到另一个事务未提交的数据。这会导致脏读现象,即一个事务读取了另一个事务未提交的数据,而这些数据在后续操作中可能会被回滚,从而导致读取的数据无效。
  2. 读已提交(Read Committed)

    • 这也是大多数数据库系统的默认隔离级别。在这个级别下,一个事务只能读取到另一个事务已经提交的数据,避免了脏读的发生。但是,它仍然可能遇到不可重复读的问题,即在同一事务内,多次读取同一数据集合时,由于其他事务的插入、更新或删除操作,导致两次读取的结果不一致。
  3. 可重复读(Repeatable Read)

    • MySQL的默认事务隔离级别(在InnoDB存储引擎下)。在这个级别下,保证了在同一个事务中多次读取同样记录的结果是一致的,即避免了不可重复读的问题。但是,它仍然可能遇到幻读的问题,即当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取这个范围时,会发现有“幻影”般的记录出现。
  4. 串行化(Serializable)

    • 这是最高的隔离级别,它通过强制事务串行执行,来避免脏读、不可重复读和幻读。但是,这种级别的性能开销最大,因为它会极大地限制并发性。

三、为什么“你改了我还看不见”?

理解了上述的事务隔离级别后,我们就可以解释为什么在某些情况下会出现“你改了我还看不见”的现象了。这主要涉及到不同隔离级别下,事务对数据的可见性规则。

  • 在读未提交(Read Uncommitted)级别下,理论上讲,如果一个事务修改了数据但尚未提交,另一个事务是可以读取到这些未提交的数据的。然而,在实际情况中,由于系统实现或性能考虑,数据库管理系统可能并不会立即将这些未提交的数据变化展现给其他事务,但这并不违反读未提交级别的定义。不过,这种情况与“你改了我还看不见”的直接现象关联不大,因为它更多关联于数据是否“脏”的问题。

  • 在读已提交(Read Committed)级别下,一个事务只能看到其他事务已经提交的数据变化。如果事务A修改了某条数据并提交,而事务B在事务A提交后才开始读取该数据,那么事务B将能看到事务A所做的修改。但如果事务B在事务A提交之前就尝试读取该数据,那么它将看不到事务A所做的修改,这在一定程度上可以解释“你改了我还看不见”的现象,但更准确地说是“你未提交我就看不见”。

  • 在可重复读(Repeatable Read)级别下,这是MySQL的默认隔离级别,它确保了在同一事务内多次读取相同记录的结果是一致的。如果事务A在事务B开始之前修改了某条数据并提交,事务B在读取该数据时,虽然能看到事务A的修改结果,但在事务B内部,如果它多次读取同一条数据,每次读取的结果都将保持一致,即使其他事务(如事务C)在此期间对该数据进行了修改并提交。这解释了为什么在可重复读级别下,即使数据被其他事务修改了,当前事务也可能“看不见”这些修改,因为它会保持第一次读取时的数据快照。

  • 在串行化(Serializable)级别下,由于事务被强制串行执行,理论上不会出现“你改了我还看不见”的情况,因为每个事务都会完整地看到其他事务的结果后再执行。但是,这种级别的性能开销使得它在实际应用中很少被采用。

四、实践中的选择与优化

在选择事务隔离级别时,开发者需要权衡数据的一致性需求与系统性能之间的平衡。过高的隔离级别虽然能保证数据的高度一致性,但可能会显著降低系统的并发处理能力;而过低的隔离级别虽然能提高性能,但可能无法满足应用对数据一致性的要求。

在MySQL中,可以通过设置全局或会话级别的transaction_isolation变量来更改事务的隔离级别。例如,将当前会话的事务隔离级别设置为可重复读(MySQL的默认级别):

  1. SET SESSION transaction_isolation = 'REPEATABLE-READ';

此外,开发者还可以通过合理的索引设计、优化查询语句、使用锁定提示(尽管MySQL的InnoDB引擎主要依赖自动锁定机制)等方式来减少对事务隔离级别的依赖,提高系统的整体性能和一致性。

五、总结

事务隔离性是数据库事务处理中的一个重要特性,它确保了事务在执行过程中的数据一致性和完整性。通过理解并合理设置事务隔离级别,开发者可以在保证数据一致性的同时,优化系统的并发处理能力。MySQL提供了四种事务隔离级别,每种级别都有其适用场景和潜在问题。在实践中,开发者应根据应用的具体需求选择合适的隔离级别,并通过其他技术手段来进一步提升系统的性能和一致性。


该分类下的相关小册推荐: