本章目标:理解事务为什么存在、并发下会出现哪些问题、隔离级别如何权衡,并掌握事务在业务开发中的正确使用方式。
事务(Transaction)用于保证一组 SQL 要么全部成功、要么全部失败,常见于转账、下单、库存扣减等场景。
flowchart LR
A[BEGIN] --> B[执行SQL-1]
B --> C[执行SQL-2]
C --> D{是否异常?}
D -- 否 --> E[COMMIT]
D -- 是 --> F[ROLLBACK]
事务内操作不可分割,失败则整体回滚。
事务执行前后,数据从一个合法状态变到另一个合法状态。
并发事务之间互不干扰,不会读到“脏中间态”。
事务提交后,数据永久生效(即使系统异常重启)。
-- 开启事务
START TRANSACTION;
-- 或 BEGIN;
-- 业务 SQL
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 成功后提交
COMMIT;
-- 出错时回滚
ROLLBACK;常用设置:
-- 查看是否自动提交(默认 1)
SELECT @@autocommit;
-- 关闭自动提交(当前会话)
SET autocommit = 0;一个事务读到了另一个未提交事务修改的数据。
同一事务内,两次读取同一行数据,结果不一致(中途被别的事务更新并提交)。
同一事务内,两次按条件查询,第二次多/少了“新行”(中途被别的事务插入/删除并提交)。
sequenceDiagram
participant T1 as 事务T1
participant T2 as 事务T2
T1->>DB: SELECT count(*) WHERE status=1
T2->>DB: INSERT 一条 status=1 并 COMMIT
T1->>DB: 再次 SELECT count(*) WHERE status=1
Note over T1: 两次结果不同(幻读)
MySQL 支持 4 个标准隔离级别(从低到高):
READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READ(MySQL InnoDB 默认)SERIALIZABLE| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 并发性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 高 |
| READ COMMITTED | 避免 | 可能 | 可能 | 较高 |
| REPEATABLE READ | 避免 | 避免 | InnoDB 下大多可控 | 中 |
| SERIALIZABLE | 避免 | 避免 | 避免 | 低 |
-- 查看当前会话隔离级别
SELECT @@transaction_isolation;
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;在 READ COMMITTED / REPEATABLE READ 下,MySQL 通过“数据多版本 + Read View”降低读写冲突。
SELECT:通常是快照读(不加锁)CREATE TABLE account (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
balance DECIMAL(12,2) NOT NULL DEFAULT 0
);START TRANSACTION;
UPDATE account SET balance = balance - 100
WHERE id = 1 AND balance >= 100;
UPDATE account SET balance = balance + 100
WHERE id = 2;
COMMIT;业务代码里要判断每一步受影响行数,任一步失败就
ROLLBACK。
balance = balance - ?)。BEGIN/COMMIT 只是入门,真正能力是:并发下依然能保证业务正确。