为什么我不建议你使用外键

MySQL 外键(Foreign Key)是用于建立表之间关系的,它定义了一个表中的一列或一组列,这些列的值必须在另一个表的主键列中存在。

MySQL 外键最大的作用就是有助于维护数据的一致性和完整性

  • 一致性:如果一个订单表引用了一个客户表的外键,外键可以确保订单的客户 ID 存在于客户表中,从而保持数据的一致性。

  • 完整性:外键可以防止在引用表中删除正在被其他表引用的记录,从而维护数据的完整性。

但是,其实在很多大型互联网公司中,很少用外键的,甚至阿里巴巴 Java 开发手册中明确规定了:

【强制】不得使用外键与级联,一切外键概念必须在应用层解决

说明: 以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风险; 外键影响数据库的插入速度。

那么,使用外键会带来哪些问题呢?

先举个例子,我们有两张表:Orders(订单)和 OrderItems(订单项)。这两个表之间通过外键建立关系,订单项表中的外键引用订单表的订单号。

复制代码
CREATE TABLE Orders (
    OrderID INT PRIMARY KEY,
    CustomerID INT,
    OrderDate DATE,
    -- 其他订单信息
);

CREATE TABLE OrderItems (
    ItemID INT PRIMARY KEY,
    OrderID INT,
    ProductID INT,
    Quantity INT,
    -- 其他订单项信息
    FOREIGN KEY (OrderID) REFERENCES Orders(OrderID)
);

性能问题

首先就是性能问题,因为外键会增加数据库的维护负担,因为每次插入、更新或删除数据时,数据库都需要检查外键约束的完整性。

这两张表中共有两个索引,一个是 Orders 表的主键索引,一个是 OrdersItems 表的外键索引,这就使得每次插入、更新或删除订单或订单项时,数据库需都要维护这两个索引,这可能会导致性能开销。

其次,在插入新的订单项之前,数据库需要执行数据一致性检查以确保引用的订单号在 Orders 表中存在。这额外的检查可能增加插入订单项的执行时间。

锁竞争问题

还有就是比较容易忽略的锁竞争问题。当多个事务同时尝试插入或更新订单项时,它们就需要去检查订单表,就需要获得额外的锁,以确保一致性。这可能导致事务之间的锁竞争,降低并发性能。

一旦有了锁竞争,就可能带来更加严重的死锁问题,所以都是需要尽量避免的。

Merge 无法适应分库分表

当数据量大的时候,我们就要考虑分库分表了,但是在分库分表环境中,相关数据可能分布在不同的数据库中,外键通常难以跨越不同数据库来建立关系。更重要的是,分库分表环境中,数据的一致性可能更难维护。跨库事务搞不定。

以上,就是一些比较重要的原因吧。其实最主要的还是外键约束会带来一些额外的开销及锁竞争。而在很多大型互联网公司中,都是会尽量避免的。

就像大厂会使用 RC 来替代 RR 一样,会尽可能的降低锁的发生,一方面提升性能,一方面避免死锁。

相关推荐
爱可生开源社区4 分钟前
SQLShift 重磅更新:支持 SQL Server 存储过程转换至 GaussDB!
数据库
贾修行26 分钟前
SQL Server 空间函数从入门到精通:原理、实战与多数据库性能对比
数据库·sqlserver
傲祥Ax39 分钟前
Redis总结
数据库·redis·redis重点总结
椰椰椰耶1 小时前
【MyBatis】XML实现,配置方法和增、删、改、查
xml·oracle·mybatis
一屉大大大花卷1 小时前
初识Neo4j之入门介绍(一)
数据库·neo4j
周胡杰2 小时前
鸿蒙arkts使用关系型数据库,使用DB Browser for SQLite连接和查看数据库数据?使用TaskPool进行频繁数据库操作
前端·数据库·华为·harmonyos·鸿蒙·鸿蒙系统
wkj0012 小时前
navicate如何设置数据库引擎
数据库·mysql
赵渝强老师2 小时前
【赵渝强老师】Oracle RMAN的目录数据库
数据库·oracle
暖暖木头2 小时前
Oracle注释详解
数据库·oracle
御控工业物联网2 小时前
御控网关如何实现MQTT、MODBUS、OPCUA、SQL、HTTP之间协议转换
数据库·sql·http