暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

MySQL 外键影响

原创 CuiHulong 2022-06-14
2932

数据库范式是一个比较抽象的概念,对于关系型数据库来说,有效的解决关系数据库中避免数据冗余,减少数据库的存储空间,并且减轻维护数据完整性的成本。

数据库范式也是关系型数据库核心的设计模式之一,也是从事数据库开发人员必备知识。其中外键适用于3NF第三范式,在数据库中的作用是保持数据一致性,完整性。主要目的是使两张表形成关联,控制存储在外键表中的数据。 外键在一定程度上说明的业务逻辑,会使设计周到具体全面。

特别是,MySQL已主键驱动数据,经验告诉我们,外键其实不适合。实际环境中很少使用外键,一般都是程序实现关联关系。

外键理解

在MySQL外键的关联中,包含多种action关联,如语法中体现:

[CONSTRAINT [symbol]] FOREIGN KEY [index_name] (col_name, ...) REFERENCES tbl_name (col_name,...) [ON DELETE reference_option] [ON UPDATE reference_option] reference_option: RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT

注意:

  • 目前外键使用范围 InnoDB和NDB存储引擎都会使用FOREIGN_KEY,还有受到lower_case_table_names大小写敏感系统变量。

  • 父子表必须使用相同的存储引擎,并且不能将它们定义为临时表。
    创建外键约束需要父表上的REFERENCES权限。

  • 外键和引用键中的对应列必须具有类似的数据类型。固定精度类型(如INTEGER、DECIMAL)的大小和符号必须相同。字符串类型的长度不必相同。对于非二进制(字符)字符串列,字符集和排序规则必须相同。

  • 在外键引用表中,必须有一个索引,其中外键列以相同顺序列在第一列。以便外键检查可以快速,不需要全表扫描。

  • NDB要求在作为外键引用的任何列上都有一个显式的唯一键(或主键)。InnoDB不是。

  • 不支持外键列上的索引前缀,BLOB和TEXT列不能包含在外键中。

  • InnoDB目前不支持分区的表的外键

  • 外键约束不能引用虚拟生成的列。

外键的行为

从语法上包含三个Action 直接引用,update ,delete 对应操作:

  • CASCADE:删除或更新父表中的行,并自动删除或更新子表中匹配的行。

  • SET NULL:从父表中删除或更新行,并将子表中的外键列或列设置为NULL.

  • RESTRICT:拒绝父表的删除或更新操作。

  • NO ACTION:来自标准SQL的关键字。在MySQL中,相当于RESTRICT。如果在被引用的表中有一个相关的外键值,MySQL服务器拒绝对父表进行删除或更新操作。一些数据库系统有延期检查,NO ACTION就是延期检查。在MySQL中,外键约束被立即检查,所以NO ACTION与RESTRICT相同

  • SET DEFAULT:这个动作被MySQL解析器识别,但是InnoDB和NDB都拒绝包含ON DELETE SET DEFAULT或ON UPDATE SET DEFAULT子句的表定义。

备注:对于未指定的ON DELETE或ON UPDATE,默认操作总是NO action

InnoDB使用深度优先搜索算法对对应外键约束的索引记录执行级联操作。

锁机制

MySQL根据需要将元数据锁扩展到外键约束相关的表。对于外键检查,在相关表上使用共享只读锁(lock TABLES READ)。对于级联更新,在操作涉及的相关表上使用一个无共享的写锁(lock TABLES write)。当存在外键约束的时候,每次要去扫描此记录是否合格。所以一段关联字段操作,就容易会出现死锁现象。

#结构 CREATE TABLE categories( cat_id int not null auto_increment primary key, cat_name varchar(255) not null ) ENGINE=InnoDB; CREATE TABLE products( prd_id int not null auto_increment primary key, prd_name varchar(355) not null, prd_price decimal, cat_id int not null, FOREIGN KEY fk_cat(cat_id) REFERENCES categories(cat_id) ON UPDATE CASCADE ON DELETE RESTRICT )ENGINE=InnoDB; #数据 insert into categories(cat_id,cat_name) values(1,'水果'),(2,'蔬菜'); insert into products(prd_id,prd_name,prd_price,cat_id) values(1,'苹果',9,1),(2,'菠萝',10,1),(3,'白菜',8,2),(4,'可乐',7,3);

1)有意思的演示:
父表插入数据未提交,子表更新等待。
父表进行更新未提交,子表插入等待。

session1 session2
image.png image.png
image.png image.png
#外键最少2张表操作 mysql> show OPEN TABLES where In_use > 0; +----------+----------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+----------+--------+-------------+ | dbdemo | products | 2 | 0 | +----------+----------+--------+-------------+ mysql> SHOW ENGINE INNODB STATUS\G #外键错误 ------------------------ LATEST FOREIGN KEY ERROR ------------------------ 2022-05-31 17:22:52 140626893833984 Transaction: TRANSACTION 9011856, ACTIVE 0 sec updating or deleting mysql tables in use 1, locked 1 4 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1 MySQL thread id 23, OS thread handle 140626893833984, query id 168 localhost root updating update categories set cat_id=3 where cat_id=2 Foreign key constraint fails for table `dbdemo`.`products`: , CONSTRAINT `products_ibfk_1` FOREIGN KEY (`cat_id`) REFERENCES `categories` (`cat_id`) Trying to update in parent table, in index PRIMARY tuple: DATA TUPLE: 4 fields; 0: len 4; hex 80000002; asc ;; 1: len 6; hex 000000898290; asc ;; 2: len 7; hex 01000001970151; asc Q;; 3: len 6; hex e894ace88f9c; asc ;; But in child table `dbdemo`.`products`, in index fk_cat, there is a record: PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000002; asc ;; 1: len 4; hex 80000003; asc ;;

2)DDL操作受外键约束:

mysql > TRUNCATE TABLE products; Query OK, 0 rows affected (0.03 sec) mysql > TRUNCATE TABLE categories; ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint (`dbdemo`.`products`, CONSTRAINT `products_ibfk_1`) mysql> DROP TABLE categories; ERROR 3730 (HY000): Cannot drop table 'categories' referenced by a foreign key constraint 'products_ibfk_1' on table 'products'.

注意:MySQL里外键是即时检查的,对每一行都会运行外键检查。如上述情况 或 用load data 导入数据,在检查外键约束上往往消耗大量时间。有时候,可以灵活处理,忽略外键检查:SET foreign_key_checks=0,默认值是1。

3)子表的里外键值是实际保存的数据,一旦父表外键更新,那子表随着更新。可以理解两张表索引结构变更。其实一个动作触发双倍的索引操作,IO放大。

总结

外键在MySQL提现有两种,一个是如何保证数据库数据的完整性和一致性;二是性能的影响。

不用外键时数据管理简单,操作方便,性能高(导入导出等操作,在insert, update, delete 数据的时候更快)

1.有了外键,当做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,而不得不消耗资源;

2.当删除,更新操作底层需要重新重组索引表,这样导致上锁的时间延长;

3.外键还会因为需要请求对其他表内部加锁而容易出现死锁情况;

4.数据库需要维护外键的内部管理;

综合这些因素,不推荐在关系型数据库使用外键,除了把控不好,在MySQL这样轻量级数据库上,影响范围比较大,建议全靠代码逻辑去保证。

最后修改时间:2022-12-27 13:29:31
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论