MySQL 8.x并行执行TRUNCATE是否会导致INSERT外键约束报错?
问题判定
该问题不属于配置遗漏,是MySQL 8.0版本存在的并发DDL引发外键元数据损坏的已知Bug。
- 已在MySQL 8.0.29及多个8.x中间版本稳定复现,相同操作逻辑在MySQL 5.7版本下运行无异常
- 异常触发特征:被关联的父表中已存在外键要求的合法ID记录,但MySQL持续抛出外键约束校验失败错误,即使新开会话连接数据库执行插入操作仍无法成功,异常状态会持续留存。
复现流程
基础复现步骤:
- 创建父表A
- 创建子表B,为子表B配置指向表A主键
id字段的外键约束 - 在会话级别关闭
FOREIGN_KEY_CHECKS参数后,并行执行TRUNCATE表A、TRUNCATE表B操作 - 向表A插入id为1的记录
- 向表B插入关联字段值为1的记录
该报错为概率触发,重复执行上述流程100次左右必然触发异常。
稳定复现脚本
执行以下bash脚本即可触发数据库异常状态:
#!/bin/bash DB_USER='root'; DB_PASSWD='root'; DB_NAME='mysql_8_bug'; TABLE_A='a'; TABLE_B='b'; set -m # 初始化环境 mysql --user=$DB_USER --password=$DB_PASSWD << EOF CREATE DATABASE IF NOT EXISTS $DB_NAME DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; EOF mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME << EOF DROP TABLE IF EXISTS $TABLE_A, $TABLE_B; EOF mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME << EOF CREATE TABLE $TABLE_A (id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY); EOF mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME << EOF CREATE TABLE $TABLE_B (id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, int_a_id BIGINT(20) UNSIGNED DEFAULT NULL, CONSTRAINT fk_a_id FOREIGN KEY (int_a_id) REFERENCES a (id)); EOF # 初始化结束 for i in {0..100} do # 并行执行TRUNCATE操作 mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME --execute="SET @@session.FOREIGN_KEY_CHECKS = 0; TRUNCATE TABLE $TABLE_A; SET @@session.FOREIGN_KEY_CHECKS = 1;" & mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME --execute="SET @@session.FOREIGN_KEY_CHECKS = 0; TRUNCATE TABLE $TABLE_B; SET @@session.FOREIGN_KEY_CHECKS = 1;" & wait mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME --execute="INSERT INTO $TABLE_A (id) VALUES (1);" mysql --user=$DB_USER --password=$DB_PASSWD $DB_NAME --execute="INSERT INTO $TABLE_B (int_a_id) VALUES (1);" if [ $? -gt 0 ] then echo "触发异常!此时表A中已存在id=1的记录,但表B插入关联数据持续报错,数据库进入异常状态。" exit 0 fi done
根因与规避方案
- 根因:MySQL 8.0重构了数据字典与元数据锁(MDL)实现逻辑,关闭会话级外键检查时并行TRUNCATE存在外键关联的表,会触发竞态条件,导致InnoDB存储的外键元数据与实际表结构不一致,外键校验逻辑错误判定父表无对应合法记录。
- 规避方案:存在外键关联的表执行TRUNCATE操作时,不要关闭外键检查后并行执行,需按照「先清空子表、后清空父表」的顺序串行操作。
- 异常修复:如果已经触发该问题,执行
ALTER TABLE 异常子表名 FORCE;重建表即可修复损坏的外键元数据,恢复正常写入。
内容的提问来源于stack exchange,提问作者Dimitry
相关产品推荐
相关产品推荐

