Rails执行db:schema:load报Mysql2外键约束错误该如何排查
该报错核心原因是create_table的force: :cascade参数会在创建表前先删除已存在的同名表,删除时其他表存在指向active_storage_blobs的外键约束,触发了MySQL的外键保护逻辑。你可以按以下步骤定位:
第一步:确认目标库现存表的依赖关系
直接登录目标MySQL数据库,执行SHOW TABLES;查看是否已存在active_storage相关表,尤其是active_storage_attachments,该表默认会创建关联active_storage_blobs.id的外键,只要该表存在,删除active_storage_blobs就会触发报错。
你也可以执行以下SQL直接查询所有依赖active_storage_blobs的外键关联:SELECT TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = '替换为你的数据库名' AND REFERENCED_TABLE_NAME = 'active_storage_blobs';第二步:校验schema.rb的表定义顺序
Rails的schema.rb按迁移执行的先后顺序生成表定义,如果active_storage_attachments的表定义在active_storage_blobs之前,执行schema加载时会先创建active_storage_attachments,后续执行到active_storage_blobs的force: :cascade逻辑时,就会因为已存在的外键关联删表失败。第三步:临时关闭外键校验验证问题
你可以临时关闭MySQL外键检查后重新执行schema加载,确认是否为依赖顺序问题:# 替换为你实际的运行环境,比如production/development rails db:environment:set RAILS_ENV=development # 替换为你实际的数据库用户名、数据库名,执行后输入密码 mysql -u root -p 你的数据库名 -e "SET FOREIGN_KEY_CHECKS=0;" rails db:schema:load # 执行完成后重新开启外键校验 mysql -u root -p 你的数据库名 -e "SET FOREIGN_KEY_CHECKS=1;"如果上述操作执行成功,即可确认是表删除/创建顺序的外键依赖问题,后续正式操作时直接清空目标库所有表再执行
rails db:schema:load即可正常运行。第四步:排查db:prepare无报错的原因
rails db:prepare的默认逻辑是:如果数据库不存在则创建,若数据库已存在且已经加载过schema,则执行未运行的迁移,仅未加载过schema时才会执行schema加载。你之前的操作大概率已经在库内生成了部分表,db:prepare未触发schema加载逻辑所以没有报错,手动执行rails db:schema:load时才触发了删旧表的逻辑导致报错。
内容的提问来源于stack exchange,提问作者tkhobbes

