事务未生效需先确认使用InnoDB引擎;C++中用MySQL Connector/C++应调用setAutoCommit(false)开启事务,执行SQL后commit()提交或rollback()回滚,并确保每个ResultSet读取完毕或关闭,且连接不跨线程共享。

事务没生效?先确认你用的是支持事务的存储引擎
MySQL 默认的 MyISAM 引擎根本不支持事务,COMMIT 和 ROLLBACK 会静默失败——你代码写得再标准也没用。必须用 InnoDB 或 MEMORY(后者不持久,慎用)。建表时显式指定:
CREATE TABLE account (id INT, balance DECIMAL) ENGINE=InnoDB;
如果已有表,用 ALTER TABLE account ENGINE=InnoDB; 转换。PostgreSQL 和 SQLite 默认都支持事务,但 SQLite 的 WAL 模式下并发行为有差异,后面会提。
C++里怎么开启、提交和回滚事务?以 MySQL Connector/C++ 为例
别直接拼接 START TRANSACTION 字符串执行——这容易被连接池或自动重连机制打断。要用驱动提供的事务控制接口:
-
sql::Connection::setAutoCommit(false)是起点,关闭自动提交后所有语句都在同一事务上下文中 - 执行完 SQL(如
stmt->executeUpdate("UPDATE account SET balance = balance - 100 WHERE id = 1"))后,调用conn->commit()提交 - 出错时必须显式调用
conn->rollback(),否则连接可能卡在未提交状态,后续操作被阻塞 - 记得恢复
conn->setAutoCommit(true),否则下次获取连接时仍处于手动事务模式,极易引发诡异问题
事务里执行多条语句,为什么第二条就报错“Commands out of sync”?
这是 MySQL C++ 驱动常见陷阱:执行 SELECT 后没取完结果集,就紧接着执行 UPDATE,底层 socket 缓冲区混乱。解决方法只有两个:
- 对每个
sql::Statement或sql::PreparedStatement,执行查询后必须调用res->next()直到返回false,或直接用res->close() - 更稳妥的做法是:一个事务内,每个
executeQuery()配套一个独立的sql::ResultSet对象,并在其作用域结束前完成读取或关闭 - SQLite 的
sqlite3_exec()没这个问题,但它的BEGIN IMMEDIATE会立即加锁,高并发下容易死锁
跨线程共享 connection 对象做事务安全吗?
完全不安全。C++ 数据库连接对象(如 sql::Connection*)不是线程安全的,即使加了互斥锁,事务状态(autocommit 开关、当前事务 ID)也只属于该连接实例。常见错误是:
立即学习“C++免费学习笔记(深入)”;
- 线程 A 调用
setAutoCommit(false),线程 B 同时调用executeUpdate()—— B 的语句可能意外进入 A 的事务,也可能被自动提交 - 连接池返回的连接默认是 autocommit=true,你不能假设它“干净”,每次使用前必须显式设置
- 真正安全的做法:每个线程独占一个连接,事务生命周期严格绑定在线程内;或者用 RAII 封装,构造时
setAutoCommit(false),析构时按需commit()或rollback()
事务的边界比看起来更脆弱——它依赖连接状态、驱动实现、甚至网络中间件是否透传 COMMIT 包。别省略任何一次 rollback(),也别信任连接池自动清理事务状态。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/xitongjiaocheng/126944.html