你曾被要求类似问题在面试中吗?或者将来会遇到,让我们一起探索和掌握它。
感谢您阅读这篇文章。更多面试问题:
https://programmerscareer.com/zh-cn/software-interview-set/
主题:1.1 数据库事务中的问题介绍
在深入数据库事务的复杂性时,需要承认这些操作不总是简单的。它们的主要目的是执行一系列操作,使数据库从一个一致的状态变化到另一个一致的状态。然而,同时进行事务时,我们遇到了许多问题,需要解决以维持一致性和完整性。
其中一个挑战是并发事务的问题。为了维持事务的 ACID 属性(原子性、一致性、隔离性和持久性),数据库系统必须正确地处理并发事务的执行。如果未能确保正确管理,可能会导致以下问题:
- 脏读:这种问题发生在一个事务读取另一个事务还未提交的变更时。如果后者事务被回滚,则前者事务读取了一个无效的值。
- 不可重复的读取:这是当一个事务多次读取同一行时,每次读取都会得到不同的数据,因为其他事务正在更新该行同时进行。
- 幻读:这是当一个事务执行两次相同的查询时,第二次结果集包含未在第一次结果集中的行,由另一个事务添加。
这些事务控制问题会破坏数据库事务的顺畅功能并影响数据完整性。在以下课程中,我们将深入探讨脏读和幻读的场景,并了解解决方案,包括 MySQL InnoDB 引擎提供的解决方案。
主题:1.2 理解脏读
在数据库的上下文中,“脏读”是指一个事务读取另一个事务还未提交的脏数据。例如,事务 1 修改了某行,但尚未提交。现在,在事务 1 提交之前,事务 2 读取了未提交的变更。这就是脏读。
为什么这是一个问题? 假设事务 1 最终回滚。在这种情况下,变更被撤销,但事务 2 已经读取了脏数据,导致不一致性并可能导致无效的结果在数据库中。
下面是一个简单的例子:
步骤 1:
- 事务 1 在
orders
表中修改了order_status
字段,将其从 ‘Pending’ 更新为 ‘Shipped’。
步骤 2:
- 在事务 1 提交之前,事务 2 读取了
order_status
并发现它是 ‘Shipped’。
步骤 3:
- 事务 1 遇到错误并执行 ROLLBACK 操作,将
order_status
更改回 ‘Pending’。
步骤 4:
- 然而,事务 2 继续进行,并读取 ‘Shipped’ 状态,尽管这从未存在过。
脏读可能会导致严重的错误,特别是在数据分析或报告过程中,准确性是至关重要的。
中文翻译:
主题:1.3 理解幻读
像脏读一样,幻读也是数据库事务中的并发问题。幻读通常发生在事务重新查询它已经查询过的数据,但发现新行,这些行在初始读取之后被其他事务插入或更新。
这些“幻”行是由另一个事务在我们的初始事务开始之后并在其结束之前插入或更新所导致的。
为了更清楚地理解这一点,让我们考虑一个简单的例子:
步骤 1:
- 事务 1 从
orders
表中检索所有order_status
为 ‘Pending’ 的行。
步骤 2:
- 在这之间,事务 2 在
orders
表中插入了一个新行,其order_status
为 ‘Pending’,并提交。
步骤 3:
- 现在,事务 1 再次运行相同的检索查询。这次,它发现事务 2 插入的行——这是一个幻行。
幻读问题主要发生在较低的隔离级别中,例如“读已提交”,但不是较高的隔离级别,例如“序列化”。这是由于使用排他范围锁来阻止在读范围内插入新行所导致的。
然而,这些较高的隔离级别也会遇到问题,例如较低的并发和较高的争用。因此,事务隔离级别的选择通常需要权衡性能和一致性之间的交换。但是,不要担心,InnoDB 提供了处理这些情况的方法。
主题:1.4 InnoDB 在处理幻读方面的作用
InnoDB 存储引擎在 MySQL 中起着关键的作用,处理数据库事务问题,包括幻读。它通过使用 多版本并发控制 (MVCC) 来允许多个事务同时访问同一行,而不会影响彼此的工作。
每个事务看到数据库在其工作开始时的一个快照,使并发事务相互隔离。这对 MySQL InnoDB 中的 “I” (一致性) 部分起着重要作用。
此外,您还可以在 MySQL 中设置不同的隔离级别来自定衡读一致性、并发和性能之间的平衡。这些隔离级别包括 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。
重复读级别是 InnoDB 的默认级别,它保证了所有在同一事务中的读取都会看到数据库在事务开始时的一个快照。这个特性有效地防止了幻读。
然而,在某些业务场景中,序列化级别可能是必需的,提供最高的数据一致性,但是以代价较低的并发和性能。
在本课程的后期,我们将详细讨论 InnoDB 如何实现 ACID 属性并自定化这些事务属性,根据用户需求进行调整。
主题:1.5事务隔离级别
事务隔离级别在数据库管理系统中如何管理并保护事务免受潜在问题(脏读、不可重复读和幻读)的中心角色。
在 MySQL 中,有四种预设的隔离级别,每种级别具有性能和保护之间的不同权衡:
- 读未提交:这是隔离级别的最低水平,它允许事务看到其他事务未提交的更改。这意味着事务可能会看到“脏”数据,其他事务可能会后悔。
- 读已提交:这个级别保证了任何读取的数据都已提交。因此,它防止脏读。然而,如果事务多次读取同一行,它可能会看到不同的值,如果另一事务修改了该行之间,这可能会导致不可重复读。
- 可重复读:这是 InnoDB 的默认隔离级别。它防止脏读和不可重复读,并确保所有读取的相同行在同一事务内返回相同的结果,除非该行自身被该事务修改。
- 序列化:这是隔离级别的最高水平。它锁定读取的行,防止其他事务(读或写)访问它们,直到第一事务完成。虽然这种级别可以防止脏读、不可重复读和幻读,但它显著降低了并发性。
了解这些隔离级别是管理并发事务有效的关键。在下一主题中,我们将讨论一些实现并发控制的技术和实践。
主题:1.6并发控制的策略
数据库中的并发控制的目的是允许多个事务同时访问数据库,而不会发生冲突或错误。为了有效地实现并发控制,我们可以利用以下策略:
- 锁定基于的协议:这是一种常见的方法,其中给定事务在需要时为数据项锁定访问。有两种锁:排他锁和共享锁。前者不允许其他事务访问数据项,后者允许,但仅限于读取目的。
- 时间戳基于的协议:这种方法涉及为每个事务分配一个时间戳,确保早期事务具有优先权,特别是在冲突时。
- 有效性基于的协议:也称为乐观并发控制,这种方法允许事务执行无限制并在提交时进行有效性检查。
- **多版本并发控制 (MVCC)**:主要用于 InnoDB,MVCC允许每个与数据库连接的用户从事务开始时看到一致的快照集。
- 数据项的粒度:这决定了锁定的数据项的大小——从单行到整个数据库。
每种策略都有其优势和劣势。例如,锁定基于的协议可能会创造性能问题由于锁争,而 MVCC可能会为 InnoDB 提供高并发和减少了锁定的需求,但可能会增加存储的成本。
要选择一种策略,应考虑应用程序的需要和考虑,例如性能、一致性和复杂性。
主题:1.7 回和评估
你已经做得非常好地探索了数据库事务的关键方面,了解了并发控制的概念,它所倾向的问题,并使用各种策略来处理这些问题。
到目前为止,我们已经了解了并发控制的必要性,即处理同时发生的事务。我们已经识别了潜在的挑战,例如脏读、不可重复读和幻读,这些基本上涉及事务如何处理来自其他事务的数据更改。
我们深入探讨了脏读是什么,它是如何发生的,以及对数据库事务的影响。同样,我们对幻读发生的情况进行了详细讨论,并讨论了它可能会导致的问题。
我们研究了 MySQL 中 InnoDB 存储引擎的角色,特别是它是如何实现“一致读取”来处理幻读。
我们进一步深入探讨了事务隔离级别的概念,了解配置不同的事务隔离级别可以影响脏和幻读的发生。
我们还讨论了处理并发问题的各种策略,例如事务调度和使用各种类型的锁定基于的协议。
现在,让我们回一下这些关键概念,通过评估来评估你对脏读、幻读和 InnoDB 解决这些问题的方法的理解。
好的,让我们来解决三个评估问题。试着解决每一个,然后我会提供答案。
简单问题(难度:3/10)
假设同时发生两个事务,事务 1 读取了数据对象,事务 2 后来更新了它。识别这种读取问题可能会发生的。
复杂熟问题(难度:6/10)
在数据库事务的上下文中,MySQL 中的 InnoDB 存储引擎是如何处理幻读的?描述一下 ‘一致读取’ 是如何帮助管理这些幻读。
复杂未熟问题(难度:9/10)
你是银行系统的数据库管理员。同时发生两个事务,其中一个是客户 ‘A’ 向客户 ‘B’ 转账,另一个是银行计算客户 ‘A’ 的总余额。由于这些事务的并发性,银行余额计算发生在转账事务未完成之前。解释可能会发生的问题。
简单问题(难度:3/10)
这是一个“脏读”的问题。在数据库管理中,脏读是指事务 1(Transaction 1)可以读取数据库中的行,该行已经由另一事务(Transaction 2)修改,但尚未提交。因此,如果 Transaction 2 回滚更改,Transaction 1 将读取被视为不存在的数据。
复杂熟问题(难度:6/10)
InnoDB 存储引擎在 MySQL 中使用了“一致读取”来处理幻读。一致读取是 InnoDB 选择操作使用的非锁定读取,它为提供数据库的一致性快照提供了支持。它通过应用多版本并发控制(MVCC)来实现,其中存储多个版本的记录。这样,用户就可以无锁定延迟浏览数据库。
复杂未熟问题(难度:9/10)
在给定的场景中,可能会发生不可重复读的问题。不可重复读是指事务读取了同一行两次并获取了不同的数据。在这种情况下,银行余额计算事务可能先计算了余额,然后再次读取数据后客户 ‘A’ 转账了金额。因此,第一次和第二次读取会导致不同的客户 ‘A’ 的余额。
English post: https://programmerscareer.com/mysql-interview12/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:本文为作者原创,转载请注明出处。
评论