MySQL 事务具有 ACID 四大特性,这些特性保证了数据的一致性和可靠性。
- **原子性(Atomicity)**:事务里的操作是一个不可分割的整体,要么全部成功执行,要么全部失败回滚。以银行转账为例,从 A 账户向 B 账户转 100 元,包含 A 账户扣款和 B 账户收款两个操作。若扣款成功但收款时系统出错,整个事务会回滚,A 账户不会减少金额。
- **一致性(Consistency)**:事务执行前后,数据库需保持一致状态,不能破坏数据库的完整性约束,如主键约束、唯一约束等。还是以转账为例,转账前后两个账户的总金额应保持不变,若因错误导致总金额改变,就违背了一致性原则。
- **隔离性(Isolation)**:多个事务并发执行时,一个事务的执行不应被其他事务干扰,就像每个事务都在独立访问数据库。MySQL 提供了不同隔离级别来控制事务间的隔离程度。
- **持久性(Durability)**:事务提交成功后,其对数据库的修改会永久保存,即便数据库系统出现故障也不会丢失。通常数据库会把事务修改记录到磁盘日志文件中,系统崩溃后可通过日志恢复。
事务的隔离级别
MySQL 定义了 4 种隔离级别,不同级别在处理并发事务时的表现不同。
- **读未提交(Read Uncommitted)**
- 允许一个事务读取另一个未提交事务的数据。这种隔离级别可能会导致脏读(读取到未提交的数据)、不可重复读(多次读取同一数据结果不同)和幻读(查询时发现数据记录数发生变化)问题。
- 示例:事务 A 修改了数据但未提交,事务 B 读取到了这些未提交的数据,若事务 A 回滚,事务 B 读取的数据就是无效的,即发生了脏读。
- **读已提交(Read Committed)**
- 一个事务只能读取另一个已提交事务的数据。该级别避免了脏读问题,但仍可能出现不可重复读和幻读问题。
- 示例:事务 A 读取某条记录,事务 B 修改该记录并提交,事务 A 再次读取时得到不同结果,这就是不可重复读。
- **可重复读(Repeatable Read)**
- 这是 MySQL 的默认隔离级别。在一个事务中,多次读取同一数据结果保持一致,避免了脏读和不可重复读问题,但仍可能存在幻读问题。
- 示例:事务 A 第一次查询某范围的数据有 10 条记录,事务 B 插入一条符合该范围的记录并提交,事务 A 再次查询该范围数据时发现有 11 条记录,出现了幻读。不过,MySQL 的 InnoDB 存储引擎通过间隙锁在一定程度上解决了幻读问题。
- **串行化(Serializable)**
- 最高的隔离级别,所有事务串行执行,避免了脏读、不可重复读和幻读问题。但这种隔离级别会导致并发性能极低,因为事务需要排队执行。
- 示例:事务 A 执行时,其他事务必须等待其完成才能开始,保证了数据的绝对一致性。
事务的基本使用
在 MySQL 中,事务的基本使用涉及几个关键语句。
显式事务
sql
-- 开始事务
START TRANSACTION;
-- 执行一系列 SQL 语句
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 提交事务,使所有操作生效
COMMIT;
-- 或者回滚事务,撤销所有操作
-- ROLLBACK;
自动提交模式
MySQL 默认开启自动提交模式,每条 SQL 语句都会被当作一个单独的事务自动提交。可以通过以下语句关闭自动提交模式:
sql
-- 关闭自动提交模式
SET autocommit = 0;
-- 执行一系列 SQL 语句
UPDATE table_name SET column1 = value1 WHERE condition;
-- 提交事务
COMMIT;
-- 若需要回滚
-- ROLLBACK;
-- 开启自动提交模式
SET autocommit = 1;
使用 `SAVEPOINT` 设置保存点
保存点可在事务中设置中间点,方便部分回滚。
sql
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 设置保存点
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 若发现第二个操作有问题,可回滚到保存点
ROLLBACK TO sp1;
-- 提交事务
COMMIT;