ThinkPHP用Db::transaction闭包方式保证数据一致性,自动开启、提交或回滚;事务内操作强制走主库,需InnoDB引擎,配合约束、校验与锁机制提升可靠性。

ThinkPHP 用事务保证数据一致,核心是让一组数据库操作“全成功或全失败”,避免中间出错导致脏数据。它不靠猜、不靠手动拼 SQL,而是通过框架封装好的机制自动管理开启、提交和回滚。
用 Db::transaction 最省心
这是官方最推荐的方式,代码简洁、异常自动回滚、不易漏写逻辑:
- 把所有要原子执行的操作放进闭包里,框架自动处理 try-catch 和 rollback
- 哪怕中间某条 update 失败、某个 insert 报唯一键冲突,整个事务都会撤回
- 不用手动调 Db::startTrans()、Db::commit() 或 Db::rollback()
示例:
Db::transaction(function () {Db::name('order')->insert(['sn' => 'ORD20261027001', 'status' => 'created']);
Db::name('stock')->where('goods_id', 1001)->dec('quantity', 5);
Db::name('log')->insert(['action' => 'create_order', 'time' => time()]);
});
读写分离下事务自动走主库
如果你开了主从配置(deploy => 1 且 rw_separate => true),事务内所有操作——包括 find()、select()、count()——都会强制走主库,不会发到从库:
- 即使你写了
->slave(),在事务中也无效 - 这样能避免“刚写完订单,立刻查却查不到”的主从延迟问题
- 但注意:这只能解决事务内的读一致性;事务外的“写后即读”仍需业务层兜底(比如注册后查用户,必须单独再走一次主库)
手动事务要盯紧异常和回滚
如果用 Db::startTrans() 开启,就必须自己管好全流程:
- try 块里执行操作,成功后调 Db::commit()
- catch 中必须调 Db::rollback(),否则连接可能卡在事务状态,后续查询出错
- 别忘了设 PDO 错误模式为异常(
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)),否则 SQL 错误可能静默忽略 - 事务范围尽量小,比如转账只包两条 update,别把发短信、写日志塞进去
事务不是万能的,还得配合其他手段
单靠事务不能覆盖所有一致性场景:
- 跨库、跨服务的操作(如支付+库存+物流)需用消息队列 + 补偿事务,TP6 本身不提供分布式事务支持
- 高并发下的超卖问题,光有事务不够,得加
SELECT ... FOR UPDATE或乐观锁 - 表引擎必须是 InnoDB;MyISAM 不支持事务,开了也白开
- 配合数据库约束(唯一索引、外键)、应用层校验(余额是否充足)、幂等设计(重复提交不重复扣款)效果更稳
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











