MySQL事务使用教程:提交、回滚、隔离级别与并发问题解析

MySQL 事务是保证数据一致性的核心机制。转账扣款、订单创建、库存扣减、积分变更等业务,往往不是一条 SQL 就能完成。如果中间某一步失败,前面已经执行的写入必须能够回滚,否则就会出现扣了钱却没生成订单、减了库存却没记录流水的问题。

事务看起来只是 BEGINCOMMITROLLBACK 三个动作,但真正用好它,还需要理解隔离级别、并发问题和锁的影响。本文从事务基础讲起,整理 MySQL 中事务提交、回滚、隔离级别和常见并发问题。

事务是什么

事务是一组需要整体成功或整体失败的数据库操作。只要其中一步失败,就应该回滚到事务开始前的状态。

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

这类转账操作必须放在事务里。否则第一条更新成功、第二条更新失败时,数据就会不一致。

ACID 特性

事务通常用 ACID 描述:原子性、一致性、隔离性、持久性。原子性表示要么全部成功,要么全部失败;一致性表示事务前后数据满足约束;隔离性表示并发事务之间互不干扰;持久性表示提交后的数据不会丢失。

实际开发中,最常接触的是原子性和隔离性。一个负责回滚,一个负责处理并发读写。

MySQL事务使用教程配图:提交回滚隔离级别与并发问题
MySQL 事务不仅是提交和回滚,还包括隔离级别、锁和并发一致性控制。

开启事务

MySQL 可以使用 START TRANSACTIONBEGIN 开启事务。

START TRANSACTION;
-- 执行业务 SQL
COMMIT;

如果所有操作成功,就执行 COMMIT。提交后,事务中的修改会真正生效。

回滚事务

如果事务中某一步失败,应执行 ROLLBACK 回滚。

START TRANSACTION;
UPDATE orders SET status = 'paid' WHERE id = 1001;
UPDATE inventory SET stock = stock - 1 WHERE sku = 'A001';
ROLLBACK;

ROLLBACK 会撤销当前事务中尚未提交的修改。业务代码里通常会在捕获异常后执行回滚。

自动提交

MySQL 默认可能处于自动提交模式,即每条 SQL 单独作为一个事务执行。可以查看或设置 autocommit。

SELECT @@autocommit;
SET autocommit = 0;

实际项目中,更常见的是由框架或数据库连接层管理事务,而不是手动长期关闭自动提交。

InnoDB 才支持事务

MySQL 中常用的 InnoDB 引擎支持事务。老旧 MyISAM 引擎不支持事务。如果发现 ROLLBACK 没效果,要先检查表引擎。

SHOW TABLE STATUS LIKE 'orders';

生产项目通常优先使用 InnoDB。

隔离级别

隔离级别决定并发事务之间能看到什么数据。MySQL 常见隔离级别有:读未提交、读已提交、可重复读、串行化。

SELECT @@transaction_isolation;

隔离级别越高,并发问题越少,但可能带来更多锁等待和性能成本。

读未提交

读未提交允许一个事务读取另一个未提交事务的数据,会产生脏读。这个级别很少用于严肃业务。

比如事务 A 修改余额但未提交,事务 B 已经读到了这个余额。如果事务 A 最后回滚,事务 B 之前读到的就是无效数据。

读已提交

读已提交只能读取其他事务已经提交的数据,可以避免脏读。但同一个事务中两次读取同一条件,可能结果不同,这叫不可重复读。

很多数据库默认使用读已提交。它在一致性和并发性能之间相对平衡。

可重复读

MySQL InnoDB 默认隔离级别通常是可重复读。在同一个事务内,多次读取同一批数据,结果保持一致。

可重复读能减少不可重复读问题。InnoDB 还通过 MVCC 和锁机制处理很多幻读场景。

串行化

串行化隔离级别最高,会让事务像排队一样执行,能最大程度避免并发异常,但性能成本最高。一般只在极少数强一致场景使用。

脏读、不可重复读、幻读

脏读是读到了别人未提交的数据;不可重复读是同一事务中两次读取同一行结果不同;幻读是同一事务中两次按条件查询,出现了新增或消失的记录。

理解这些问题,有助于选择合适隔离级别,也能避免把所有并发异常都简单归因于“数据库不稳定”。

事务不要太长

事务开启后不要长时间不提交。长事务会占用连接、持有锁、影响其他写入,还可能导致 undo 日志积压。

事务里只放必要数据库操作,不要夹杂外部接口请求、文件上传、长时间计算等不确定耗时动作。

库存扣减示例

库存扣减要避免超卖。常见写法是在更新时带上库存条件。

UPDATE products
SET stock = stock - 1
WHERE id = 1001 AND stock > 0;

执行后检查受影响行数,如果为 0,说明库存不足。再结合事务处理订单创建,就能减少并发超卖风险。

死锁处理

并发事务可能出现死锁。MySQL 会自动回滚其中一个事务。应用层应该捕获死锁错误,并根据业务做有限次数重试。

减少死锁的常见做法包括:固定更新顺序、缩短事务时间、减少事务中扫描范围、确保条件能走索引。

常见错误

第一种错误是多步写入不加事务。第二种错误是事务中调用外部接口,导致事务时间过长。第三种错误是以为 ROLLBACK 能撤销已经提交的数据。第四种错误是不了解隔离级别,遇到并发问题只会加锁。第五种错误是没有处理死锁重试。

实践建议

使用 MySQL 事务时,先明确哪些操作必须整体成功,再把它们放进同一个事务。成功就提交,失败就回滚。事务尽量短,SQL 条件要能走索引,并根据业务选择隔离级别。

事务不是万能锁,但它是保证数据一致性的底线。把事务边界设计清楚,很多订单、支付、库存类问题都会少很多。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容