【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 或联合查询等方式。

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

相关推荐
云创智城-yuncitys2 小时前
[特殊字符]⚡ 停充一体化云平台:基于微服务架构的城市智慧停车+新能源充电解决方案
java·微服务·架构
y = xⁿ2 小时前
重生之我创作出了小红书:对象存储模块,用户资料模块
后端·mysql·intellij-idea
Y001112362 小时前
Day10-MySQL-事物
数据库·sql·mysql
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 高效便捷的民航订票系统为例,包含答辩的问题和答案
java
轩情吖2 小时前
MySQL之用户管理
数据库·c++·后端·mysql·权限管理·用户管理
零雲2 小时前
java面试:Spring事务失效的场景有哪些?
java·数据库·面试
二月十六2 小时前
运行 ‘XXXX‘ 时出错 运行 XXXX时出错。命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行,然后重新运行。
java·jar
wenlonglanying2 小时前
mysql之联合索引
数据库·mysql
Aloha_up2 小时前
redis与数据库的一致性问题分析
数据库·redis·缓存