为啥没走我想要的索引

最近看报警,发现有慢日志。

现状

表结构是这样的

sql 复制代码
CREATE TABLE `A` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '名称',
  `description` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `space_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '空间ID',
  PRIMARY KEY (`id`),
  KEY `idx_space_id` (`space_id`),
  KEY `idx_create_time` (`create_time`),
  KEY `idx_update_time` (`update_time`),
) ENGINE=InnoDB COMMENT='A表'

慢日志的语句为:

sql 复制代码
select * from `A` where space_id = ? order by create_time desc limit ?

explain查看了一下

列名 列值
type index
possible_keys idx_space_id
key idx_update_time
key_len 5
ref ``
rows ***
filtered 0.06
Extra Using where; Backward index scan

可以看到最终走的是updatetime的索引,不是spaceid,所以filtered特别低。

原因

选择索引是优化器的工作,优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句。在数据库里面,扫描行数是影响执行代价的因素之一。扫描的行数越少,意味着访问磁盘数据的次数越少,消耗的 CPU 资源越少。但扫描行数并不是唯一的判断标准,优化器还会结合是否使用临时表、是否排序等因素进行综合判断。

为啥没有走spaceid索引?和sql里的order有关!

如果真的是用了spaceid,那我们来看一下会是什么流程。

explain会显示Using filesort,使用"文件排序"!

因为数据库会初始化sort_buff,查询符合spaceid的信息,将字段放入sort_buffer,然后按照create_time快速排序,按照排序结果取对应行给客户端。

sort_buffer_size,就是 MySQL 为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。这样速度就更慢了。

解决方案

其实这是一个比较常规的问题,要解决的话只要创建(space_id,create_time)的联合索引即可。

有时候大家在review别人的代码的时候,也需要关注一下这点,防止线上出现这种慢查询问题。

总结

数据库方面除了会使用之外,能够知晓一些底层原理,能够避免很多问题,而且也能解决线上很多问题,推荐

  • 《MySQL技术内幕:InnoDB存储引擎》:系统讲述InnoDB,相对易懂,能帮大家建立MySQL整体认知

  • 《MySQL实战45讲》:知识点讲解清晰、深入,很多知识常用于面试、工作中

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:shidawuhen.github.io/

往期文章回顾:

  1. 设计模式

  2. 招聘

  3. 思考

  4. 存储

  5. 算法系列

  6. 读书笔记

  7. 小工具

  8. 架构

  9. 网络

  10. Go语言

相关推荐
计算机毕设定制辅导-无忧学长6 小时前
《基于微信小程序的个人智能刷题助手的设计与实现》
java·spring boot·mysql·微信小程序·uniapp·个人智能刷题助手系统
geovindu6 小时前
sql:Managing Complex Joins and Subqueries in SQL using mysql 9.0
大数据·数据库·数据仓库·sql·mysql·数据库开发·数据库架构
武子康6 小时前
把调用移进同一个类之后:一次 Spring 事务回滚反例
人工智能·后端·agent
步行cgn6 小时前
Spring MVC 中实现 request 域效果的几种方式
java·后端
Rain的Java大神之路6 小时前
MySQL 被高并发打爆?从定位到分库分表逐层拆解(含秒杀库存实战)
java·后端·面试
瑞克-桑切斯6 小时前
MySQL部署手册(5.7.20/8.0)
数据库·mysql
Cosolar6 小时前
DeepInfer 完全教程:DeepSeek 内部推理框架原理、架构与部署实战
后端·架构·github
YDS8296 小时前
Small Spring IOC篇:应用上下文实现和Bean的扩展
java·后端·spring
喵个咪6 小时前
GoWind ShortPlay 拆解:Ent privacy/viewer 多租户隔离——从中间件到 SQL 的零信任链
后端·架构·go
啵啵啵鱼7 小时前
九、JDBC 操作 MySQL 数据库
android·数据库·mysql