MySQL 的 CREATE DATABASE 不支持事务,执行后立即生效且无法回滚;替代方案是先检查库是否存在,再建库并捕获异常,后续建表、插入等操作才可放入事务。

PHP 创建数据库时不能用事务
MySQL 的 CREATE DATABASE 是 DDL 语句,不支持事务控制。无论用 mysqli、PDO 还是原生 SQL,在事务中执行建库操作,ROLLBACK 都不会撤销它——建库会立即生效,且无法回滚。
常见错误现象:
– 在 beginTransaction() 后调用 exec("CREATE DATABASE test_db"),再 rollback(),发现库依然存在
– 误以为事务能包裹“建库 + 建表 + 插数据”全流程,结果建库成功但后续失败,留下空库
- DDL(如
CREATE DATABASE、DROP TABLE)在 MySQL 中会隐式提交当前事务 - 即使你用
PDO::ATTR_AUTOCOMMIT => false,也拦不住 DDL 的自动提交行为 - PostgreSQL 等少数数据库允许事务内建 schema,但 MySQL 不行——这是引擎层限制,不是 PHP 写法问题
替代方案:建库前加存在性检查 + 异常捕获
既然不能靠事务兜底,就得靠逻辑控制和防御性操作。核心思路是:先查库是否存在,不存在再建;建库失败就抛异常,避免继续执行下游逻辑。
$pdo = new PDO('mysql:host=localhost', $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
try {
// 检查库是否存在
$stmt = $pdo->query("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'myapp_db'");
if ($stmt->fetch() === false) {
// 不存在才建库(注意:这里不进事务)
$pdo->exec("CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
}
// 切换到新库(必须显式切换)
$pdo->exec("USE myapp_db");
// ✅ 此处开始的建表、插数据等 DML/DDL 才可进事务
$pdo->beginTransaction();
$pdo->exec("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50))");
$pdo->exec("INSERT INTO users VALUES (1, 'Alice')");
$pdo->commit();
} catch (PDOException $e) {
// 建库失败或建表失败都会进这里,但建库本身无法回滚
error_log("DB setup failed: " . $e->getMessage());
// 可选:手动清理(如 DROP DATABASE myapp_db),但需确认权限和安全性
}
PDO 事务嵌套的常见误解
PHP 的 PDO 本身不支持真正的嵌套事务(savepoint 是模拟,非标准嵌套)。所谓“嵌套”,其实是用 SAVEPOINT 实现局部回滚,但前提是所有操作都在同一数据库连接、且底层支持(MySQL 支持 SAVEPOINT)。
立即学习“PHP免费学习笔记(深入)”;
-
beginTransaction()只能调用一次,重复调用会报错SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active - 想实现“建表失败不影响建库”,只能拆成两个独立流程:建库(无事务)→ 成功后,再连新库做带事务的初始化
- 如果坚持单连接操作,可用
SAVEPOINT手动管理,例如:$pdo->exec("SAVEPOINT sp1");,失败时$pdo->exec("ROLLBACK TO sp1");,但这对CREATE DATABASE无效
真正需要事务保护的操作有哪些
建库不行,但这些操作可以且应该放进事务:
-
CREATE TABLE/DROP TABLE(同库内 DDL,MySQL 5.6+ 支持事务内执行,但仍有风险,建议只用于初始化阶段) -
INSERT/UPDATE/DELETE多表关联写入(如订单+订单项+库存扣减) - 批量导入数据时,避免部分成功导致状态不一致
- 使用
PDO::prepare()+execute()绑定参数的写操作,比裸exec()更安全
复杂点在于:建库是部署期操作,事务是运行期保障。混在一起容易高估事务能力,低估 DDL 的破坏性。实际项目中,建库通常由 DBA 或部署脚本完成,PHP 应专注连接已有库后的业务事务。











