为啥没走我想要的索引

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

现状

表结构是这样的

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语言

相关推荐
BingoGo7 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php
JaguarJack8 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php·服务端
小灰灰搞电子8 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
小奏技术8 小时前
10 MB 的 Postman 替代品,启动不到 1 秒
后端
东风破_8 小时前
Text2SQL :用自然语言操作 SQLite 数据库
人工智能·后端
IT_陈寒13 小时前
Python的多线程就是个假把式,我算是体验到了
前端·人工智能·后端
码事漫谈13 小时前
如果 AI 要圈养人类,它可能不需要笼子
后端
newerp13 小时前
Golang 调度循环:Go runtime 如何永不停歇地找人干活
后端·程序员·go
明月_清风14 小时前
AI 时代,为什么架构师又开始谈"本体论"?
人工智能·后端·agent