MySQL数据库的事务(Transaction)与多版本并发控制(MVCC)
MySQL数据库的事务(Transaction)与多版本并发控制(MVCC)
事务(Transaction)是数据库管理系统中的一个重要概念,它确保了数据库操作的原子性、一致性、隔离性和持久性。而多版本并发控制(MVCC)则是一种用于管理并发控制和事务隔离级别的数据库技术,它允许在不锁定资源的情况下进行高并发的数据访问,同时保持事务的隔离性。本文将详细介绍MySQL数据库中的事务机制和MVCC的工作原理。
一、事务(Transaction)
1. 事务的概念
事务(Transaction)是数据库管理系统中的一个重要概念,它指的是一组不可分割的数据库操作,这些操作要么全部成功执行要么全部不执行,从而确保数据库从一个一致性状态转换到另一个一致性状态。
举例:
张三使用 100点券 购买游戏道具,此过程后台服务器需要两个操作,首先将张三的点券扣除100点,然后再给张三添加道具。
如果在这个过程中没有开始事务,假如服务器在扣除100点券后宕机了,没有来得及给张三添加道具,那么在服务器恢复后,会出现点券被扣除了但是道具没有获取到的情况,即张三因此白白损失了100点券。
如果开起了事务,在服务器恢复后,数据库会回滚到扣除点券前的状态,张三就不会因此白白损失100点券。
这就是数据库中事务的作用。
在实际应用中,事务通常通过以下语句来控制:
- 开始一个新的事务:
BEGIN;
或
START TRANSACTION;
- 提交当前事务,使所有修改永久生效:
COMMIT;
- 回滚当前事务,撤销所有未提交的修改:
ROLLBACK;
- 设置一个事务的保存点,允许部分回滚到保存点的状态:
SAVEPOINT;
2.事务的特性
事务必须具备以下四个属性,简称ACID属性:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成。如果事务中的某个操作失败,整个事务将回滚到事务开始前的状态,就像这个事务从未执行过一样。
- 一致性(Consistency):事务必须确保数据库从一个一致性状态转换到另一个一致性状态。一致性状态意味着数据库中的数据必须满足所有预定义的规则和约束,例如数据的完整性和业务规则。
- 隔离性(Isolation):并发执行的事务之间是彼此隔离的,不会相互影响。事务的隔离级别定义了事务在并发执行时如何隔离,以避免脏读、不可重复读和幻读等并发问题。
- 永久性(Durability):一旦事务被提交,它对数据库的修改就是永久性的,即使系统发生故障也不会丢失。持久性通常通过将事务的修改写入到持久化存储(如磁盘)来实现。
InnoDB 引擎保证事务四个特性的技术如下:
- 原子性:是通过 回滚日志(undo log)来保证的。
- 持久性:是通过 重做日志(redo log)来保证的。
- 隔离性:是通过 多版本并发控制(MVCC)或锁机制来保证的。
- 一致性:是通过 持久性 + 原子性 + 隔离性 来保证的。
3.并发事务的问题
并发事务可以提高数据库的吞吐量和效率。
当多个事务同时对同一数据进行操作时,可能会遇到脏读(Dirty Read)、不可重复读(Non-Repeatable Read)、幻读(Phantom Read)的问题。
- 脏读(Dirty Read): 一个事务读取了另一个事务未提交的数据。如果那个事务最终回滚了,那么读取到的数据就是无效的,这就是脏读现象。
- 不可重复读(Non-Repeatable Read): 在一个事务中,多次读取同一个数据,由于其他事务的介入,其中某次读取的结果可能不同,就意味着发生了不可重复读现象。
- 幻读(Phantom Read):一个事务在读取某个范围的数据时,如果由于其他事务的插入操作,导致再次读取时出现了 幻影 数据,即出现前后两次查询到的记录数量不一样的情况,就意味着发生了幻读现象。
4.事务的隔离级别
为了解决并发事务的问题,数据库系统通常提供以下几种隔离级别:
- 读未提交(read uncommitted):指一个事务还没提交时,它做的变更就能被其他事务看到。此种隔离级别可能发生脏读、不可重复读、幻读现象。
- 读提交(read committed):指一个事务提交之后,它做的变更才能被其他事务看到。此种隔离级别避免了脏读现象,但是可能发生不可重复读、幻读现象。
- 可重复读(repeatable read):指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。此种隔离级别避免了脏读、不可重复读现象,但是可能发生幻读现象。
- 串行化(serializable):会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。此种隔离级别避免了脏读、不可重复读、幻读现象。
MySQL默认的隔离级别是RR(Repetable Read)
数据库系统通过锁机制、多版本并发控制(MVCC)等技术来实现这些隔离级别,以确保事务的隔离性和数据的一致性。
二、多版本并发控制(MVCC)
1. MVCC 概论
多版本并发控制(Multi-Version Concurrency Control,MVCC)是一种用于管理并发控制和事务隔离级别的数据库技术。它允许在不锁定资源的情况下进行高并发的数据访问,同时保持事务的隔离性。
MVCC通过保存数据的多个版本来实现这一点,每个事务都可以访问到它应该看到的数据版本。
MVCC的特点:
无锁读取: MVCC允许非锁定的读取操作。这意味着读操作不会阻塞写操作,写操作也不会阻塞读操作。读操作可以访问数据的历史版本,而不会干扰当前正在进行的写操作。
版本控制: 每当数据被修改时(如更新或删除),MVCC不会直接在原始数据上进行更改,而是创建一个新的数据版本。这个新版本包含了修改后的数据,而旧版本仍然保留以供其他事务使用。
快照读取: 事务在开始时会创建一个数据库的快照,这个快照包含了事务开始时所有数据的最新版本。即使数据在事务执行期间被其他事务修改,该事务仍然会读取到它开始时的快照数据。
事务隔离性: MVCC通过控制不同事务对数据版本的访问来实现不同的隔离级别。例如,在可重复读(Repeatable Read)隔离级别下,事务在整个过程中都会看到一致的数据视图,即使其他事务已经提交了对数据的更改。
垃圾收集: 由于MVCC会创建多个数据版本,因此需要一种机制来清理不再需要的旧版本。这通常通过垃圾收集器来完成,它会在没有事务再需要某个特定版本时删除它。
性能优化: MVCC可以提高数据库的性能,因为它减少了锁的争用,允许更多的并发操作。这对于读多写少的数据库系统尤其有利。
MVCC在许多现代数据库系统中得到了应用,如PostgreSQL、Oracle、MySQL的InnoDB存储引擎等。这些系统通过MVCC实现了高效的并发控制和事务隔离,同时保持了数据的一致性和完整性。
当前读与快照读:
- 当前读:加锁的 SELECT 就属于当前读,例如select … lock in share mode、select … for update 等,它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- 快照读(SnapShot Read):不加锁的 select 操作就是快照读,它是一致性不加锁的读。事务读取到的数据,要么是事务开始前就已经存在的数据,要么是事务自身插入或者修改过的数据。
MVCC多版本并发控制指的是维持一个数据的多个版本,使得读写操作没有冲突。快照读就是MySQL为我们实现MVCC模型的其中一个具体非阻塞读功能。
2. MVCC 工作流程
MVCC模型在MySQL中的具体实现则是由 3个隐式字段(trx_id、roll_pointer、row_id)、undo log 、Read View 等完成的。
1. 三个隐式字段
隐式字段 字段名 字段作用 字段大小
trx_id 最近修改事务id 记录创建这条记录或者最后一次修改该记录的事务id 6字节
roll_pointer 回滚指针 指向这条记录的上一个版本,用于配合undolog,指向上一个旧版本 7字节
row_id 隐藏主键 如果数据表没有主键,那么innodb会自动生成一个6字节的row_id 6字节
2. 回滚日志(undo log)
为了实现事务的回滚,MySQL会在事务执行过程中记录所有必要的回滚信息到回滚日志中。这样,一旦数据库在事务中途崩溃,系统就可以利用这些日志信息将数据恢复到事务开始前的状态,确保数据的一致性和完整性
回滚日志(Undo Log) 是数据库系统中,特别是支持事务的数据库系统中,用于处理事务失败时的回滚操作以及保持事务隔离性的一种日志。
3. 读视图(Read View)
Read View 是事务在执行快照读时创建的一种视图。这个视图捕捉了事务开始读取数据那一刻数据库的状态,包括所有当时活跃的事务ID。
Read View 中维护了四个字段:
m_ids 列表:指在创建 Read View 时,数据库中所有已经启动但尚未提交的事务的Id列表。这个列表记录了在快照读操作发生那一刻,所有活跃事务的标识。活跃事务指的是那些已经开始执行但还没有完成提交操作的事务。这些事务可能正在对数据库进行修改,但这些修改对其他事务还不可见,直到它们提交。
min_trx_id: 指在创建 Read View 时,所有活跃事务中Id最小的那个事务的Id。这个值代表了
m_ids
列表中的最小值,即最早启动但还未提交的事务的Id。
max_trx_id:指在创建 Read View 时,数据库中下一个事务应该被分配的Id值。
max_trx_id
并不是
m_ids
列表中的最大值,而是当前全局事务中最大的事务Id加1,表示下一个新事务的预期Id。
creator_trx_id: 指创建该 Read View 的事务自己的Id,即执行快照读操作的事务的Id。这个Id用于标识是哪个事务创建了这个特定的Read View。
4. MVCC 工作流程
MVCC的工作是依赖于三个隐藏字段、回滚日志(undo log)、读视图(Read View)的。
MVCC工作流程如下:
以下是MVCC(多版本并发控制)的工作流程:
创建Read View:当事务开始时,InnoDB会为该事务创建一个Read View,这个Read View包含了事务开始时数据库的快照,记录了所有已经提交的事务信息。
查找数据版本:事务根据Read View中的信息,查找数据的适当版本进行读取。根据数据的版本信息,MVCC中数据版本的可见性和不可见性情况有以下几种:
- 事务 trx_id 小于当前事务的 Read View 中的 min_trx_id:表示该版本是在当前事务开始之前提交的,因此对当前事务可见。
- 事务 trx_id 大于等于当前事务的 Read View 中的 max_trx_id:意味着该版本是在当前事务开始之后创建的,还未提交或正在提交,对当前事务不可见。
- 事务 trx_id 在当前事务的 Read View 中的 min_trx_id 与 max_trx_id 之间,且该事务的 trx_id 不在 m_ids 列表中:说明这个版本是在当前事务开始时或之后提交的,且已经完成提交,所以对当前事务可见。
- 事务 trx_id 在当前事务的 Read View 中的 min_trx_id 与 max_trx_id 之间,且该事务的trx_id 在 m_ids 列表中:虽然这个版本是在当前事务开始时或之后创建的,但还没有完成提交,因此对当前事务不可见。
MVCC通过快照读的方式获取数据,不加锁读取之前的快照数据,适用于普通的SELECT语句。
生成新版本:当事务需要修改数据时,会创建数据的新版本,并将其添加到版本链中。同时,更新事务Id和其他相关信息。InnoDB会将旧版本的数据存储在 Undo Log 中,并更新当前行的 trx_id 和 roll_pointer。这样,数据库可以根据不同事务的需求,选择合适的数据版本提供给查询。
标记提交状态:当事务提交时,会将其相关的版本标记为已提交状态。已提交事务的数据版本对其他事务可见。
清理旧版本:系统会定期清理不再需要的旧数据版本,以节省存储空间。