千家信息网

如何理解MySQL limit导致的执行计划差异

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容介绍了"如何理解MySQL limit导致的执行计划差异"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够
千家信息网最后更新 2026年11月03日如何理解MySQL limit导致的执行计划差异

本篇内容介绍了"如何理解MySQL limit导致的执行计划差异"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

今天收到一个业务的报警,提示慢日志比较频繁,登上环境查看,发现SQL是一条看起来很简单的语句,环境在MySQL 5.7.16版本下,慢日志里面执行时间显示是近1分钟,我在从库上面执行了一下,发现优化空间确实很大:

select OrgId from `testcomm`.apply_join_org where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc LIMIT 1; Empty set (48.71 sec)

执行计划如下:

explain select OrgId     -> from `testcomm`.apply_join_org     ->  where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc LIMIT 1\G *************************** 1. row ***************************            id: 1   select_type: SIMPLE         table: apply_join_org    partitions: NULL          type: index possible_keys: IndexRTUser           key: IndexCreateTime       key_len: 5           ref: NULL          rows: 4332      filtered: 0.00         Extra: Using where 1 row in set, 1 warning (0.00 sec)

到了这个时候,不上表结构有些草率了,结构有所删减。

CREATE TABLE `apply_join_org` (   `ApplyJoinId` int(11) NOT NULL AUTO_INCREMENT,   `RTId` int(11) DEFAULT NULL,   `UserId` int(11) NOT NULL,   `OrgId` int(11) NOT NULL,   `ApplyMsg` varchar(100) DEFAULT NULL,   `CreateTime` datetime NOT NULL,   `ReplyMemId` int(11) DEFAULT '0',   `ReplyTime` datetime NOT NULL,   `ApplyStatus` tinyint(4) DEFAULT '1' COMMENT '0拒绝1申请2同意',   `IfDel` tinyint(4) DEFAULT '1',   `UpdateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,   `RP` int(11) DEFAULT '0' COMMENT 'RP值',   `sex` tinyint(1) DEFAULT NULL,   `IfLeaguer` tinyint(1) NOT NULL DEFAULT '0',   PRIMARY KEY (`ApplyJoinId`),   KEY `IndexOrgIdStatus` (`OrgId`,`ApplyStatus`,`IfDel`),   KEY `IndexRTUser` (`UserId`),   KEY `IndexCreateTime` (`CreateTime`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=22495957 DEFAULT CHARSET=utf8  1 row in set (0.00 sec)

此外涉及的这张表的数据量有2000万左右,从目前的执行效率来看,无疑于走了一个全表扫描。

其实这个问题到了这个还是比较好理解的。从语句的表现,结合表结构,我们可以感觉到: 整个SQL的执行过程中,原本是基于字段UserId,没想到却因为order by中的CreateTime,导致索引选择错误,执行代价差异很大。

所以到了这里,我们如何来定性这个问题:

1)是因为order by导致的吗?

2)是因为时间字段的排序导致的吗?

3)是因为limit操作导致的吗?

4)是因为userid本身的数据过滤效果差导致的吗?

对于这些疑问,我们可以很快通过几条对比SQL就能够快速验证。

通过如下的SQL可以看到order by不是最主要的原因

select OrgId     ->      from `testcomm`.apply_join_org     ->       where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime ; Empty set (0.01 sec

order by排序也不是最主要的原因

select OrgId     -> from `testcomm`.apply_join_org     ->  where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc ; Empty set (0.01 sec)

order by排序+limit 10也不是最主要的原因

select OrgId from `testcomm`.apply_join_org where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc LIMIT 10; Empty set (0.01 sec)

order by 排序+limit 2也不是最主要的原因

select OrgId     -> from `testcomm`.apply_join_org     ->  where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc LIMIT 2; Empty set (0.01 sec)

而经过这些对比,主要加入了limit 1,索引选择情况就会发生变化。我们抓取一条limit 2的执行计划来看看。可以明显看到type为ref,此外ref部分差异很大(const)。

>explain select OrgId  from `testcomm`.apply_join_org   where IfDel=1 and ApplyStatus=1 and UserId = 12345678 ORDER BY CreateTime desc LIMIT 2\G *************************** 1. row ***************************            id: 1   select_type: SIMPLE         table: apply_join_org    partitions: NULL          type: ref possible_keys: IndexRTUser           key: IndexRTUser       key_len: 4           ref: const          rows: 4854      filtered: 1.00         Extra: Using index condition; Using where; Using filesort 1 row in set, 1 warning (0.00 sec)

如果想得到更进一步的信息,可以使用如下的方式:

SET optimizer_trace="enabled=on" SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G

查看

reconsidering_access_paths_for_index_ordering部分的信息会是关键所在。

"index_provides_order": true,

"order_direction": "desc",

而对于这个问题的分析,主要还是在于对于cost的评估方式,显然在目前的测试中,增加了额外的order by排序操作,导致了代价会略微高一些,而在优化器中在评估中,显然这部分是缺失了一些信息导致判断失误。

有如下几种方式可以修复:

1)补充完整的复合索引,userid和CreateTime能够做到互补,该方案已经在同构环境中做了完整的模拟测试,能够达到预期

alter table  `testcomm`.apply_join_org drop key IndexRTUser; alter table  `testcomm`.apply_join_org add  key `IndexRTUser2`(UserId,CreateTime);

2)使用force index的hint方式来强制索引,当然对于业务具有一定的侵入性

3)调整SQL逻辑模式,确实是否可以使用其他的方式来代替这种limit 1的使用模式。

而从长计议,其实整个评估中的优化器还是比较薄弱的,对于索引选择中的判断依据,如果有了直方图等辅助信息,整个过程会更加如虎添翼,这块的内容,准备在8.0中进行一些模拟测试,稍后奉上测试结果。

"如何理解MySQL limit导致的执行计划差异"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

方式 索引 排序 差异 信息 原因 测试 很大 内容 环境 结构 过程 还是 问题 评估 选择 业务 代价 字段 情况 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 山东特亿宝互联网科技有限公司 中关村互联网科技集团 圆梦互联网科技 河南真二互联网科技有限公司 广州互联网科技公司 广州宏创互联网科技 互联网女皇 腾讯科技 苏州大垚互联网科技 互联网科技公司名称 互联网时代科技改变生活 广州恒生互联网科技有限 互联网为啥属于高科技 新兴科技互联网创新大赛颁奖典礼 注册互联网科技公司经营范围大全 速维有品互联网信息科技(苏州)有限公司 工业互联网山东科技大学 陕西九四连强互联网科技有限公司 米泉互联网科技专业怎么样 互联网科技大会大佬 恒生互联网科技趋势 互联网黑科技 广州百单互联网科技有限公司 深圳互联网科技公司 互联网科技素材视频竖屏 互联网科技商业 南京一网一红互联网科技有限公司 互联网硬科技公司 互联网时代科技型公司的机会 同城互联网络科技 互联网科技博主张洲
0