{T}

事务

事务(Transaction)是将一组 SQL 操作作为一个整体执行的机制。事务确保这组操作要么全部成功,要么全部失败,是数据库保证数据一致性的核心功能。

为什么需要事务

考虑转账场景:

sql
-- 从账户 A 转账 100 元到账户 B
-- 第一步:A 账户减去 100 元
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

-- 第二步:B 账户加上 100 元
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

如果第一步成功但第二步失败,会导致资金丢失。事务可以确保两步操作作为一个整体,要么都成功,要么都失败。

ACID 特性

事务具有四个核心特性:

特性英文说明
原子性Atomicity事务中的操作要么全部执行,要么全部不执行
一致性Consistency事务完成后,数据状态保持一致
隔离性Isolation并发事务之间相互隔离,互不影响
持久性Durability事务提交后,修改永久保存

ACID 关系

  • 原子性是基础
  • 隔离性是手段
  • 一致性是约束
  • 持久性是目的

事务控制语句

基本语法

sql
-- 开启事务
BEGIN;
-- 或
START TRANSACTION;

-- 提交事务
COMMIT;

-- 回滚事务
ROLLBACK;

显式事务示例

sql
-- 转账事务
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

回滚事务

sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 发现问题,回滚
ROLLBACK;

保存点(SAVEPOINT)

sql
BEGIN;
INSERT INTO orders (id, amount) VALUES (1, 100);
SAVEPOINT order_created;

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

-- 回滚到保存点
ROLLBACK TO items_added;

-- 继续其他操作
INSERT INTO order_items (order_id, product_id) VALUES (1, 102);
COMMIT;

删除保存点

sql
RELEASE SAVEPOINT savepoint_name;

自动提交

MySQL 自动提交模式

sql
-- 查看自动提交状态
SELECT @@autocommit;

-- 关闭自动提交
SET autocommit = 0;

-- 开启自动提交
SET autocommit = 1;

completion_type 参数

sql
-- 查看当前设置
SELECT @@completion_type;

-- 设置 completion_type
SET @@completion_type = 1;
说明
0默认,COMMIT 后需要手动开启新事务
1COMMIT AND CHAIN,提交后自动开启新事务
2COMMIT AND RELEASE,提交后断开连接

自动提交与显式事务

sql
-- autocommit = 1 时
BEGIN;  -- 显式开启事务
INSERT INTO test VALUES ('数据');
COMMIT; -- 需要显式提交

-- autocommit = 0 时
INSERT INTO test VALUES ('数据');  -- 自动开启事务
COMMIT; -- 需要显式提交

隔离级别

四种隔离级别

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

设置隔离级别

sql
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置下一个事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 查看当前隔离级别
SELECT @@transaction_isolation;

READ UNCOMMITTED(读未提交)

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

sql
-- 事务 A
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
UPDATE students SET name = 'Bob' WHERE id = 1;
-- 未提交...

-- 事务 B
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT * FROM students WHERE id = 1;  -- 读到 'Bob'(脏读)

READ COMMITTED(读已提交)

只能读到已提交的数据,但同一事务内可能读到不同结果(不可重复读):

sql
-- 事务 A
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
UPDATE students SET name = 'Bob' WHERE id = 1;
COMMIT;

-- 事务 B
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT * FROM students WHERE id = 1;  -- 读到 'Alice'
-- 事务 A 提交后
SELECT * FROM students WHERE id = 1;  -- 读到 'Bob'(不可重复读)

REPEATABLE READ(可重复读)

同一事务内多次读取结果一致,但可能出现幻读:

sql
-- 事务 A
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
INSERT INTO students (id, name) VALUES (99, 'Bob');
COMMIT;

-- 事务 B
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT * FROM students WHERE id = 99;  -- 空
-- 事务 A 提交后
SELECT * FROM students WHERE id = 99;  -- 仍然空
UPDATE students SET name = 'Alice' WHERE id = 99;  -- 更新成功!
SELECT * FROM students WHERE id = 99;  -- 出现了!(幻读)

SERIALIZABLE(串行化)

最高隔离级别,事务串行执行,避免所有并发问题:

sql
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT * FROM students WHERE id = 1;
-- 其他事务无法修改 id=1 的记录,直到此事务提交
COMMIT;

MySQL 默认隔离级别

MySQL InnoDB 默认使用 REPEATABLE READ。

隔离级别选择

场景推荐级别原因
高并发读取READ COMMITTED性能好,避免脏读
需要一致性读取REPEATABLE READ默认级别,适合大多数场景
极高数据一致性SERIALIZABLE性能差,谨慎使用
统计分析(允许脏读)READ UNCOMMITTED性能最好

事务最佳实践

1. 保持事务简短

sql
-- 好:事务简短
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

-- 差:事务过长
BEGIN;
-- 大量操作...
SELECT ...;
UPDATE ...;
DELETE ...;
-- 业务逻辑处理...
COMMIT;

2. 避免在事务中处理耗时操作

sql
-- 差:事务中包含网络请求
BEGIN;
UPDATE orders SET status = 'processing';
-- 发送邮件(耗时操作)
CALL send_email(...);
COMMIT;

-- 好:先完成数据库操作
BEGIN;
UPDATE orders SET status = 'processing';
COMMIT;
-- 事务外发送邮件
CALL send_email(...);

3. 正确处理错误

sql
-- 使用存储过程处理错误
DELIMITER //
CREATE PROCEDURE transfer_funds(
    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;
    UPDATE accounts SET balance = balance - amount WHERE id = from_id;
    UPDATE accounts SET balance = balance + amount WHERE id = to_id;
    COMMIT;
    SELECT '转账成功' AS result;
END //
DELIMITER ;

4. 使用合适的隔离级别

sql
-- 报表查询:使用较低隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT * FROM large_table WHERE ...;
COMMIT;

-- 关键业务:使用默认或更高隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- 关键操作
COMMIT;

总结

事务控制语句

语句说明
BEGIN / START TRANSACTION开启事务
COMMIT提交事务
ROLLBACK回滚事务
SAVEPOINT创建保存点
ROLLBACK TO回滚到保存点

隔离级别对比

级别脏读不可重复读幻读性能
READ UNCOMMITTED可能可能可能最高
READ COMMITTED不会可能可能
REPEATABLE READ不会不会可能
SERIALIZABLE不会不会不会最低

最佳实践

  1. 保持事务简短:减少锁定时间
  2. 选择合适隔离级别:平衡一致性和性能
  3. 正确处理错误:使用异常处理机制
  4. 避免长事务:防止资源长时间锁定