【MySql】超时问题分析

问题

现有如下sql,在满足条件数据不足limit限制时超时

sql 复制代码
select *  from  `tms_order` where   create_time<'2024-03-17' and biz_type in ('DO','DO_COLD')    order by id asc  limit 1000  

大致表结构如下:表数据2000万

sql 复制代码
CREATE TABLE `tms_order` (
  `id` bigint(19) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `biz_id` varchar(100) NOT NULL COMMENT '业务单号',
  `biz_type` varchar(30) NOT NULL COMMENT '单据类型,如常温出库单、退货单 冷链 门店 鲲鹏订单 调拨单等',
  	......
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_by` varchar(50) NOT NULL DEFAULT 'wms' COMMENT '更新者',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_delete` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除 0-否 1-是',
  `
  `predict_package_count` int(11) DEFAULT '0' COMMENT 'WMS转DO预计包裹数',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_biz_id_type` (`biz_id`, `biz_type`),
  KEY `idx_parent_id` (`parent_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE = InnoDB AUTO_INCREMENT = 41025046 DEFAULT CHARSET = utf8mb4 COMMENT = '运输订单信息表'

分析

当查询 tms_order 表(2000万数据)且没有符合条件的数据时发生超时,主要原因是索引选择不当导致需要扫描大量数据才能确认无结果。具体分析如下:

1. 查询条件与现有索引不匹配

  • 条件create_time < '2024-03-17'biz_type IN ('DO','DO_COLD')
  • 排序ORDER BY id ASC
  • 现有索引idx_create_time(只包含 create_time),没有 biz_type 的单独索引,也没有覆盖 id 的复合索引。

2. 执行计划推测

优化器可能选择使用 idx_create_time 索引:

  • 通过索引快速定位所有 create_time < '2024-03-17' 的记录(可能范围极大,比如覆盖了大部分数据)。
  • 对每条索引对应的行进行回表 (访问聚簇索引获取完整行数据),然后检查 biz_type 是否在集合中。
  • 由于排序要求 ORDER BY id,且 idcreate_time 无直接顺序关系,MySQL 需要将满足条件的所有行(如果有)暂存,然后进行文件排序(filesort),最后取前1000条。

3. 为何"查不到数据"比"查到数据"更慢?

  • 有数据时 :如果满足 biz_type 条件的记录较多,可能在扫描 idx_create_time 的早期就找到了1000条,即使需要回表和排序,也能通过 LIMIT 1000 提前终止扫描(但实际需要根据优化器策略判断是否提前终止,某些情况下仍需获取全部候选行后再排序)。
  • 无数据时 :没有任何记录满足 biz_type 条件,但 idx_create_time 的范围扫描仍需执行完毕(直到索引末尾),并且每条索引都要回表检查 biz_type。若 create_time < '2024-03-17' 覆盖了例如1500万行,则需要1500万次回表操作(随机I/O),导致巨大的 I/O 开销和 CPU 消耗,最终超时。

4. 其他潜在原因

  • 锁竞争:如果表上有频繁的写操作,查询可能被阻塞,但通常与是否有结果无关。
  • 临时表/文件排序:即使无数据,如果优化器预估错误,可能仍会创建临时表,但无数据时开销较小。

优化建议

  • 创建复合索引 ,同时覆盖查询和排序字段,避免回表:

    sql 复制代码
    -- 索引1:以 create_time 为前列,包含 biz_type 和 id(覆盖索引)
    ALTER TABLE tms_order ADD INDEX idx_create_time_biz_type_id (create_time, biz_type, id);
    
    -- 或者根据实际数据分布,优先过滤性强的字段:
    ALTER TABLE tms_order ADD INDEX idx_biz_type_create_time_id (biz_type, create_time, id);

    这样查询可直接在索引上完成条件过滤和排序,无需回表,且能快速定位到无数据的边界。

  • 如果数据量极大 ,考虑按时间分区(如按 create_time 分区),查询时仅扫描相关分区。

  • 分析数据分布 ,若 biz_type 取值很少,可考虑改用 EXISTS 或联合查询等方式。

通过以上优化,查询可以在索引遍历过程中快速判断无数据,避免大量回表,从而防止超时。

相关推荐
阿维的博客日记10 分钟前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson11 分钟前
CAS的底层实现
java
AI应用实战 | RE15 分钟前
014、索引高级实战:当单一向量库不够用的时候
数据库·人工智能·langchain
ffqws_15 分钟前
Spring Boot入门:通过简单的注册功能串联Controller,Service,Mapper。(含有数据库建立,连接,及一些关键注解的讲解)
数据库·spring boot·后端
九英里路22 分钟前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串
YDS82926 分钟前
大营销平台 —— 抽奖前置规则过滤
java·spring boot·ddd
清水白石00827 分钟前
《Python 架构师的自动化哲学:从基础语法到企业级作业调度系统与 Airflow 止损实战》
数据库·python·自动化
仍然.31 分钟前
多线程---CAS,JUC组件和线程安全的集合类
java·开发语言
阿华田51231 分钟前
MySQL性能优化大全
数据库·mysql·性能优化
不懂的浪漫37 分钟前
mqtt-plus 架构解析(五):错误处理与 ErrorAction 聚合策略
java·spring boot·后端·物联网·mqtt·架构