DBMS 中 SQL 的脏读

2024年8月28日 | 阅读 7 分钟

在继续之前,让我们了解一些更好地理解该主题的先决条件

事务隔离级别

众所周知,数据库必须遵循 ACID 特性才能保持一致性。隔离是这四个特性(原子性、一致性、隔离性和持久性)之一,它决定了事务完整性对其他用户和系统可见的程度。它表示事务必须在系统中发生,以便它是唯一使用数据库系统资源的事务。

事务必须与其他数据库系统中其他事务执行的数据更新隔离的程度由其隔离级别决定。以下现象用于确定事务隔离级别

  • 当一个事务读取尚未提交的数据时,就会发生脏读,并因此得名。让我们举个例子,事务 1 修改了一行但未提交,而事务 2 在此期间读取了已修改的行。如果事务 1 回滚了修改,那么事务 2 将读取被认为从未存在过的数据。
  • 当一个事务两次读取同一行并每次都收到不同的值时,就称其正在执行不可重复读。考虑事务 T1 读取数据的场景。如果另一个事务 T2 更新并提交了相同数据,由于并发性,事务 T1 再次读取相同数据时将返回不同的值。
  • 幻读:当执行两个相同的查询,但每个查询返回的行不同时,就会发生幻读。例如,假设事务 T1 获取一组符合一组搜索条件的行。现在,事务 T2 创建了一些符合事务 T1 搜索条件的新行。如果事务 T1 再次执行读取行的语句,它将获得一组不同的行。

顾名思义,调度是设置事务并逐个执行它们的过程。当有大量事务同时运行时,并且需要指定操作顺序以使操作不重叠时,调度就会发挥作用,并且事务会适当地计时。并发控制(介绍)和 DBMS 中的事务隔离级别文章分别介绍了事务和调度的基础知识。我们将在本文中讨论不同的时间表。

  • 串行调度:串行调度是指在一个正在运行的事务完成之前,没有事务开始。这些调度不交错地执行事务。
  • 非串行调度:这种调度方式涉及不同事务进程的交错。这可能会导致并发问题恶化。事务以非串行方式执行,同时保持串行时间表的准确性和一致性。在非串行调度中,与串行调度不同,一个事务必须等待另一个事务完成所有操作,后续事务可以不等待前一个事务完成而开始。这样的调度不允许任何并发事务的好处。它有两种类型:可串行化调度和不可串行化调度。

非串行调度分为两类:可串行化和不可串行化。

a. 可串行化

为了保持数据库一致,它必须是可串行化的。它主要用于非串行调度,以确定调度是否会导致任何不一致。另一方面,串行调度不需要可串行化,因为它只在前一个事务完成后才遵循一个事务。只有当非串行调度与 n 个事务的串行调度可比时,才被认为是可串行化调度。由于允许并发,在这种情况下,许多事务可以同时运行。可串行化调度有助于提高 CPU 性能和资源利用率。它们有两种类型

i. 冲突可串行化

如果一个调度可以通过交换非冲突进程转换为串行调度,则称其为冲突可串行化。如果满足所有要求,则称两个操作冲突。

  • 它们与某些事务相关联。
  • 它们处理相同的数据块。
  • 其中之一必须是写入操作。

ii. 视图可串行化

如果一个调度与串行调度视图等价(没有重叠事务),则称其为视图可串行化。冲突调度是视图可串行化的,但如果可串行化包含盲写,则不是冲突可串行化的。

b. 不可串行化

不可串行化调度中的两类是可恢复调度和不可恢复调度。

可恢复调度:可恢复调度中的事务仅在其读取其修改的所有事务都已提交后才提交。换句话说,如果某个事务 Tj 正在读取已被某个其他事务 Ti 更改或写入的值,则 Tj 的提交必须发生在 Ti 的提交之后。

可能存在三种不同的可恢复调度类型

i. 级联调度

当一个事务中的失败导致其他依赖事务回滚或中止时,使用级联回滚或级联中止调度。

ii. 无级联调度

无级联调度中的事务仅在其打算查看其修改的所有事务都已提交后才读取数据。阻止单个事务中止导致多个事务回滚。防止事务读取同一调度中另一个事务未提交的修改是阻止级联中止的一种方法。

当一个事务读取已被另一个事务更新但尚未提交的数据时,这在 SQL 中被称为“脏读”。换句话说,将未提交的数据从一个事务读取到另一个事务可能会产生不准确或不一致的结果。

当一个事务更新一个数据项但由于网络故障、系统故障或其他问题而未能提交更改时,可能会发生这种情况。如果另一个事务在第一个事务有机会提交之前读取了更新的数据,则可能会发生脏读。

例如,考虑两个事务 T1 和 T2。T1 启动一个事务并更改表中的一行,但它不完成。在此期间,T2 尝试读取同一行,在 T1 提交其修改之前。如果未提交的数据对其可用,T2 将执行脏读,这可能会产生不可靠或不一致的结果。

SQL 具有多级事务隔离,它定义了事务应如何相互隔离并有助于阻止脏读。隔离级别包括

读取未提交:在此级别,允许事务访问来自其他事务的未提交数据,这可能导致脏读。

读取已提交:在此级别,事务仅允许读取已提交的数据,从而防止脏读。

可重复读:此级别保证事务始终为特定查询读取相同数据,即使其他事务在此期间更改数据。它也禁止脏读。

可串行化提供了最高级别的隔离,它保证事务是串行执行的,并防止脏读等异常。

应使用事务隔离级别来避免 SQL 中的脏读,这可能会产生错误或不一致的结果。

常见的并发问题通常分为四类:脏读、丢失读、不可重复读和幻读。

当允许一个事务读取已被另一个事务更新但尚未提交的行时,就会发生脏读。这主要是由于同时发生多个未提交的事务造成的。

示例 - 表 - 记录

从 S Adam 账户向 Zee Young 账户转账 10 美元

执行上述查询将导致响应“未提交”,因为存在问题?没有 ID=C。如果我们希望在另一个事务中使用该行,那么就会发生脏读。如果两个 UPDATE 查询都成功,那么输出将是“已提交”,没有部分提交。执行前:表 - 记录

请注意,如果输入了有效的 ID,第一个事务结果将显示为已提交或影响了一行,但第二个事务结果不会受到影响。解释:如果有一个机票预订系统,一位客户试图在有 10 张票可用时预订一张票,那么第二个事务将通知第二个消费者当时还有 9 个座位可供预订。令人惊讶的是,如果第一个客户当时借记卡或钱包里没有足够的钱,第一个事务将回滚,此时还有 9 个座位,这是第二个事务指示的。

示例:对于第一个客户,可用票

第 1 步 -

第 2 步 - 第 1 位客户的预订时间

第 3 步 -

请注意,如果第一个事务在第一个客户付款期间因任何原因失败,则有九个座位可用。回滚,然后读取可用的座位 9 的脏数据。在第一个事务回滚后,再次有 10 个座位可用。第三步和第二步同时发生。在事务 1 回滚后,实际可用的座位是