MySQL 面试:数据库的事务隔离级别有哪些?各有哪些优缺点

你曾被提过类似问题在面试中问过吗?或者将来会遇到,让我们一起探索和掌握它!

感谢您阅读这篇文章。更多面试问题:
https://programmerscareer.com/zh-cn/software-interview-set/

主题 1.1:事务隔离级别简介

让我们开始了解 MySQL 中的事务隔离级别!

先来看基本知识。

在允许并发事务的数据库系统中,事务隔离级别决定了一个事务与另一个事务之间的隔离程度。它们是必要的,因为它们帮助管理并发事务,防止数据不一致,并确保任何事务系统中的数据的完整性。

ANSI/ISO SQL 标准定义了四个事务隔离级别,具有相应的现象防止:

  1. 读未提交:这是事务隔离级别的最低级别,在这种情况下,一个事务可能会看到另一个事务尚未提交的更改。
  2. 读已提交:它保证了任何数据的读取是在读取时提交的。它不能防止其他事务修改数据。
  3. 可重复读:这个级别确保了如果一个事务读取了数据,然后其他事务修改了该数据,则第一个事务将获取相同的数据,不管后续的读取是否发生。
  4. 序列化 - 这个级别提供了最高的数据保护,它通过执行事务来实现,或者是一个接一个的事务。但是,这可能会导致性能问题。

还要知道,MySQL,具有 InnoDB 存储引擎,只支持 Repeatable Read(默认隔离级别)、Read Committed 和 Serializable。

每个隔离级别都有其优点和缺点,解决了某些问题,同时可能会引入其他问题。这是管理并发事务的必要权衡,以平衡性能和准确性。

主题 1.2:读未提交级别

在“读未提交”中,事务可能会读取尚未提交的数据,这是因为它读取的数据尚未提交到数据库中。

考虑下面的场景:一个事务正在修改表的一些行:

1
2
3
UPDATE Inventory   
SET Quantity = Quantity - 10
WHERE ItemName = 'Apples';

当这个事务仍然在进行时,另一个事务读取了同一表的数据。根据读未提交级别,它会看到尚未提交的更改,包含正在处理的数据,导致所谓的“脏读”。

可能会出现一个事务失败(并发行回),使这些更改无效。但是,第二个事务已经读取了未提交数据,继续使用错误、不准确的数据。这可能会导致数据不一致。

在性能方面,然而,“读未提交”通常更快,因为它不需要为读数据锁定,以防止其他事务修改或读取。

然而,“读未提交”的缺点现在显现出来。它无法保证数据的准确性和一致性,因为,就像我们所说的,它允许“脏读”。

在实际应用中,这个隔离级别通常被避免,除非性能是最重要的因素,数据准确性不是主要考虑因素。

主题 1.3:读已提交级别

根据名字所表明,“读已提交”级别允许事务只看其他事务在开始读取之前已经提交的更改。因此,它解决了我们在“读未提交”隔离级别中讨论的“脏读”问题。

让我们通过一个简单的例子来说明:

考虑两个账户,‘A’ 和 ‘B’,其当前余额分别为 $500 和 $200。假设一笔交易被初始化,从账户 ‘A’ 中转出 $100,并转入账户 ‘B’。在此过程中,账户 ‘A’ 的余额减少到 $400,尽管交易尚未完成。

在“读未提交”隔离级别中,如果另一项目试图同时计算两个账户的总余额,它可能会将账户 ‘A’ 的中间状态(即 $400)和账户 ‘B’ 的原始状态(即 $200)相加,导致错误的总余额 $600。

然而,在“读已提交”隔离级别中,第二项目等待第一项目完全完成。因此,它正确地计算总余额为 $700($400 在账户 ‘A’ 中 + $300 在账户 ‘B’ 中)。

因此,在“读已提交”隔离级别中,一个事务不会看到其他事务未提交的更改,这是维护数据一致性的一个大步。

然而,现在我们面临另一个问题,称为“非重复读”。这发生在单个事务的生命周期内,试图两次读取同一行,但每次读取时获取不同的数据。这种情况是可能的,如果,在第一次和第二次读取之间,另一项目修改了该行并提交了更改。

主题1.4:可重复读级别

在“可重复读”隔离级别下,不仅其他事务的变更在提交之前是不可见的(就像“读提交”一样),但是事务第一次读取某些数据后,该数据在该事务的生命周期内不能变化。

换句话说,在同一事务中多次运行相同的 SELECT 查询将返回相同的结果,不管其他并发事务是否发生变化。这种约束解决了“非重复读”问题。

让我们来看一个例子:

考虑一种情况,其中事务读取了某些行,然后另一个独立事务修改了其中的某些行并提交了变更。如果第一个事务再次尝试读取相同的行,根据“读提交”隔离级别,它会注意到这些变化。

但是,在“可重复读”隔离级别下,第一个事务不会因为其他事务在其生命周期内提交的变更而察觉到任何变化。因此,读取相同的行会产生相同的结果。

虽然它解决了“脏读”和“非重复读”问题,但它也会遇到另一个问题:“幻读”问题,我们将在我们的下一节中讨论。

主题1.5:序列化级别

“序列化”级别是所有级别中最严格的,提供了最高的数据一致性。它不仅处理“脏读”和“非重复读”问题,还解决了“幻读”问题。

首先,让我们了解一下“幻读”是什么。它是在事务的中间运行时新行被添加或现有行被删除的事务中发生的。它被命名为“幻”,因为这些记录似乎是“幻”的。

例如,考虑一个事务读取了某些行。另一个独立事务在此期间添加了某些新行并提交了变更。如果第一个事务再次读取该表,它会看到新行,这些行似乎是“幻”的。

在“序列化”隔离级别下,这种情况是不可能的。当事务在这个级别下运行时,它似乎就像其他事务不存在一样,消除了并发性相关的问题。

然而,这种精确性有代价。“序列化”隔离级别严重降低了性能,特别是对大型数据库。

简而言,“序列化”隔离级别确保绝对数据完整性,但是以性能为代价。

在讨论了每个特定的隔离级别之后,必须注意,您选择的级别最终取决于应用程序的性质。它总是关于性能和数据完整性之间的平衡。

主题1.6:MySQL事务隔离级别解释

我们先讨论过,MySQL 中可用的四种事务隔离级别分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和序列化(Serializable)。每种级别都为数据一致性、并发性和性能之间提供了不同的平衡。

然而,问题仍然存在:MySQL 是如何内部实现这些级别的?

MySQL 主要使用锁定来确保并发事务之间的数据一致性和隔离。它使用不同类型的锁,例如共享锁和排他锁,根据事务的要求和设置的隔离级别。

我们不会深入讨论细节,但是让我们了解这些锁是什么:

  1. 共享锁(S 锁):当事务只是读取记录并未修改它时,会保持共享锁。多个事务可以同时保持共享锁的同一记录。
  2. 排他锁(X 锁):当事务修改记录时,会保持排他锁。在给定时间内,只有一个事务可以保持排他锁的记录。

这些锁用于维护数据一致性并防止数据不一致。例如:

  • Read Uncommitted 级别下,不会使用任何锁来防止其他事务写入记录。
  • Read Committed 级别下,会使用共享锁,但是在读取行后立即释放锁。
  • Repeatable Read 级别下,会使用共享锁并保持其直到事务完成。
  • Serializable 级别下,会使用共享锁,并且直到事务完成之前,其他事务不能修改或插入新记录。

因此,根据使用的隔离级别,MySQL 引擎会以不同的方式获取和释放这些锁来实现所需的数据一致性,并在并发性和反之之间进行权衡。

然而,这种机制只是冰山一角。实际实现要复杂得多,并涉及许多其他因素,例如锁升级、死锁检测、日志缓冲和更多。

主题1.7:实践案例

在本节中,我们将通过实际场景来了解不同的事务隔离级别的实际应用,并将所有的学习联系起来。

最合适的隔离级别主要取决于特定的读/写工作负载和每个应用的业务要求。在实际场景中,我们需要找到并发性和隔离之间的平衡。

让我们来看几个场景:

场景 1:银行系统

对于处理事务性数据的银行系统,例如银行转账,脏读和非重复读是绝对不可接受的。例如,如果你从一个自动Tellermachine(ATM)取出了钱,但由于并发事务,系统未能立即注册扣除,这可能会导致你提取了超过你的余额。这是对我们来说的一个美妙的场景,但对银行来说是一个灾难性的情况!

因此,对于这样的系统,通常使用较高的隔离级别,例如SERIALIZABLEREPEATABLE READ,尽管这可能会影响性能。

场景 2:电子商务应用

对于电子商务应用,允许脏读可能会导致卖出更多的商品,而如果我们非常严格地处理隔离级别,它可能会降低应用的性能并影响用户体验。通常使用READ COMMITTED的隔离级别在这里进行了交换,在交换中,我们在严格的隔离和增加并发性之间进行了权衡。

场景 3:数据分析和报告

在数据分析或报告场景中,我们正在读取大量数据但不会修改它,通常可以使用较低的隔离级别,例如READ UNCOMMITTED。这可以减少锁的开销并提高吞吐量。

请记,没有一个解决方案适用于所有的系统,它总是取决于特定的要求和情况。

主题1.8:就绪面试

问题: 解释事务隔离级别。

答案: 事务隔离级别控制在从数据库中选择数据时所发生的锁定的程度。数据项上的锁定对数据库的并发性和一致性至关重要,特别是事务处理中。根据 SQL 标准定义的四种标准事务隔离级别:未提交读取、已提交读取、可重复读取和序列化。

问题: 每种事务隔离级别的优势和劣势是什么?

答案:

  • 未提交读取: 事务可能会读取其他事务尚未提交的更改,导致脏读和其他不一致性。优势是需要锁定的少量,因此性能更好。
  • 已提交读取: 允许事务只读已提交的更改,避免脏读,但仍可能导致非重复读取或幻读。通常提供了一致性和性能的良好平衡。
  • 可重复读取: 确保任何读取的数据不会更改,避免脏读和非重复读取,但仍可能导致幻读。
  • 序列化: 最高级别的隔离。确保事务以完全隔离的方式进行,避免脏读、非重复读取和幻读,但可能会导致性能下降,因为需要广泛的锁定。

问题: 何时可能使用每种隔离级别?

答案:

  • 未提交读取: 数据分析任务,在看到未提交更改是可取的情况下,性能是关键的。
  • 已提交读取: 应用程序,在维持高度并发性更重要的情况下,可能会出现偶尔不一致性,例如某些低影响的电子商务应用程序。
  • 可重复读取: 在维持数据的一致性是至关重要的情况下,例如某些金融应用程序。
  • 序列化: 仅在严格需要时使用,因为性能方面的影响,例如管理高度敏感数据的应用程序。

这些只是一些可能的面试问题的例子。记住,深入理解这些概念将允许您适应面试者可能会问的任何特定问题。

主题1.9:回顾和评估

了解数据库事务的事务隔离级别是处理数据库事务时的基本知识。到目前为止,我们已经探讨了不同的事务隔离级别、其使用、优势和可能的弱点。

为了巩固理解,让我们来快速回顾并提出一些问题:

1. 根据 SQL 标准,描述四种事务隔离级别。

_你的答案: ______

2. 描述一种场景,在哪里你会使用 REPEATABLE READ 隔离级别。

_你的答案: ______

3. ‘脏读是指事务读取其他事务尚未提交的数据’ 是否为真?

_你的答案: ______

4. 哪种事务隔离级别具有最严格的锁定,导致事务性能上的最大影响?为什么?

_你的答案: ______

5. ‘在 Read Committed 级别下可能会发生幻读’ 是否为真?

_你的答案: ______

花时间来回答这些问题,反思你的回答,并与我们所学的内容进行比较。当你准备好时,我们可以一起讨论答案。如果你有任何不确定或需要更深入的解释,请勿犹豫。让我们确保你完全理解了这个概念!

English post: https://programmerscareer.com/mysql-interview15/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:本文为作者原创,转载请注明出处。

MySQL 面试:什么是数据库事务,MySQL 为什么会使用 InnoDB 作为默认选项? MySQL面试: 简述数据库中的 ACID 分别是什么?

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×