banner
ShuWa

ShuWa

是进亦忧,退亦忧。然则何时而乐耶?
twitter

事務

事务有哪些特性?#

事务必须遵守以下四个特性,而 MyISAM 引擎不支持事务:

  • 原子性(Atomicity):一个事务中的所有操作要么全部完成,要么全部不完成。事务在执行过程中发生错误时,会回滚到事务开始前的状态。
  • 一致性(Consistency):事务操作前后,数据必须满足完整性约束,数据库保持一致性状态。
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致。每个事务都有一个完整的数据空间,对其他并发事务是隔离的。
  • 持久性(Durability):事务处理结束后,对数据的修改是永久的,即使系统故障也不会丢失。

InnoDB 引擎通过以下技术来保证事务的这四个特性:

  • 持久性是通过 redo log(重做日志)来保证的。
  • 原子性是通过 undo log(回滚日志)来保证的。
  • 隔离性是通过 MVCC(多版本并发控制)或锁机制来保证的。
  • 一致性是通过持久性 + 原子性 + 隔离性来保证。

并行事务会引发什么问题?#

在同时处理多个事务时,可能会出现以下问题:

  • 脏读(dirty read):一个事务读取了另一个未提交事务修改过的数据,导致脏读。
  • 不可重复读(non-repeatable read):在一个事务内多次读取同一数据,如果前后两次读取的数据不一样,就发生了不可重复读。
  • 幻读(phantom read):在一个事务内多次查询符合条件的记录数量,如果前后两次查询到的记录数量不一样,就发生了幻读。

事务的隔离级别有哪些?#

SQL 标准提出了四种隔离级别来规避并发事务引发的问题。隔离级别越高,性能效率越低。这四个隔离级别如下:

  • 读未提交(read uncommitted):一个事务未提交时,其变更可以被其他事务看到。
  • 读提交(read committed):一个事务提交后,其变更才能被其他事务看到。
  • 可重复读(repeatable read):一个事务执行过程中看到的数据与事务启动时看到的数据一致。MySQL InnoDB 引擎的默认隔离级别。
  • 串行化(serializable):对记录加上读写锁,在多个事务对同一记录进行读写操作时,后访问的事务必须等待前一个事务执行完成。

MySQL InnoDB 引擎的默认隔离级别是可重复读,它在很大程度上避免了幻读现象。为了避免幻读,有两种解决方案:

  • 对于快照读(普通 select 语句),通过 MVCC 方式解决幻读。在可重复读隔离级别下,事务执行过程中看到的数据与事务启动时看到的数据一致。即使其他事务在中间插入了一条数据,该数据对查询是不可见的,从而避免了幻读问题。
  • 对于当前读(select ... for update 等语句),通过 next-key lock(记录锁 + 间隙锁)方式解决幻读。当执行 select ... for update 语句时,会加上 next-key lock。如果其他事务在该锁范围内插入了一条记录,插入语句将被阻塞,从而避免了幻读问题。

如何通过 Read View 实现 MVCC#

Read View 是什么?#

Read View 有四个字段:

image

除了 Read View 的字段,我们还需要了解聚簇索引记录中的两个隐藏列:

image

对于使用 InnoDB 存储引擎的数据库表,聚簇索引记录中包含以下两个隐藏列:

  • trx_id:当一个事务对某条聚簇索引记录进行改动时,会将该事务的事务 ID 记录在 trx_id 隐藏列中。
  • roll_pointer:每次对某条聚簇索引记录进行改动时,会将旧版本的记录写入 undo 日志,并使用该隐藏列作为指针,指向每个旧版本记录,从而可以找到修改前的记录。

创建 Read View 后,可以根据记录的 trx_id 划分为三种情况:

image

除了自己的更新记录总是可见外,事务还可以看到以下情况的记录:

  • 如果记录的 trx_id 小于 Read View 中的 min_trx_id,表示该版本的记录是在创建 Read View 之前已提交的事务生成的,因此该版本的记录对当前事务可见。
  • 如果记录的 trx_id 大于等于 Read View 中的 max_trx_id,表示该版本的记录是在创建 Read View 之后启动的事务生成的,因此该版本的记录对当前事务不可见。
  • 如果记录的 trx_id 在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
    • 如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务仍然活跃(尚未提交事务),因此该版本的记录对当前事务不可见。
    • 如果记录的 trx_id 不在 m_ids 列表中,表示生成该版本记录的活跃事务已经提交,因此该版本的记录对当前事务可见。

通过 "版本链" 来控制并发事务访问同一记录时的行为,这就是 MVCC(多版本并发控制)。

对于 "读提交" 和 "可重复读" 隔离级别的事务,它们通过 Read View 来实现,它们的区别在于创建 Read View 的时机不同:

  • "读提交" 隔离级别在每个 select 语句都会生成一个新的 Read View,这意味着事务期间多次读取同一数据,前后两次读取的数据可能不一致,因为期间可能有其他事务修改了该记录并提交了事务。
  • "可重复读" 隔离级别在启动事务时生成一个 Read View,然后整个事务期间都使用该 Read View,这保证了事务期间读取的数据都是事务启动前的记录。
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。