表引擎须为innodb,否则事务回滚无效;insertall需try/catch捕获异常以触发回滚;大批量数据应分片并用db::transaction闭包控制每片事务,严禁手动commit/rollback。

确认表引擎是否为 InnoDB
事务回滚失败,90% 的情况是表用的 MyISAM 引擎——它根本不支持事务,Db::rollback() 调了也白调,还不报错。执行 SHOW CREATE TABLE user;,检查输出里是否有 ENGINE=InnoDB。如果是 ENGINE=MyISAM,立刻执行:ALTER TABLE `user` ENGINE=InnoDB;。建表时务必显式声明 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,光靠配置文件设默认引擎不可靠,尤其在手动建表或用某些迁移工具时容易被忽略。
insertAll 在事务中必须显式捕获异常
insertAll 返回的是影响行数(int),不是布尔值;部分失败时 PDO 可能只返回成功条数、不抛异常(取决于 MySQL 的 sql_mode 和 strict mode)。这意味着:不 catch,事务就“假装成功”继续往下走,Db::rollback() 根本不会触发。
- 错误写法:
Db::insertAll($data);后直接Db::commit(),没 try/catch - 正确写法:必须包裹在
try中,且捕获\Throwable(PHP 8.0+ 要求,否则Fatal Error类异常会绕过回滚) - 注意:
insertAll遇到主键/唯一索引冲突会直接抛think\db\exception\DataNotFoundException,不是静默跳过
批量分片时每批次都要独立事务控制
处理 180 万数据时,不能把整个循环塞进一个事务——内存爆、锁表久、超时风险高。但也不能每个 insertAll 单独 commit,否则失败无法整体回滚。折中做法是分片 + 每片独立事务 + 外层逻辑兜底:
- 单次
insertAll数据量建议 ≤ 500 条,避免 PDO 参数绑定超限或 MySQLmax_allowed_packet报错 - 每片用
Db::transaction()闭包方式,天然保证该片内失败自动 rollback - 外层用计数器或状态数组记录各片结果,某片失败可选择终止后续或标记跳过(根据业务容忍度)
- 不要在分片循环里混用
Db::startTrans()+ 手动commit/rollback,易因中断导致连接残留或状态错乱
闭包事务中别自己调 commit/rollback
Db::transaction(function () { ... }); 的机制是:闭包正常结束 → 自动 commit;闭包抛出未捕获异常 → 自动 rollback。如果在闭包里手动写 Db::commit() 或 Db::rollback(),会干扰框架内部事务状态,可能导致后续操作误判、连接未释放,甚至出现 “Cannot execute queries while other unbuffered queries are active” 类错误。
另外,闭包外的代码(比如 Db::transaction() 调用之后的那行日志)完全不受事务保护——哪怕闭包成功 commit 了,外层再抛错,也不会触发回滚。这点常被忽略,尤其是日志、缓存更新、消息推送等伴随操作写在事务块外面时。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











