mysql中使用事务与外键保证数据一致性

7次阅读

mysql事务仅InnoDB引擎支持,MyISAM不支持;外键需配合事务才能保证数据一致性;DDL会隐式提交事务;SET FOREIGN_KEY_CHECKS=0危险且不跨会话。

mysql中使用事务与外键保证数据一致性

MySQL 事务必须用 InnoDB 引擎才生效

MyISAM 不支持事务,哪怕写了 BEGINSTART TRANSACTION,提交或回滚都无效。检查表引擎用:

SHOW CREATE table `order`;

如果看到 ENGINE=MyISAM,就得改:

ALTER TABLE `order` ENGINE = InnoDB;

注意:改引擎会锁表,大表慎操作;同时确保 MySQL 配置没禁用 InnoDB(skip-innodb 已废弃,但旧配置可能残留)。

外键约束依赖事务才能真正防脏写

外键本身只拦住显式违反参照的动作(比如插入不存在的 user_id),但若没包在事务里,多步操作仍可能中间态不一致。例如先删用户、再删其订单,中间若崩了,就剩孤儿订单。正确做法是:

BEGIN; delete FROM `user` WHERE id = 123; DELETE FROM `order` WHERE user_id = 123; COMMIT;

此时外键 + 事务共同保证“全成功或全失败”。另外,外键定义时要明确 ON DELETE CAScadEON DELETE restrict,否则默认是 RESTRICT,删父记录会直接报错 Cannot delete or update a parent row

事务中执行 DDL(如 ALTER)会隐式提交

在事务里执行 ALTER TABLEDROP INDEX 等 DDL 语句,MySQL 会自动触发 COMMIT,导致前面所有 DML 不可回滚。常见踩坑场景:

BEGIN; INSERT INTO `log` VALUES ('start'); ALTER TABLE `order` ADD COLUMN status TINYINT DEFAULT 0; INSERT INTO `log` VALUES ('done'); -- 这条即使后面 ROLLBACK,前面 INSERT 也已提交 ROLLBACK;

结果 log 表里仍有 ‘start’。解决办法:DDL 单独执行,或改用支持事务 DDL 的 MySQL 8.0+(但仅限部分操作,且需确认 innodb_ddl_log_enabled=ON)。

SET FOREIGN_KEY_CHECKS=0 是危险开关

临时关外键检查(SET FOREIGN_KEY_CHECKS=0)常用于导入数据或修复,但它绕过所有外键约束,事务也无法挽救。一旦关了又忘了开,后续所有写入都可能造出脏数据。更隐蔽的问题是:它**不跨会话**,所以脚本里设了却没重连,新连接仍是开启状态;但若在长连接中设为 0 后异常退出,下次复用该连接时仍为 0。建议:

  • 只在必要时临时关闭,且紧跟着 SET FOREIGN_KEY_CHECKS=1
  • 避免在应用层代码里动态开关,优先用 DISABLE KEYS 配合 LOAD DATA INFILE
  • 监控 SQL 日志,搜 FOREIGN_KEY_CHECKS=0 出现频次

text=ZqhQzanResources