什么是数据库的逻辑外键?数据库的物理外键和逻辑外键各有什么优缺点?

Sherwin.Wei Lv7

什么是数据库的逻辑外键?数据库的物理外键和逻辑外键各有什么优缺点?

回答重点

逻辑外键是一种在应用程序层面上管理和维护数据完整性的方法,而不是通过数据库本身的外键约束。主要是利用应用程序代码来保证引用的完整性。

逻辑外键的优缺点

优点:

  • 灵活性高:应用程序层面控制,可以更灵活地实现复杂的业务逻辑。
  • 性能优化:避免了数据库层面的约束检查,可以在某些情况下提高性能(详细看扩展知识)。
  • 跨数据库兼容性:逻辑外键在不同类型的数据库之间更容易迁移。

缺点:

  • 代码复杂性增加:需要在应用程序代码中手动实现和维护引用完整性,增加了代码的复杂性和错误的可能性。
  • 一致性风险:如果应用程序代码未正确实现引用完整性检查,可能导致数据不一致。
  • 维护成本高:逻辑外键需要开发人员持续关注和维护,增加了维护成本。

物理外键的优缺点

优点:

  • 自动维护引用完整性:数据库会自动检查和维护外键约束,确保数据的一致性。
  • 减少应用层复杂性:开发人员不需要手动管理引用完整性,减少了代码的复杂性和错误的可能性。
  • 数据完整性保障:数据库层面的约束能够更有效地防止非法数据的插入或更新。

缺点:

  • 性能开销:外键约束会增加插入、更新和删除操作的开销,特别是在处理大量数据时。
  • 迁移和复制的复杂性:在进行数据库迁移或复制时,外键约束可能会增加复杂性,需要小心处理。
  • 灵活性较低:物理外键在某些复杂业务逻辑下可能不够灵活,需要更多的手动控制。

扩展知识

逻辑外键实现例子

比如插入一笔订单的时候,先判断这个用户是否存在:

1
2
3
4
5
6
7
8
9
10
11
12
// 检查引用完整性
public void insertOrder(Order order) {
if (!customerExists(order.getCustomerId())) {
throw new IllegalArgumentException("面试鸭用户不存在");
}
// 插入订单
orderRepository.save(order);
}

private boolean customerExists(Long customerId) {
return customerRepository.existsById(customerId);
}

如果是复杂的业务,例如还需要判断用户是否是已注销、是否被冻结等等,此时仅需修改 customerExists 就能实现,而物理外键就没办法实现这么灵活的需求了。

为什么不推荐使用数据库的外键?

阿里巴巴 Java 开发手册写到了:

image.png

可以看到,主要是因为数据库的外键会产生级联更新从而导致性能问题。

因为现在很多互联网公司都是高并发大流量场景,所以一般互联网公司不推荐使用外键。

数据库外键性能问题盘点

1)级联更新

按照手册举例,如果学生表的 ID 被修改,数据库同时需要改成绩表中的对应的学生 ID,如果还有其他关联表则都需要被修改。

在高并发数据量大的情况下,一次修改会产生意料之外的级联更新使得数据库压力过大,导致系统其他操作数据库的请求阻塞,很可能导致系统全面崩盘。

2)检查维护

还是拿学生成绩举例,因为设置了外键,插入成绩表对应的学生 ID 时,数据库需要去检查这个学生 ID 在对应的学生表是否存在,来保证数据外键完整性约束。

除了插入,删除、更新相关外键,数据库都需要去检查数据的完整性,这就产生了性能开销。

3)锁问题

如果多个事务并发修改学生表,那么对应锁定的数据涉及的成绩表也需要被锁定,同理并发修改成绩表,那么对应的学生的表的数据也需要被锁定。

关联外键越多锁定的数据也就越多。锁的数据多除了性能问题,还可能会带来死锁的问题。

数据库外键业务问题盘点

1)无法支持复杂的业务控制

不说其他复杂的情况,就拿删除数据使用逻辑删除的情况,数据库的外键就无法满足级联修改,还是需要开发人员编写业务代码手动控制。

2)分库分表

数据库的外键约束只能控制一个数据库实例,跨实例的情况下无法满足。

Comments