1. MySQL事务基础概念
事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。在MySQL中,事务具有四个关键特性,通常被称为ACID特性:
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性(Consistency):事务应确保数据库从一个一致状态转变为另一个一致状态
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
MySQL默认的存储引擎InnoDB完全支持ACID特性,而MyISAM等其他引擎则不支持事务。
重要提示:在MySQL 5.5版本之前,默认存储引擎是MyISAM,之后改为InnoDB。如果你需要事务支持,务必确认表使用的是InnoDB引擎。
2. MySQL事务操作语法
2.1 基本事务控制语句
MySQL中使用以下语句控制事务:
START TRANSACTION; -- 或 BEGIN -- 执行SQL语句 COMMIT; -- 提交事务 ROLLBACK; -- 回滚事务示例:
START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; COMMIT;2.2 设置自动提交
MySQL默认是自动提交模式(autocommit=1),每条SQL语句都会自动提交。可以修改此行为:
SET autocommit = 0; -- 关闭自动提交 -- 现在需要显式使用COMMIT提交事务 SET autocommit = 1; -- 重新开启自动提交3. 事务隔离级别
MySQL支持四种事务隔离级别,用于控制事务之间的可见性:
- READ UNCOMMITTED(读未提交):最低隔离级别,允许读取未提交的数据变更
- READ COMMITTED(读已提交):只能读取已提交的数据
- REPEATABLE READ(可重复读):MySQL默认级别,确保同一事务中多次读取同样数据结果一致
- SERIALIZABLE(串行化):最高隔离级别,完全串行执行
查看和设置隔离级别:
-- 查看当前隔离级别 SELECT @@transaction_isolation; -- 设置会话级隔离级别 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置全局级隔离级别 SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;4. 事务中的锁机制
4.1 锁类型
MySQL InnoDB支持多种锁:
- 共享锁(S锁):允许事务读取一行数据
- 排他锁(X锁):允许事务删除或更新一行数据
- 意向锁:表级锁,表明事务稍后会对表中的行施加哪种锁
- 记录锁:锁定索引中的记录
- 间隙锁:锁定索引记录之间的间隙
- 临键锁:记录锁和间隙锁的组合
4.2 显式加锁
-- 加共享锁 SELECT * FROM table WHERE id = 1 LOCK IN SHARE MODE; -- 加排他锁 SELECT * FROM table WHERE id = 1 FOR UPDATE;5. 事务实践中的常见问题
5.1 死锁处理
死锁是指两个或多个事务互相持有对方需要的锁,导致都无法继续执行。MySQL会自动检测死锁并回滚其中一个事务。
减少死锁的策略:
- 按固定顺序访问表和行
- 减少事务持有锁的时间
- 使用较低的隔离级别
- 添加合理的索引
5.2 长事务问题
长时间运行的事务会带来多种问题:
- 占用锁资源,影响并发性能
- 可能导致大量undo日志堆积
- 增加死锁概率
监控长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;6. 事务性能优化
6.1 事务设计原则
- 尽量缩短事务执行时间
- 避免在事务中进行网络I/O操作
- 合理设置隔离级别
- 批量操作使用批量提交
6.2 事务拆分技巧
将大事务拆分为小事务:
-- 不推荐 START TRANSACTION; -- 执行大量操作 COMMIT; -- 推荐方式 for (batch in batches) { START TRANSACTION; -- 处理一批数据 COMMIT; }7. 事务与存储过程
在存储过程中使用事务:
DELIMITER // CREATE PROCEDURE transfer_funds( IN from_account INT, IN to_account INT, IN amount DECIMAL(10,2) ) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; START TRANSACTION; UPDATE accounts SET balance = balance - amount WHERE id = from_account; UPDATE accounts SET balance = balance + amount WHERE id = to_account; COMMIT; END // DELIMITER ;8. 事务监控与诊断
8.1 查看当前运行的事务
SELECT * FROM information_schema.INNODB_TRX;8.2 查看锁等待情况
SELECT * FROM performance_schema.events_waits_current WHERE EVENT_NAME LIKE '%lock%';8.3 分析事务日志
SHOW ENGINE INNODB STATUS;9. 分布式事务
MySQL支持XA协议实现分布式事务:
-- 协调者 XA START 'transaction_id'; -- 参与者1 XA START 'transaction_id'; -- 参与者2 XA START 'transaction_id'; -- 执行操作... -- 准备阶段 XA END 'transaction_id'; XA PREPARE 'transaction_id'; -- 提交或回滚 XA COMMIT 'transaction_id'; -- 或 XA ROLLBACK 'transaction_id';10. 事务最佳实践
- 合理设置事务隔离级别,通常REPEATABLE READ已足够
- 避免在事务中进行耗时操作
- 设计良好的索引减少锁冲突
- 监控和优化长事务
- 考虑使用乐观锁替代悲观锁
- 批量操作时考虑分批提交
在实际项目中,我曾经遇到一个典型的转账场景问题:在高并发情况下,简单的转账事务可能导致死锁。通过分析发现,问题出在不同事务中对账户记录的操作顺序不一致。解决方案是强制所有事务按照账户ID升序处理,这样避免了循环等待的情况。