最近遇到一个线上慢查询的问题,存在外键却没有索引,那么有啥区别呢?

88 阅读4分钟

你好,我是风一样的树懒,一个工作十多年的后端专家,曾就职京东、阿里等多家互联网头部企业。公众号“吴计可师”,已经更新了近百篇高质量的面试相关文章,喜欢的朋友欢迎关注点赞


最近遇到了一个线上慢查询的问题,最后确定是没有创建索引,但是很奇怪的是存在一个外键,那么外键是否能加快查询,与索引区别是什么呢?

数据库中的索引和外键是两个不同的概念,各自有不同的用途和实现方式。以下是它们的核心区别和实际应用场景:


一、定义与核心区别

维度索引(Index)外键(Foreign Key)
核心目的加速数据查询维护表间数据完整性
本质数据结构(如B+树、哈希表)数据约束(引用完整性约束)
强制关系不涉及表间关系必须关联另一表的主键或唯一键
是否可选可选创建(根据查询需求)可选约束(根据业务逻辑需求)
性能影响加速查询,但增加写操作开销保证数据一致性,但可能降低并发写入性能

二、实际应用场景

1. 索引的典型场景

  • 加速WHERE条件查询
    SELECT * FROM orders WHERE user_id = 100;  -- 在user_id字段创建索引
    
  • 优化JOIN操作
    SELECT * FROM users 
    JOIN orders ON users.id = orders.user_id;  -- 在orders.user_id创建索引
    
  • 加速排序和分组
    SELECT * FROM products ORDER BY price;  -- 在price字段创建索引
    

2. 外键的典型场景

  • 防止无效引用
    -- 订单表中的user_id必须存在于用户表
    ALTER TABLE orders 
    ADD FOREIGN KEY (user_id) REFERENCES users(id);
    
  • 级联操作
    -- 用户删除时,自动删除其所有订单
    ALTER TABLE orders 
    ADD FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
    

三、实现细节对比

1. 索引的类型

  • 普通索引(INDEX):加速查询,允许重复值。
  • 唯一索引(UNIQUE):确保列值唯一。
  • 主键索引(PRIMARY KEY):特殊的唯一索引,不允许NULL。
  • 全文索引(FULLTEXT):用于文本搜索优化。
  • 复合索引:多列组合索引(如INDEX (col1, col2))。

2. 外键的约束行为

  • ON DELETE:定义父表数据删除时的行为:
    • CASCADE:级联删除子表数据。
    • SET NULL:将子表外键设为NULL。
    • RESTRICT:阻止删除(默认)。
  • ON UPDATE:定义父表主键更新时的行为(类似ON DELETE)。

四、对数据库性能的影响

1. 索引的代价

  • 写入开销:每次INSERT/UPDATE/DELETE需要维护索引结构。
  • 存储空间:索引通常占用表数据的10%~30%空间。
  • 优化器选择:不合理的索引可能导致优化器选择低效执行计划。

2. 外键的代价

  • 锁竞争:外键约束可能引发行级锁或表级锁,影响并发。
  • 事务开销:需要检查引用完整性,增加事务处理时间。
  • 级联风险ON DELETE CASCADE可能意外删除大量数据。

五、设计时的权衡建议

1. 索引使用原则

  • 高频查询字段必加索引:WHERE、JOIN、ORDER BY涉及的列。
  • 避免过度索引:每个额外索引都会增加维护成本。
  • 复合索引列顺序:区分度高的列放在前面。
  • 监控索引使用率:定期清理未使用的索引。

2. 外键使用原则

  • 核心业务关系必加外键:如订单→用户、评论→文章。
  • 非核心关系可省略:如日志表、统计分析表。
  • 测试环境开启,生产环境慎用:某些高并发场景禁用外键,改用应用层校验。
  • 与触发器谨慎配合:避免复杂的级联逻辑难以维护。

六、常见误区澄清

1. 外键会自动创建索引吗?

  • MySQL InnoDB:自动为外键创建索引。
  • PostgreSQL不会自动创建索引,需手动添加。
  • Oracle:不自动创建,需手动管理。

2. 主键和唯一索引的区别

主键(PRIMARY KEY)唯一索引(UNIQUE)
每表只能有一个每表可以有多个
不允许NULL值允许一个NULL值(取决于数据库实现)
用于定义表的主标识仅保证列值唯一

七、实际案例解析

场景:电商系统订单管理

  • 索引设计
    -- 高频查询:按用户ID查订单
    CREATE INDEX idx_orders_user_id ON orders(user_id);
    
    -- 复合索引:按状态和时间范围查询
    CREATE INDEX idx_orders_status_time ON orders(status, create_time);
    
  • 外键设计
    -- 确保订单关联的用户存在
    ALTER TABLE orders 
    ADD FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
    
    -- 确保商品存在(允许商品被删除后订单保留)
    ALTER TABLE order_items 
    ADD FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE SET NULL;
    

总结

  • 索引是“加速器”:以空间换时间,优化查询性能。
  • 外键是“约束器”:以性能换安全,保障数据一致性。
  • 合理搭配使用:核心业务表建议同时使用索引和外键,非核心表根据场景取舍。

今天文章就分享到这儿,喜欢的朋友可以关注我的公众号,回复“进群”,可进免费技术交流群。博主不定时回复大家的问题。 公众号:吴计可师