事务解决数据一致性问题:不满足原子性的操作会导致数据库状态不一致,如转账中扣款成功但入账失败,造成资金丢失。

事务解决什么问题:不满足原子性的操作会怎样
MySQL 用事务,核心就为保证「要么全成功,要么全失败」。没有事务时,比如转账操作:UPDATE accounts SET balance = balance - 100 WHERE id = 1 执行了,但紧接着 UPDATE accounts SET balance = balance + 100 WHERE id = 2 因网络中断或服务器宕机没执行——钱就凭空消失了。
这类跨行、跨表、需逻辑一致的修改,一旦中间出错又没回滚机制,数据库状态就会不一致。事务通过 BEGIN / COMMIT / ROLLBACK 把多个语句打包成一个不可分割的单元。
哪些业务场景必须开事务
不是所有 UPDATE 或 INSERT 都需要事务,但以下场景几乎必须加:
- 银行类资金操作:扣款+入账、充值+冻结、支付+库存扣减
- 订单创建全流程:生成订单记录 + 扣减商品库存 + 写入物流单号 + 更新用户积分
- 多表状态同步:用户注销时,要删
users、清user_sessions、归档user_logs,缺一不可 - 幂等性兜底:接口重复提交时,靠事务+唯一约束(如
UNIQUE KEY (order_id, event_type))确保只生效一次
注意:单纯查数据(SELECT)、单行单表的简单更新(如 UPDATE config SET value = 'on' WHERE key = 'feature_x'),一般不用事务。
autocommit 关掉还是开着:开发中容易踩的坑
MySQL 默认是 autocommit = 1,即每条 SQL 自动提交。这看似省事,但会导致事务失效:
比如你写了 BEGIN,然后执行两条 UPDATE,最后 COMMIT —— 如果中间某条语句报错(如违反外键约束),MySQL 不会自动回滚前面已执行的语句,除非你显式捕获错误并调用 ROLLBACK。
更危险的是:应用层连接池复用连接时,如果上一个请求开了事务但没 COMMIT 或 ROLLBACK,下一个请求可能在同一个事务上下文中执行,导致意外的数据锁定或脏读。
发卡宝是一个专业的软件卡密等虚拟商品在线交易平台,拥有多种兑换方式,费率低,结算快,正规企业平台一直稳定运营,24小时不间断提供自动发卡服务。【模板说明】试用版自带一套模板(响应式)【环境支持】PHP环境 / 200M或以上空间大小 / 开启父路径 / 设置index.php为默认首页 / 目录写入权限需要开启【数据库】MySQL【安装步骤】将文件上传至空间目录,运行“http://域名/inst
建议做法:
- 明确需要事务的逻辑,用
BEGIN显式开启 - 应用代码里统一用 try-catch 包裹,出错必
ROLLBACK,成功必COMMIT - 避免依赖
autocommit = 0全局设置,它会让每个连接长期处于未提交状态,极易引发锁等待和连接堆积
隔离级别怎么选:READ COMMITTED 够用,别盲目上 SERIALIZABLE
MySQL 默认隔离级别是 REPEATABLE READ,但它在高并发更新同一行时容易触发间隙锁(gap lock),导致不必要的锁冲突。实际业务中,多数场景用 READ COMMITTED 更轻量、更可控。
比如电商下单减库存:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; SELECT stock FROM products WHERE id = 123 FOR UPDATE; -- 检查 stock > 0 后再 UPDATE UPDATE products SET stock = stock - 1 WHERE id = 123; COMMIT;
这个流程在 READ COMMITTED 下只锁住命中行,不会锁间隙;而 REPEATABLE READ 可能连 id = 124 的插入都拦住,影响并发吞吐。
SERIALIZABLE 几乎不用——它会让所有 SELECT 都加读锁,相当于把并发变成串行,响应延迟直接翻倍。
真正要注意的不是“选哪个最高级”,而是理解每个级别对锁行为和一致性的影响。线上出过问题的,往往是默认 REPEATABLE READ 下没意识到 gap lock 的存在,又没做充分压测。









