Skip to content

事务处理

事务概述

事务是一组数据库操作的逻辑单元,要么全部成功,要么全部失败。

ACID 特性

特性说明
原子性 (Atomicity)事务是不可分割的工作单位
一致性 (Consistency)事务前后数据保持一致状态
隔离性 (Isolation)多个事务互不干扰
持久性 (Durability)事务提交后永久保存

事务控制

基本语法

sql
START TRANSACTION;
BEGIN;

COMMIT;
ROLLBACK;

示例

sql
START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT;

保存点

sql
START TRANSACTION;

INSERT INTO orders (user_id, amount) VALUES (1, 100);
SAVEPOINT sp1;

INSERT INTO order_items (order_id, product_id) VALUES (1, 1);
SAVEPOINT sp2;

ROLLBACK TO sp1;
COMMIT;

隔离级别

四种隔离级别

级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

查看隔离级别

sql
SELECT @@transaction_isolation;
SELECT @@global.transaction_isolation;

设置隔离级别

sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

各隔离级别详解

READ UNCOMMITTED(读未提交)

sql
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

可能读取到其他事务未提交的数据(脏读)。

READ COMMITTED(读已提交)

sql
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

只能读取已提交的数据,但可能出现不可重复读。

REPEATABLE READ(可重复读)

sql
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

MySQL 默认隔离级别,同一事务中多次读取结果相同。

SERIALIZABLE(串行化)

sql
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

最高隔离级别,完全串行执行,性能最低。

并发问题

脏读

读取到其他事务未提交的数据。

事务A: UPDATE users SET age = 30 WHERE id = 1;
事务B: SELECT age FROM users WHERE id = 1;  -- 读取到 30
事务A: ROLLBACK;  -- age 实际未改变

不可重复读

同一事务中两次读取结果不同。

事务A: SELECT age FROM users WHERE id = 1;  -- 25
事务B: UPDATE users SET age = 30 WHERE id = 1; COMMIT;
事务A: SELECT age FROM users WHERE id = 1;  -- 30

幻读

同一事务中两次查询记录数不同。

事务A: SELECT * FROM users WHERE age > 20;  -- 5条
事务B: INSERT INTO users (age) VALUES (25); COMMIT;
事务A: SELECT * FROM users WHERE age > 20;  -- 6条

锁机制

锁类型

锁类型说明
共享锁 (S)读锁,允许多个事务同时读取
排他锁 (X)写锁,独占资源
意向锁表级锁,表示事务意图

行级锁

sql
SELECT * FROM users WHERE id = 1 FOR UPDATE;
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
SELECT * FROM users WHERE id = 1 FOR SHARE;

表级锁

sql
LOCK TABLES users READ;
LOCK TABLES users WRITE;
UNLOCK TABLES;

间隙锁

sql
SELECT * FROM users WHERE id BETWEEN 1 AND 10 FOR UPDATE;

临键锁 (Next-Key Lock)

InnoDB 在 REPEATABLE READ 级别下使用临键锁防止幻读。

死锁

死锁示例

事务A: UPDATE users SET age = 1 WHERE id = 1;
事务B: UPDATE users SET age = 2 WHERE id = 2;
事务A: UPDATE users SET age = 3 WHERE id = 2;  -- 等待
事务B: UPDATE users SET age = 4 WHERE id = 1;  -- 死锁

查看死锁

sql
SHOW ENGINE INNODB STATUS;

避免死锁

  1. 按相同顺序访问表和行
  2. 避免长事务
  3. 使用较低的隔离级别
  4. 合理设计索引

事务日志

Redo Log(重做日志)

保证事务持久性,崩溃恢复时使用。

sql
SHOW VARIABLES LIKE 'innodb_log%';

Undo Log(回滚日志)

保证事务原子性,用于回滚和 MVCC。

sql
SHOW VARIABLES LIKE 'innodb_undo%';

Binlog(二进制日志)

用于主从复制和增量备份。

sql
SHOW VARIABLES LIKE 'log_bin%';
SHOW BINARY LOGS;

MVCC 多版本并发控制

实现原理

  • 每行数据包含隐藏列:DB_TRX_ID、DB_ROLL_PTR
  • Undo Log 保存历史版本
  • Read View 决定可见性

Read View

字段说明
m_ids活跃事务ID列表
min_trx_id最小活跃事务ID
max_trx_id下一个事务ID
creator_trx_id创建者事务ID

可见性判断

如果 DB_TRX_ID < min_trx_id:可见
如果 DB_TRX_ID >= max_trx_id:不可见
如果 DB_TRX_ID 在 m_ids 中:不可见
否则:可见

下一步学习