索引优化

那次数据库崩溃

还记得我第一次实现的订单超时检查吗?

2024 年 3 月的一天,订单量达到 80 万单。早上 10 点,运维群里突然炸锅:

“数据库 CPU 100%!” “所有查询都卡住了!” “用户无法下单!”

我慌了,登录数据库一看,那个定时任务的查询正在扫描 80 万条订单记录

慢查询日志

数据设计要点

  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

这个查询每分钟都在执行,数据库当然扛不住!


索引基础

什么是索引?

索引就像书的目录,可以快速找到需要的内容,而不必翻遍整本书。

数据设计要点

  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

索引的类型

类型说明适用场景
主键索引唯一标识记录主键字段
唯一索引值必须唯一手机号、身份证
普通索引最常用的索引查询条件字段
联合索引多个字段的组合多条件查询
全文索引文本搜索搜索功能

联合索引设计

最关键的问题

数据设计要点

  • 核心是在 delay_tasks 里保存业务事实,而不是把规则散落在应用逻辑里。
  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 关键字段包括 idtask_idexecute_atstatus,它们决定后续查询和管理能力。

索引设计原则

原则 1:最左前缀原则

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

原则 2:索引覆盖查询

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

原则 3:选择性和基数

数据设计要点

  • 这里关注数据模型和约束关系,不需要记住具体语法。

实战索引优化

场景 1:订单超时查询

数据设计要点

  • 核心是在 orders 里保存业务事实,而不是把规则散落在应用逻辑里。
  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
  • 关键字段包括 iduser_idproduct_idstatuscreated_at,它们决定后续查询和管理能力。

场景 2:延时任务查询

数据设计要点

  • 核心是在 delay_tasks 里保存业务事实,而不是把规则散落在应用逻辑里。
  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
  • 关键字段包括 idtask_idtask_typetask_dataexecute_atretry_countcreated_atupdated_at,它们决定后续查询和管理能力。

索引维护

定期分析索引

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

删除无用索引

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

重建碎片化索引

数据设计要点

  • 这是一次表结构演进:随着业务能力增加,把新状态、新时间点或新归属关系补进数据模型。
  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

想一想

思考 1

如果查询条件中有 execute_at >= NOW() AND execute_at <= NOW() + INTERVAL 1 HOUR,如何设计索引?

参考答案

问题分析:

这是一个范围查询,查询未来 1 小时内将要执行的任务。

数据设计要点

  • 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。

索引设计:

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。

为什么方案 1 更好?

最左前缀原则:

方案 1:idx_execute_status (execute_at, status)
  ├─ 先按 execute_at 过滤(范围查询)
  └─ 再按 status 过滤
  ✅ execute_at 的范围查询可以直接利用索引

方案 2:idx_status_execute (status, execute_at)
  ├─ 先按 status 过滤(等值查询)
  └─ 再按 execute_at 过滤(范围查询)
  ⚠️ 虽然也能用,但不够高效

最佳实践:

数据设计要点

  • 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。