事务:一组数据库操作,要么全部成功,要么全部失败。
东巴文理解:
事务 = 原子操作
转账操作 = 扣款 + 到账
要么都成功,要么都失败
不能只完成一半
原子性(Atomicity):
一致性(Consistency):
隔离性(Isolation):
持久性(Durability):
语法格式:
START TRANSACTION;
-- 或
BEGIN;
示例:
-- 开启事务
START TRANSACTION;
-- 执行SQL语句
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 提交事务
COMMIT;
语法格式:
COMMIT;
示例:
-- 转账事务
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 提交事务
COMMIT;
语法格式:
ROLLBACK;
示例:
-- 转账事务
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 发现错误,回滚事务
ROLLBACK;
语法格式:
SAVEPOINT 保存点名;
ROLLBACK TO SAVEPOINT 保存点名;
RELEASE SAVEPOINT 保存点名;
示例:
START TRANSACTION;
INSERT INTO orders (id, amount) VALUES (1, 100);
SAVEPOINT sp1;
INSERT INTO orders (id, amount) VALUES (2, 200);
SAVEPOINT sp2;
INSERT INTO orders (id, amount) VALUES (3, 300);
-- 回滚到保存点sp2
ROLLBACK TO SAVEPOINT sp2;
-- 提交事务
COMMIT;
-- 最终只插入id=1和id=2的记录
-- 查看自动提交状态
SHOW VARIABLES LIKE 'autocommit';
-- 关闭自动提交
SET autocommit = 0;
-- 开启自动提交
SET autocommit = 1;
注意:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| 读未提交(READ UNCOMMITTED) | 可能 | 可能 | 可能 | 最高 |
| 读已提交(READ COMMITTED) | 不可能 | 可能 | 可能 | 高 |
| 可重复读(REPEATABLE READ) | 不可能 | 不可能 | 可能 | 中 |
| 串行化(SERIALIZABLE) | 不可能 | 不可能 | 不可能 | 最低 |
脏读:
不可重复读:
幻读:
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 查看全局隔离级别
SELECT @@global.transaction_isolation;
语法格式:
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
示例:
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
设置隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
演示脏读:
-- 会话A
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 未提交
-- 会话B
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 读取到未提交的数据
COMMIT;
-- 会话A
ROLLBACK; -- 回滚
设置隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
演示不可重复读:
-- 会话A
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 第一次查询
-- 会话B
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT; -- 提交
-- 会话A
SELECT balance FROM accounts WHERE id = 1; -- 第二次查询,结果不同
COMMIT;
设置隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
演示避免不可重复读:
-- 会话A
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 第一次查询
-- 会话B
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT; -- 提交
-- 会话A
SELECT balance FROM accounts WHERE id = 1; -- 第二次查询,结果相同
COMMIT;
注意:MySQL的REPEATABLE READ通过MVCC和Next-Key Lock避免了幻读。
设置隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
演示避免幻读:
-- 会话A
START TRANSACTION;
SELECT * FROM accounts WHERE id > 0; -- 第一次查询
-- 会话B
START TRANSACTION;
INSERT INTO accounts (id, balance) VALUES (3, 1000); -- 被阻塞
COMMIT;
-- 会话A
SELECT * FROM accounts WHERE id > 0; -- 第二次查询,结果相同
COMMIT;
特点:
语法格式:
LOCK TABLES 表名 READ|WRITE;
UNLOCK TABLES;
示例:
-- 加读锁
LOCK TABLES accounts READ;
SELECT * FROM accounts;
UNLOCK TABLES;
-- 加写锁
LOCK TABLES accounts WRITE;
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
UNLOCK TABLES;
特点:
示例:
-- 行锁(通过事务)
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 加排他锁
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
特点:
语法格式:
SELECT * FROM 表名 WHERE 条件 LOCK IN SHARE MODE;
-- 或(MySQL 8.0+)
SELECT * FROM 表名 WHERE 条件 FOR SHARE;
示例:
-- 会话A
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE; -- 加共享锁
-- 会话B
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE; -- 成功
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 被阻塞
COMMIT;
-- 会话A
COMMIT; -- 释放锁
特点:
语法格式:
SELECT * FROM 表名 WHERE 条件 FOR UPDATE;
示例:
-- 会话A
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 加排他锁
-- 会话B
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 被阻塞
COMMIT;
-- 会话A
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT; -- 释放锁
意向锁:表级锁,表示事务打算在表中的行上加锁。
类型:
作用:
| 锁类型 | IS | IX | S | X |
|---|---|---|---|---|
| IS | ✅ | ✅ | ✅ | ❌ |
| IX | ✅ | ✅ | ❌ | ❌ |
| S | ✅ | ❌ | ✅ | ❌ |
| X | ❌ | ❌ | ❌ | ❌ |
特点:
示例:
-- 锁定id=1的记录
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
特点:
示例:
-- 锁定id在1到3之间的间隙(不包含1和3)
SELECT * FROM accounts WHERE id > 1 AND id < 3 FOR UPDATE;
特点:
示例:
-- 锁定id<=3的记录和间隙
SELECT * FROM accounts WHERE id <= 3 FOR UPDATE;
死锁:两个或多个事务互相等待对方释放锁,导致永久阻塞。
示例:
-- 会话A
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 锁定id=1
-- 会话B
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 2; -- 锁定id=2
-- 会话A
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 等待id=2的锁
-- 会话B
UPDATE accounts SET balance = balance + 100 WHERE id = 1; -- 等待id=1的锁
-- 死锁发生
-- 查看死锁信息
SHOW ENGINE INNODB STATUS;
输出:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-01-01 12:00:00 0x7f8a8c0b6700
*** (1) TRANSACTION:
TRANSACTION 12345, ACTIVE 2 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 10, OS thread handle 140234567891456, query id 100 localhost root updating
UPDATE accounts SET balance = balance + 100 WHERE id = 2
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 58 page no 4 n bits 72 index PRIMARY of table `test`.`accounts` trx id 12345 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
...
-- 查看死锁检测配置
SHOW VARIABLES LIKE 'innodb_deadlock_detect';
-- 开启死锁检测
SET GLOBAL innodb_deadlock_detect = ON;
1. 按固定顺序访问表:
-- 不推荐:不同事务访问顺序不同
-- 事务A:先访问表1,再访问表2
-- 事务B:先访问表2,再访问表1
-- 推荐:所有事务按相同顺序访问
-- 事务A:先访问表1,再访问表2
-- 事务B:先访问表1,再访问表2
2. 大事务拆小事务:
-- 不推荐:大事务
START TRANSACTION;
-- 大量操作
COMMIT;
-- 推荐:小事务
START TRANSACTION;
-- 少量操作
COMMIT;
3. 尽量使用索引访问:
-- 不推荐:无索引,锁全表
UPDATE accounts SET balance = balance - 100 WHERE name = '张三';
-- 推荐:有索引,锁行
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
4. 设置锁等待超时:
-- 查看锁等待超时时间
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
-- 设置锁等待超时时间(秒)
SET innodb_lock_wait_timeout = 10;
MySQL检测到死锁后,自动回滚其中一个事务。
查看被回滚的事务:
SHOW ENGINE INNODB STATUS;
-- 查看当前运行的事务
SELECT * FROM information_schema.INNODB_TRX;
-- 查看当前的锁
SELECT * FROM information_schema.INNODB_LOCKS;
-- 查看锁等待
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 杀死事务
KILL 事务ID;
MVCC(多版本并发控制):通过保存数据的历史版本,实现非阻塞读。
优点:
InnoDB每行数据包含:
作用:
示例:
当前数据:
id=1, name='张三', DB_TRX_ID=100, DB_ROLL_PTR=0x123
Undo Log(旧版本):
id=1, name='李四', DB_TRX_ID=99, DB_ROLL_PTR=0x456
Read View:事务进行快照读时产生的读视图。
包含内容:
可见性判断:
特点:
示例:
-- 快照读
SELECT * FROM accounts WHERE id = 1;
特点:
示例:
-- 当前读
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;
INSERT INTO accounts VALUES (...);
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
DELETE FROM accounts WHERE id = 1;
-- 创建账户表
CREATE TABLE accounts (
id INT PRIMARY KEY,
name VARCHAR(50),
balance DECIMAL(10, 2)
);
INSERT INTO accounts VALUES
(1, '张三', 1000.00),
(2, '李四', 1000.00);
-- 转账事务
DELIMITER //
CREATE PROCEDURE transfer(IN from_id INT, IN to_id INT, IN amount DECIMAL(10, 2))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT '转账失败' AS result;
END;
START TRANSACTION;
-- 检查余额
IF (SELECT balance FROM accounts WHERE id = from_id) >= amount THEN
-- 扣款
UPDATE accounts SET balance = balance - amount WHERE id = from_id;
-- 到账
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
COMMIT;
SELECT '转账成功' AS result;
ELSE
ROLLBACK;
SELECT '余额不足' AS result;
END IF;
END //
DELIMITER ;
-- 调用存储过程
CALL transfer(1, 2, 100.00);
-- 创建商品表
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(50),
stock INT
);
INSERT INTO products VALUES (1, 'iPhone', 100);
-- 扣减库存(避免超卖)
START TRANSACTION;
-- 使用排他锁锁定商品
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
-- 检查库存
IF (SELECT stock FROM products WHERE id = 1) > 0 THEN
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;
SELECT '扣减成功' AS result;
ELSE
ROLLBACK;
SELECT '库存不足' AS result;
END IF;
-- 批量插入数据
START TRANSACTION;
INSERT INTO orders (id, amount) VALUES (1, 100);
INSERT INTO orders (id, amount) VALUES (2, 200);
INSERT INTO orders (id, amount) VALUES (3, 300);
COMMIT;
✅ 事务具有ACID特性 ✅ 隔离级别解决并发问题 ✅ 锁机制保证数据一致性 ✅ MVCC实现非阻塞读 ✅ 死锁需要预防和处理
完成本章学习后,请确认您能够: