事务未生效的主因是数据库引擎不支持(如MyISAM)或DB实例被重建导致事务上下文丢失;需确认引擎为InnoDB、禁用自动事务后手动控制、统一使用同一DB实例操作并检查trans_status()判断结果。

事务没生效?检查是否在支持事务的数据库引擎上
CodeIgniter 的 $this->db->trans_start() 和 $this->db->trans_complete() 只是包装逻辑,底层依赖数据库本身是否支持事务。MySQL 下如果用 MyISAM 引擎,事务调用不会报错,但 ROLLBACK 完全无效——所有写操作照常提交。
- 确认表引擎:执行
SHOW CREATE TABLE your_table;,确保显示ENGINE=InnoDB - CI 连接配置里不能强制指定
stricton或emulate_prepare导致事务被绕过(尤其 PHP 8.1+ + MySQLi) - SQLite 默认支持事务,但注意它不支持嵌套事务;PostgreSQL 则对
SAVEPOINT更敏感,CI 原生不封装 savepoint,需手写$this->db->query('SAVEPOINT sp1')
手动 commit/rollback 怎么写才可靠
CI 的自动事务模式(trans_start() + trans_complete())只在 trans_complete() 被调用时统一判断并提交或回滚。但如果你中间需要提前控制,就得绕过自动流程:
- 必须先关掉自动管理:
$this->db->trans_off(); - 再显式开启:
$this->db->trans_begin(); - 成功后调用
$this->db->trans_commit();,失败则$this->db->trans_rollback(); - 注意:一旦调用了
trans_off(),后续所有查询都不再受事务包裹,哪怕再调trans_start()也无效,必须重启连接或重实例化 DB 对象
事务里调用模型方法容易丢事务上下文
常见错误是把多个 $this->some_model->update() 堆在事务块里,结果发现部分更新没回滚。原因是:
- 模型内部若重新获取了 DB 实例(比如用
$this->load->database()新建连接),就脱离了当前事务连接 - CI 的事务状态是绑定在单个 DB 实例上的,不是全局的
- 模型方法里如果用了
query()但没显式传入当前事务连接句柄,也会断开
解决办法只有两个:
- 所有模型方法都接受一个可选的
$db参数,并统一用传入的实例执行查询 - 或者干脆别在事务里调模型,把 SQL 操作收拢到控制器或专门的 service 类里,用同一个
$this->db实例操作
调试事务失败时,看哪里的日志最直接
CI 自身不记录事务级日志,出问题只能靠组合排查:
- 开启数据库日志:
$this->db->save_queries = TRUE;,然后在trans_complete()后立刻 dump$this->db->queries,确认事务相关语句(BEGIN/COMMIT/ROLLBACK)是否发出 - 查 MySQL 的 general_log:临时打开
SET GLOBAL general_log = 'ON';,看实际发到数据库的指令序列 - 注意 CI 在检测到任何查询失败(比如主键冲突、字段超长)时会自动标记事务失败,但未必抛异常——要主动检查
$this->db->trans_status()返回值,它才是最终判决依据
事务真正难的不是语法,是连接生命周期和模型解耦。很多人卡在“看着代码没问题,但数据就是没回滚”,八成是 DB 实例被无意中重建了。










