在之前的MySQL运维中,要求禁用触发器/存储过程/外键等一些数据库常见功能,因此对MySQL外键也相对比较陌生,今天特地探究下。
现有表TB001和TB002各包含6291456行数据,创建脚本如下:
CREATE TABLE `tb001` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `C1` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=6684558 DEFAULT CHARSET=utf8; CREATE TABLE `tb002` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `C1` int(11) DEFAULT NULL, `C2` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=6356897 DEFAULT CHARSET=utf8;
现在在TB002上的C1列增加一个外键指向TB001的主键,有如下三种创建方式:
方式1:直接创建外键
查询:ALTER TABLE TB002 ADD CONSTRAINT `FK_C1` FOREIGN KEY (`C1`) REFERENCES `TB001` (`ID`) 共 6291456 行受到影响 执行耗时 : 55.889 sec 传送时间 : 1.003 sec 总耗时 : 56.892 sec
方式2:创建索引+创建外键
查询:alter table TB002 ADD INDEX FK_C1(C1) 共 0 行受到影响 执行耗时 : 9.676 sec 传送时间 : 1.001 sec 总耗时 : 10.678 sec -------------------------------------------------- 查询:ALTER TABLE TB002 ADD CONSTRAINT `FK_C1` FOREIGN KEY (`C1`) REFERENCES `TB001` (`ID`) 共 6291458 行受到影响 执行耗时 : 1 min 9 sec 传送时间 : 1.001 sec 总耗时 : 1 min 10 sec
方式3:创建索引+创建外键+FOREIGN_KEY_CHECKS
查询:alter table TB002 ADD INDEX FK_C1(C1) 共 0 行受到影响 执行耗时 : 9.393 sec 传送时间 : 1.002 sec 总耗时 : 10.396 sec -------------------------------------------------- 查询:SET @@foreign_key_checks=0 共 0 行受到影响 执行耗时 : 0 sec 传送时间 : 0 sec 总耗时 : 0 sec -------------------------------------------------- 查询:ALTER TABLE TB002 ADD CONSTRAINT `FK_C1` FOREIGN KEY (`C1`) REFERENCES `TB001` (`ID`) 共 0 行受到影响 执行耗时 : 0.002 sec 传送时间 : 0.001 sec 总耗时 : 0.004 sec -------------------------------------------------- 查询:SET @@foreign_key_checks=1 共 0 行受到影响 执行耗时 : 0 sec 传送时间 : 0 sec 总耗时 : 0 sec
如未设置FOREIGN_KEY_CHECKS=0,在创建外键过程中,会阻塞其他会话对表TB002的增删改操作(删除操作也不允许)。
从上面三种方式执行结果可以发现,创建外键最快的方式是:创建索引+创建外键+FOREIGN_KEY_CHECKS,在设置FOREIGN_KEY_CHECKS=0时,创建外键不会检查当前表中数据,属于元数据操作,因此很快完成,对其他会话影响较小,但无法保证表中数据满足外键约束。
FOREIGN_KEY_CHECKS相关
FOREIGN_KEY_CHECKS在官方文档解释:
If set to 1 (the default), foreign key constraints for InnoDB tables are checked. If set to 0, foreign key constraints are ignored, with a couple of exceptions. When re-creating a table that was dropped, an error is returned if the table definition does not conform to the foreign key constraints referencing the table. Likewise, an ALTER TABLE operation returns an error if a foreign key definition is incorrectly formed. Setting foreign_key_checks to 0 also affects data definition statements: DROP SCHEMA drops a schema even if it contains tables that have foreign keys that are referred to by tables outside the schema, and DROP TABLE drops tables that have foreign keys that are referred to by other tables. Setting foreign_key_checks to 1 does not trigger a scan of the existing table data. Therefore, rows added to the table while foreign_key_checks=0 will not be verified for consistency.
当FOREIGN_KEY_CHECKS设置为0时,外键约束会被忽略,而当FOREIGN_KEY_CHECKS设置为1时不会触发约束检查。