目录
事务(Transaction)是数据库管理系统中一个非常重要的概念,用于保证数据库操作的完整性和一致性。事务确保了一组操作要么全部成功,要么全部失败,从而避免了数据的不一致性。事务的原理包括以下几个核心概念和特性:
1. 事务的基本特性(ACID)
-
原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不做。事务是一个不可分割的操作单位,即使系统发生故障,事务中的操作也不会部分完成。
-
一致性(Consistency):事务的执行必须使数据库从一个一致性状态变到另一个一致性状态。即事务执行前后,数据库的状态应该是有效的,符合所有的约束条件。
-
隔离性(Isolation):事务的执行应该与其他事务隔离开来,一个事务的操作和数据不会被其他事务干扰。隔离性防止了并发事务之间的干扰,确保每个事务在执行时都能看到一致的数据状态。
-
持久性(Durability):一旦事务提交,它对数据库的修改就应该是持久的,即使系统发生故障,已经提交的事务所做的更改也不会丢失。
2. 事务的状态
一个事务的生命周期包括以下状态:
-
活动(Active):事务正在执行。
-
部分提交(Partially Committed):事务执行到一半,但尚未完全完成。
-
提交(Committed):事务完成,所有操作都已经永久保存到数据库中。
-
回滚(Rolled Back):事务失败,所有操作都被撤销,数据库回到事务开始前的状态。
-
终止(Terminated):事务由于系统故障或其他原因终止,且未完全提交或回滚。
3. 事务的操作
事务主要包括以下操作:
-
开始事务(BEGIN TRANSACTION / START TRANSACTION):标记事务的开始。
-
提交事务(COMMIT):保存事务中的所有更改,使其成为永久性的。
-
回滚事务(ROLLBACK):撤销事务中的所有更改,将数据库恢复到事务开始前的状态。
4. 事务的隔离级别
事务的隔离级别决定了一个事务对其他事务的可见性。常见的隔离级别有:
-
未提交读(Read Uncommitted):事务可以读取其他事务未提交的数据,可能导致脏读。
-
提交读(Read Committed):事务只能读取其他事务已经提交的数据,防止脏读,但可能导致不可重复读。
-
可重复读(Repeatable Read):在事务中多次读取同样的数据结果是一致的,防止脏读和不可重复读,但可能会有幻读。
-
串行化(Serializable):事务完全串行执行,防止所有并发问题,包括脏读、不可重复读和幻读。隔离性最强,但性能开销最大。
5. 事务的日志
数据库系统使用日志来记录事务的操作,以支持事务的回滚和恢复。主要有以下类型:
-
事务日志(Transaction Log):记录事务的所有操作,包括开始、提交和回滚。
-
重做日志(Redo Log):记录已提交事务的操作,以便在系统崩溃后恢复数据。
-
撤销日志(Undo Log):记录未提交事务的操作,以便在事务回滚时撤销这些操作。
6. 事务的实现
事务的实现通常依赖于以下机制:
-
锁机制:用于实现事务的隔离性,确保在事务操作期间数据的一致性和完整性。
-
多版本控制(MVCC):用于实现高并发的读写操作,避免锁的争用。
-
日志管理:用于支持事务的持久性和恢复,确保事务的操作可以在系统崩溃后被恢复。
示例
-- 关闭自动提交模式(可选)
SET AUTOCOMMIT = 0;
-- 开始事务
BEGIN; --或者使用START TRANSACTION;
-- 设置第一个保存点
SAVEPOINT sp1;
-- 执行一些操作
INSERT INTO accounts (account_id, balance) VALUES (3, 500);
-- 设置第二个保存点
SAVEPOINT sp2;
-- 执行另一项操作
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
-- 回滚到第二个保存点
ROLLBACK TO SAVEPOINT sp2;
-- 如果不需要某个回滚点了,可以删除
RELEASE SAVEPOINT sp1;
-- 一旦释放保存点,就无法再回滚到这个保存点。如果事务提交或回滚,所有的保存点都会自动被释放。
-- 此时,更新操作被回滚,但插入操作仍然有效
-- 提交事务
COMMIT;
-- 恢复自动提交模式(可选)
SET AUTOCOMMIT = 1;
注:
如果在 MySQL 事务中没有设置任何回滚点而直接使用
ROLLBACK
命令,整个事务将被完全回滚。也就是说,所有自从事务开始 (START TRANSACTION
或BEGIN
) 以来所做的更改都会被撤销,数据库会恢复到事务开始之前的状态。
MVCC 的工作原理
多版本并发控制(MVCC,Multi-Version Concurrency Control)是数据库系统中的一种技术,用于处理多个事务在并发执行时的读写冲突问题。MVCC 通过为数据库中的每一行数据维护多个版本,从而避免了事务之间的冲突,并实现了高效的并发控制。
在 MySQL 的 InnoDB 存储引擎中,MVCC 的实现主要依赖于隐藏的版本信息,这些版本信息存储在每行记录的元数据中。MVCC 允许读操作和写操作并发执行,而不会发生锁冲突。其核心机制如下:
版本控制
- InnoDB 为每行数据添加了两个隐藏的列,用于存储版本信息:
-
事务 ID (
DB_TRX_ID
):- 这是一个 6 字节的字段,用于记录最后一次对该行进行修改的事务 ID。每当一个事务修改这行数据时,这个字段会更新为当前事务的 ID。
-
回滚指针 (
DB_ROLL_PTR
):- 这是一个 7 字节的字段,指向回滚段的指针。回滚指针用于指向旧版本的记录(Undo Log),这些旧版本记录可以用于事务的回滚操作,以及实现一致性读。
-
隐藏字段的作用
-
事务 ID (
DB_TRX_ID
):用于跟踪是哪一个事务创建或最后修改了这条记录。在实现 MVCC 的过程中,这个字段帮助数据库系统识别当前事务是否可以看到该行数据的某个版本。 -
回滚指针 (
DB_ROLL_PTR
):指向对应行的 Undo Log,Undo Log 保存了数据被修改之前的旧版本。当执行ROLLBACK
时,InnoDB 可以根据回滚指针找到旧版本,从而恢复数据。同时,MVCC 通过读取旧版本来提供一致性读的能力。
隐藏主键 (DB_ROW_ID)
-
生成条件:如果一个表没有显式定义主键,也没有定义唯一非空索引,InnoDB 就会为该表自动生成一个隐藏的 DB_ROW_ID 字段。这个字段不会出现在表结构中,也不会被用户直接访问。
-
作用:DB_ROW_ID 是 InnoDB 用来唯一标识表中每一行的,特别是在没有其他唯一键的情况下,它保证了每一行记录的唯一性。InnoDB 在内部操作(如聚簇索引的管理、行的物理存储、日志记录等)时使用 DB_ROW_ID 进行识别和管理。
数据修改和版本管理流程
1. 生成新版本:
- 当一个事务对某一行数据进行修改时,InnoDB 不会直接覆盖原有的数据行。相反,它会创建该行数据的新版本。这个新版本包括了修改后的数据,并将其插入到表的物理存储中。
2. 存储旧版本:
- 旧版本的数据会被保存在 Undo Log(回滚日志)中。Undo Log 用于存储该行的历史版本,以便事务能够回滚到该版本,或者其他事务能够读取旧版本的数据。
- 回滚指针 (
DB_ROLL_PTR
) 指向这个 Undo Log,以帮助定位和访问旧版本的数据。
3. 更新事务 ID:
- 新版本的行数据会更新
DB_TRX_ID
字段为当前事务的 ID。这个字段记录了最后一次对该数据行进行修改的事务 ID。
并发事务的数据读取
1. 事务快照:
- 每个事务在开始时会创建一个一致性快照,这个快照包含了当前活跃的事务 ID 列表。基于这个快照,事务能确定在其开始时哪些事务已经提交,哪些还在进行。
2. 决定读取版本:
- 当其他并发事务读取该行数据时,它们会根据自己的事务 ID 和快照,决定读取哪个版本的数据。
- 可见性判断:读取操作会检查数据版本的
DB_TRX_ID
和事务快照,只有当数据版本的事务 ID 小于或等于当前事务的快照时间点,且事务 ID 不在当前事务的回滚列表中,才能被读取。
MVCC 的优点
- 高并发:MVCC 允许读写操作并发执行,减少了锁冲突。
- 非阻塞读:读操作不会阻塞写操作,写操作也不会阻塞读操作,提升了系统的并发性能。
- 一致性视图:事务可以读取一致的快照视图,确保数据的一致性。
MVCC 的缺点
- 空间开销:每一行数据可能存在多个版本,这会增加存储空间的开销。
- 实现复杂性:MVCC 的实现需要复杂的版本管理机制,增加了数据库系统的复杂性。