mysql 高版本检索外键_第05期:外键到底能不能用?

c3933b2dc5a70271804720ba03dad769.png

外键的设计初衷是为了在数据库端保证对逻辑上相关联的表数据在操作上的一致性与完整性。

外键在大部分企业写的开发规范里会直接规避掉!外键有优缺点,也并不是说每种场景都不适用,完全没有必要一刀切。外键到底能不能用?下面会针对不同的场景来告诉你答案。

一、外键的优缺点

优点:

  • 精简关联数据,减少数据冗余避免后期对大量冗余处理的额外运维操作。
  • 降低应用代码复杂性,减少了额外的异常处理相关数据管理全由数据库端处理。
  • 增加文档的可读性特别是在表设计开始,绘制 ER 图的时候,逻辑简单明了,可读性非常强。

缺点:

  • 性能压力外键一般会存在级联功能,级联更新,级联删除等等。在海量数据场景,造成很大的性能压力。比如插入一条新记录,如果插入记录的表有 10 个外键,那势必要对关联的 10 张表逐一检查插入的记录是否合理,延误了正常插入的记录时间。并且父表的更新会连带子表加上相关的锁。
  • 其他功能的灵活性不佳比如,表结构的更新等。

二、外键的使用

外键参照动作列表:

  • CASCADE:级联,子表跟随父表更新外键值
  • SET NULL:子表更随主表更新外键值为 NULL
  • RESTRICT/ NO ACTION:默认,限制父表改动外键值
  • SET DEFAULT:目前产生的效果和 RESTRICT 相同。

那先来简单看看 MySQL 里外键的用法。MySQL 外键仅有 InnoDB 和 NDB 两种引擎支持,这里只关注 InnoDB。

本次示例 MySQL 的版本为最新版 8.0.19

示例

下面 f1 是父表,f2、f3、f6 分别代表不同类型的外键表,也就是子表。

-- 引用基础表,也就是父表mysql-(ytt_fk/3305)->create table f1(id int primary key,    r1 int, r2 int, r3 int,key idx_r1(r1),key idx_u1 (r2,r3));Query OK, 0 rows affected (0.02 sec)--   随着参照表级联更新外键表,也就是父表更新的话,会级联更新子表的外键mysql-(ytt_fk/3305)->create table f2(id int primary key,    f1_r1 int, mark int, constraint f1_fk_r1 foreign key (f1_r1) references f1(r1) on update cascade);Query OK, 0 rows affected (0.02 sec)--  随着参照表更新外键值为 NULL,也就是父表更新的话,会级联更新子表的外键为 NULLmysql-(ytt_fk/3305)->create table f3 (id int primary key,    f1_id int, foreign key (f1_id) references f1(id) on update set null);Query OK, 0 rows affected (0.02 sec)--  多个键值外键。子表的可以引用父表非主键的其他键mysql-(ytt_fk/3305)->create table f6 ( id int auto_increment primary key,    f1_r2 int, f1_r3 int, foreign key (f1_r2,f1_r3) references f1(r2,r3));Query OK, 0 rows affected (0.02 sec)

场景一:强烈要求数据一致性,程序弱化,数据库端强化,表结构改动小,并发不高的场景。

用一条记录验证表 f2 和 f6。从功能性角度来看,外键的优势很明显,在数据库端完全满足了数据完整性校验。

mysql-(ytt_fk/3305)->insert into f1 values (1,10,100,1000);Query OK, 1 row affected (0.00 sec)mysql-(ytt_fk/3305)->insert into f2 values (1,1);Query OK, 1 row affected (0.01 sec)mysql-(ytt_fk/3305)->insert into f6 values (1,100,1000);Query OK, 1 row affected (0.00 sec)-- 更新引用表 f1mysql-(ytt_fk/3305)->update f1 set id = 2 where id =1;Query OK, 1 row affected (0.01 sec)Rows matched: 1  Changed: 1  Warnings: 0-- f2 也成功级联更新mysql-(ytt_fk/3305)->select * from f2;+----+-------+| id | f1_id |+----+-------+|  1 |     2 |+----+-------+1 row in set (0.00 sec)-- 引用表 r2 字段不允许更新,因为表 f6 有针对字段 r2 的外键约束。mysql-(ytt_fk/3305)->update f1 set r2 = 11 ;ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`ytt_fk`.`f6`, CONSTRAINT `f6_ibfk_1` FOREIGN KEY (`f1_r2`, `f1_r3`) REFERENCES `f1` (`r2`, `r3`))

场景二:频繁的数据装载,但是也严格要求数据库端保证数据一致性。

这里只验证表 f6,同时克隆一张新表 f6_no_fk,除了没有外键,表结构和 f6 一样。导入 400W 条样例数据。

-- 导入 f6,有外键,时间 32 秒多。mysql-(ytt_fk/3305)->load data infile '/var/lib/mysql-files/f1_sub.dat' into table f6;Query OK, 4000000 rows affected (32.57 sec)Records: 4000000  Deleted: 0  Skipped: 0  Warnings: 0-- 导入 f6_no_fk,没有外键,时间 25 秒多。mysql-(ytt_fk/3305)->load data infile '/var/lib/mysql-files/f1_sub.dat' into table f6_no_fk;Query OK, 4000000 rows affected (25.95 sec)Records: 4000000  Deleted: 0  Skipped: 0  Warnings: 0

从上面看到,单独的测试导入 400W 条记录,带有外键的表比非外键的表时间上没有优势。那针对上面的场景优化下,关闭外键检查参数,导入完成后,再开启。

mysql-(ytt_fk/3305)->truncate f6;Query OK, 0 rows affected (0.04 sec)-- 关闭外键检查。mysql-(ytt_fk/3305)->set foreign_key_checks=0;Query OK, 0 rows affected (0.00 sec)-- 重新导入,时间28秒多。mysql-(ytt_fk/3305)->load data infile '/var/lib/mysql-files/f1_sub.dat' into table f6;Query OK, 4000000 rows affected (28.42 sec)Records: 4000000  Deleted: 0  Skipped: 0  Warnings: 0-- 开启外键检查。mysql-(ytt_fk/3305)->set foreign_key_checks=1;Query OK, 0 rows affected (0.00 sec)

从以上结果看出,关闭外键检查后,导入时间和没有外键的表 f6_no_fk 差不多。

场景三:并发少,事物块简单。

接下来再看下简单的事物块提交方式,我简单写了一个每 500 条记录提交一次的存储过程。

DELIMITER $$CREATE DEFINER=`ytt`@`127.0.0.1` PROCEDURE `sp_generate_data`(IN `tb_name` VARCHAR(64), IN `f_number` INT)begindeclare i int default 0;set @@autocommit=0;while i < f_number DO  set @stmt = concat("insert into ",tb_name,"(f1_r2,f1_r3) values (ceil(rand()*10),ceil(rand()*10))");  prepare s1 from @stmt;  execute s1;  set i = i + 1;  if mod(i,500)=0 THEN    commit;  end if;end while;drop prepare s1;commit;set @@autocommit=1;end$$DELIMITER ;

接下来插入 100W 条记录,

-- 外键表写入总时间为 1 分 14 秒mysql> call sp_generate_data('f6',1000000);Query OK, 0 rows affected (1 min 14.14 sec)-- 非外键表写入时间为 1 分 8 秒mysql> call sp_generate_data('f6_no_fk',1000000);Query OK, 0 rows affected (1 min 8.45 sec)-- 关闭外键检查mysql> set foreign_key_checks=0;Query OK, 0 rows affected (0.00 sec)-- 时间为 1 分 4 秒mysql> call sp_generate_data('f6',1000000);Query OK, 0 rows affected (1 min 4.28 sec)mysql> set foreign_key_checks=1;Query OK, 0 rows affected (0.00 sec)

从测试的结果来看,有外键和没有外键的检索时间在这样的场景下也相差无几。

场景四:主表的外键引用字段类型要扩充,原来的数据溢出,没法保存更大的值。

比如此时字段 r2 定义的数据类型不合适了,需要更改为大点的,比如以下,直接修改会报错,

mysql-(ytt_fk/3305)->alter table f1 change r2 r2 bigint;ERROR 3780 (HY000): Referencing column 'f1_r2' and referenced column 'r2' in foreign key constraint 'f6_ibfk_1' are incompatible.mysql-(ytt_fk/3305)->alter table f6 change f1_r2 f1_r2 bigint;ERROR 3780 (HY000): Referencing column 'f1_r2' and referenced column 'r2' in foreign key constraint 'f6_ibfk_1' are incompatible.

那怎么改呢?需要先把外键删掉,修改完了类型,再加上约束。这种场景就不太适合用外键。

mysql-(ytt_fk/3305)->alter table f6 drop constraint f6_ibfk_1;Query OK, 0 rows affected (0.00 sec)Records: 0  Duplicates: 0  Warnings: 0mysql-(ytt_fk/3305)->alter table f6 change f1_r2 f1_r2 bigint;Query OK, 0 rows affected (0.04 sec)Records: 0  Duplicates: 0  Warnings: 0mysql-(ytt_fk/3305)->alter table f1 change r2 r2 bigint;Query OK, 100000 rows affected (0.73 sec)Records: 100000  Duplicates: 0  Warnings: 0mysql-(ytt_fk/3305)->alter table f6 add foreign key (f1_r2,f1_r3) references f1(r2,r3);Query OK, 0 rows affected (0.03 sec)Records: 0  Duplicates: 0  Warnings: 0

场景五:子表有触发器需求来更新必要的字段。

那关于这点就是,子表的触发器不会随着父表的更新级联应用,也就是此时触发器失效。举个例子,往 f2 上添加一个 before update 触发器。

-- 前置更新触发器CREATE TRIGGER `tr_af_update` BEFORE UPDATE ON `f2` FOR EACH ROW set new.mark = new.f1_r1;mysql-(ytt_fk/3305)->insert into f2 values (1,10,5);Query OK, 1 row affected (0.00 sec)mysql-(ytt_fk/3305)->select * from f2;+----+-------+------+| id | f1_r1 | mark |+----+-------+------+|  1 |    10 |    5 |+----+-------+------+1 row in set (0.00 sec)-- 更新父表,mysql-(ytt_fk/3305)->update f1 set r1 = 2 where r1 = 10;Query OK, 5133 rows affected (0.15 sec)Rows matched: 5133  Changed: 5133  Warnings: 0-- 子表 f2对应的级联做了更改,但是触发器动作没执行。mysql-(ytt_fk/3305)->select * from f2;+----+-------+------+| id | f1_r1 | mark |+----+-------+------+|  1 |     2 |    5 |+----+-------+------+1 row in set (0.00 sec)-- 正常的操作应该这样mysql-(ytt_fk/3305)->update f2 set id = 2;Query OK, 1 row affected (0.00 sec)Rows matched: 1  Changed: 1  Warnings: 0-- mark字段对应的克隆成了f1_r1字段的值。mysql-(ytt_fk/3305)->select * from f2;+----+-------+------+| id | f1_r1 | mark |+----+-------+------+|  2 |     2 |    2 |+----+-------+------+1 row in set (0.00 sec)

场景六:父表为分区表,有外键的需求。

那针对分区表,暂时不支持子表以分区表为父表的外键。

mysql-(ytt_fk/3305)->create table f1_partition like f1;Query OK, 0 rows affected (0.02 sec)mysql-(ytt_fk/3305)->alter table f1_partition  partition by key() partitions 4;Query OK, 0 rows affected (0.10 sec)Records: 0  Duplicates: 0  Warnings: 0mysql-(ytt_fk/3305)->create table f7 ( id int primary key,    f1_partition_id int, foreign key (f1_partition_id) references f1_partition(id));ERROR 1506 (HY000): Foreign keys are not yet supported in conjunction with partitioning

场景七:日常并发很高的场景,应该尽量减少相关事务锁的范围和量级。

那举个简单例子,看看有外键情况下,父表更新,子表级联加锁的情形。

-- SESSION 1mysql-(ytt_fk/3305)->begin;Query OK, 0 rows affected (0.00 sec)mysql-(ytt_fk/3305)->update f1 set r2 = 101 where r2 = 100;Query OK, 1 row affected (0.00 sec)Rows matched: 1  Changed: 1  Warnings: 0mysql-(ytt_fk/3305)->select sys.ps_thread_id(connection_id()) as cid;+------+| cid  |+------+|   47 |+------+1 row in set (0.00 sec)

总共有 11 个锁,也就简单的执行了下 Update,而且更新的只是一行。

-- SESSION 2mysql-((none)/3305)->select count(*) from performance_schema.data_locks where thread_id = 47;+----------+| count(*) |+----------+|       11 |+----------+1 row in set (0.00 sec)

查看锁的细化,父有 f1 有 5 个锁,子表 f6 有 6 个锁。

这都是 MySQL 为了保证数据一致性强制加的,这点在 TPS 要求比较高的场景肯定不合适,

mysql-((none)/3305)->select object_name,lock_type,lock_mode,lock_status,lock_data from performance_schema.data_locks where thread_id = 47 order by object_name;+-------------+-----------+---------------+-------------+------------------------+| object_name | lock_type | lock_mode     | lock_status | lock_data              |+-------------+-----------+---------------+-------------+------------------------+| f1          | TABLE     | IX            | GRANTED     | NULL                   || f1          | RECORD    | X             | GRANTED     | supremum pseudo-record || f1          | RECORD    | X             | GRANTED     | 100, 100, 1            || f1          | RECORD    | X,REC_NOT_GAP | GRANTED     | 1                      || f1          | RECORD    | X,GAP         | GRANTED     | 101, 100, 1            || f6          | TABLE     | IS            | GRANTED     | NULL                   || f6          | RECORD    | S,REC_NOT_GAP | GRANTED     | 100, 100, 12           || f6          | TABLE     | IX            | GRANTED     | NULL                   || f6          | RECORD    | X,REC_NOT_GAP | GRANTED     | 12                     || f6          | RECORD    | X,REC_NOT_GAP | GRANTED     | 101, 100, 12           || f6          | RECORD    | S,GAP         | GRANTED     | 101, 100, 12           |+-------------+-----------+---------------+-------------+------------------------+11 rows in set (0.00 sec)

三、外键的限制:

1. 仅有 InnoDB 和 NDB 引擎支持。

2. 不支持虚拟列。

3. 不支持临时表。

4. 外键列以及引用列数据类型、字符集、校对规则都得一致。

5. 外键列以及引用列都必须建立索引。

6. 外键引用多个列的,列顺序必须一致。

7. 大对象字段不能作为引用列。

8. constraint 命名必须在单个 database 里唯一。

9. 外键级联更新操作不会触发子表上的触发器。

10. 不支持分区表。

总结

本文主要从几个例子来演示了,外键是否应该使用以及在哪些场景下使用,让大家了解外键的详细需求。

从上面我描述的几个场景来说,场景 1,2,3 很适合用外键;场景 4,5,6,7 就不太适合用外键;可以把外键功能放在数据库之外实现。


关于 MySQL 的技术内容,你们还有什么想知道的吗?赶紧留言告诉小编吧!

3b17616d6b3bbe830b44108a6705a724.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从Ant Build演进Gradle Build:导入Ant Build文件

在大型项目上更改构建系统可能很困难并且需要大量工作。 幸运的是&#xff0c;对于那些将Ant版本迁移到Gradle版本的人&#xff0c;Gradle提供了特别方便的机制来促进这种迁移 。 由于Gradle基于Groovy构建&#xff0c;并且Groovy通过AntBuilder包含内置的Ant支持&#xff0c;因…

HTML5 之 简单汇总

参考&#xff1a; HTML5的十大新特性 前端面试必备之html5的新特性 HTML5 主要内容&#xff1a;语义化、增强型表单、多媒体标签、Canvas、SVG、地理定位、拖放API、Web Worker、Web Storage、WebSocket、HTML 5 应用程序缓存 1.语义化元素 1.1结构元素 标签描述article表示与上…

个人信息管理系统代码_Thymeleaf+SpringBoot+Mybatis实现的易游网旅游信息管理系统...

项目简介项目源码&#xff1a;麻烦转发后关注JAVA梦想口服液私信回复【源码】即可获取&#xff01;本系统是基于ThymeleafSpringBootMybatis。是非常标准的SSM三大框架(SpringBoot就是一个大框架&#xff0c;里面包含了许多的东西&#xff0c;其中Spring就是最核心的内容&#…

被问到有没有内核开发经验_一个人就是一个开发团队!成电硬核毕业生自制迷你电脑走红!...

△小视频近日&#xff0c;一段长3分多钟的小视频在B站突然爆红&#xff0c;不仅登上首页&#xff0c;获得超过200万的播放量&#xff0c;还被众多观众“膜拜”。有网友在评论区说&#xff1a;“其实这些东西吧&#xff0c;外行看起来很牛&#xff0c;但我这种内行看起来&#x…

matlab2010a连接mysql_MATLAB2010a+OpenCV2.3.1+VS2010运行TLD

出现matlab不显示C编译器的原因主要还是当前Matlab版本相对于VS来说不够新&#xff0c;比如14版的肯定支持10的VS。 本文引用地址&#xff1a; http://blog.csdn.net/shanpohe/article/details/7596401 http://blog.sina.com.cn/s/blog_adfd55190101ejvr.html TLD(Tracking Lea…

更改span标签样式_CSS 内嵌样式

前面一节我们讲了行内样式&#xff0c;但是行内样式的缺点就是样式不能重用。例如当有好多个 标签&#xff0c;我们希望所有的 标签的样式都一致&#xff0c;那么需要在每个标签中都写一遍&#xff0c;这么会很麻烦&#xff0c;也会增加很多代码。那么为了解决这个问题&#…

mysql 6安装当前密码_MySQL8.0 安装踩坑指南

就在昨天上午&#xff0c;刚为云服务器安装好Apache2.4.33和PHP7.2.4环境&#xff0c;准备再来一个最新的MySQL5.7.22。寻找5.7版本的rpm包时下到mysql80xxx.rpm&#xff0c;看人家的教程是mysql57&#xff0c;难道80是MySQL出出…出了8版&#xff0c;一搜新闻2个小时前MySQL发…

Elasticsearch环境搭建和介绍(Windows)

一、Elasticsearch介绍和安装 1.1 介绍 Elastic Elastic官网&#xff1a;https://www.elastic.co/cn/ Elastic有一条完整的产品线&#xff1a;Elasticsearch、Kibana、Logstash等&#xff0c;前面说的三个就是大家常说的ELK技术栈。 Elasticsearch Elasticsearch官网&#xff1…

Jar Hell变得轻松–用jHades揭秘classpath

Java开发人员将不得不面对的最困难的问题是类路径错误&#xff1a; ClassNotFoundException &#xff0c; NoClassDefFoundError &#xff0c;Jar Hell&#xff0c; Xerces Hell和company。 在本文中&#xff0c;我们将探究这些问题的根本原因&#xff0c;并了解最小的工具&am…

程序控制发送文件到邮箱_Kindle电子邮箱推送

Kindle 推送支持的格式目前 Kindle 推送仅支持以下几种格式。需要注意的是&#xff0c;AZW 和 AZW3 是两种不同的格式&#xff0c;虽然这两种格式 Kindle 设备都支持阅读&#xff0c;但是亚马逊的个人文档服务支持推送 AZW 但是不支持 AZW3。Kindle 格式 (.mobi 或 .azw) * 推荐…

mysql的英文字母_MySQL中查询的有关英文字母大小写问题的分析

mysql数据库在做查询时候&#xff0c;有时候是英文字母大小写敏感的&#xff0c;有时候又不是的&#xff0c;主要是由mysql的字符校验规则的设置决定的&#xff0c;通常默认是不支持的大小写字母敏感的。1. 什么是字符集和校验规则&#xff1f;字符集是一套符号和编码。校对规则…

写接口给别人调用 推送数据到我们_我们写了一个超好用的抖音矩阵数据管理工具...

我最近跑了十来个抖音号&#xff0c;遇到一些问题&#xff0c;然后通过我们NB的程序员解决了。如果你也在做抖音矩阵&#xff0c;那这些问题你肯定也会遇到&#xff0c;所以我把解决问题的方法工具化了&#xff0c;给大家用。我遇到的最大的问题&#xff0c;就是账号数据的同步…

angular 拼接html 事件无效

主要是要引用$compile方法 更多专业前端知识&#xff0c;请上 【猿2048】www.mk2048.com

更好地利用Pmd,Findbugs和CheckStyle的结果。

我们可以列举许多Java静态分析工具&#xff0c;每种工具都专注于特定领域并具有其优势&#xff0c;我们可以列举一下&#xff1a; Pmd是基于静态规则集的Java源代码分析器&#xff0c;它识别潜在的问题&#xff0c;例如&#xff1a; 可能的错误-空的try / catch / finally / s…

Java基础知识(数据类型和集合)

一、数据类型 包装类型 包装类型是对基本数据类型不足之处的补充。 基本数据类型的传递方式是值传递&#xff0c;而包装类型是引用传递&#xff0c;同时提供了很多数据类型间转换的方法。 Java1.5 以后可以自动装箱和拆箱 二、集合 List&#xff1a;有序、可重复。可以通过索引…

mfc使用cef源代码实现_如何获得微信小游戏跳一跳源码以及源代码组合包括哪些...

很多小游戏都是由源代码编写而成的&#xff0c;那大家知道源代码组合包括哪些吗?手机游戏源代码怎么使用的呢?还有&#xff0c;如何获得微信小游戏跳一跳源码?下面就由奇瑰网小编带大家来了解一下相关的内容吧。   源代码组合包括哪些   源代码作为软件的特殊部分&#…

机器学习过程中欠拟合和过拟合的诊断及解决方法

1.Diagnosing bias vs. variance 2.Regularization and bias/variance 3.Learning curves 4.Deciding what to try next 转载于:https://www.cnblogs.com/CoolJayson/p/9704385.html

angularJS解决数据显示闪一下的问题?-解决办法

转自&#xff1a;https://www.cnblogs.com/e0yu/p/7219930.html?utm_sourceitdadao&utm_mediumreferral#undefined 使用 angular JS 的时候&#xff0c;把 angularJS 放到文件底部&#xff0c;在渲染页面的时候&#xff0c;会出现闪一下的情况&#xff1a; 解决办法一&a…

定时运行python脚本并发送邮件_python实现定时发送邮件到指定邮箱

本文实例为大家分享了python实现定时发送邮件到指定邮箱的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下整个链路&#xff1a;传感器采集端采集数据&#xff0c;边缘端上传数据库&#xff0c;从数据库拿到数据。产品端有个自动出报告的需求&#xff0c;并且希望自动…

php异常处理机制

转自&#xff1a;https://www.cnblogs.com/water0729/p/5802476.html php异常我们常接触到的就是error错误码1&#xff0c;warning错误码2&#xff0c;notice错误码8这三类。出现error了系统是挂掉了&#xff0c;但是warning和notice是我们可以捕捉并处理的 php配置项display_e…