MySQL 8含ORDER BY的空结果查询耗时过长,求索引优化方案
MySQL 8.0.33大表查询性能优化问题
环境与表结构
使用MySQL 8.0.33社区版,eventiAcc表存储2000万条记录,表结构如下:
CREATE TABLE `eventiAcc` ( `chiave` int NOT NULL AUTO_INCREMENT, `tipo` tinyint NOT NULL, `account` int DEFAULT NULL, `utente` int DEFAULT NULL, `timestp` datetime NOT NULL, `dato` varchar(50) DEFAULT NULL, `extra` varchar(200) DEFAULT NULL, PRIMARY KEY (`chiave`), KEY `idx1` (`account`,`tipo`,`timestp`), KEY `idx2` (`tipo`,`utente`), KEY `idx_timestp` (`timestp`) ) ENGINE=InnoDB AUTO_INCREMENT=40908800 DEFAULT CHARSET=utf8mb3
问题现象
执行无排序的查询时,MySQL使用idx2索引,0.06秒返回空结果:
SELECT chiave, tipo, account, utente, timestp, dato, extra FROM eventiAcc WHERE utente=25169 AND tipo=26 AND dato='aggIndFatt';
但添加ORDER BY timestp DESC LIMIT 1后,查询耗时长达2分8.12秒才返回空结果:
SELECT chiave, tipo, account, utente, timestp, dato, extra FROM eventiAcc WHERE utente=25169 AND tipo=26 AND dato='aggIndFatt' ORDER BY timestp DESC LIMIT 1;
推测是MySQL优化器选择了idx_timestp索引,先全表排序再应用WHERE条件,导致性能暴跌。
测试验证结果
- 不带
dato条件的查询返回4826行,耗时0.08秒; - 上述查询添加
ORDER BY timestp DESC LIMIT 1仅耗时0.01秒; - 删除
idx_timestp索引后,原慢查询耗时降至0.08秒,但该索引为其他业务查询所需,不能删除。
问题求助
- 如何让MySQL 8忽略
idx_timestp索引? - 解决该性能问题的最优方案是什么?
解决方案
临时忽略指定索引的方法
可以通过查询语句强制指定索引,避免MySQL选择idx_timestp:
- 使用
IGNORE INDEX直接排除idx_timestp:
SELECT chiave, tipo, account, utente, timestp, dato, extra FROM eventiAcc IGNORE INDEX (idx_timestp) WHERE utente=25169 AND tipo=26 AND dato='aggIndFatt' ORDER BY timestp DESC LIMIT 1;
- 使用
FORCE INDEX强制使用idx2索引:
SELECT chiave, tipo, account, utente, timestp, dato, extra FROM eventiAcc FORCE INDEX (idx2) WHERE utente=25169 AND tipo=26 AND dato='aggIndFatt' ORDER BY timestp DESC LIMIT 1;
最优解决方案:创建覆盖联合索引
临时方法只能解决单个查询的问题,最优方案是创建适配该查询的覆盖索引,从根源上优化性能。
当前idx2仅包含(tipo, utente),无法覆盖dato过滤和timestp排序需求。创建以下联合覆盖索引:
CREATE INDEX idx_utente_tipo_dato_timestp ON eventiAcc (utente, tipo, dato, timestp DESC) INCLUDE (chiave, account, extra);
索引设计说明:
utente, tipo, dato:按过滤基数从高到低排序,优先过滤掉不符合条件的记录;timestp DESC:直接满足查询的排序需求,避免额外的排序操作;INCLUDE子句:包含查询需要的其他字段,让索引成为覆盖索引,查询时无需回表读取原数据,性能达到最优。
该索引既能快速过滤出符合utente=25169 AND tipo=26 AND dato='aggIndFatt'的记录,又能直接按timestp降序获取第一条数据,即使结果为空也能瞬间返回。
内容的提问来源于stack exchange,提问作者Andrea
相关产品推荐
相关产品推荐

