[项目与实习] 记录一次优化SQL的过程

背景

之前一直听闻MYSQL的索引很重要,但是很多时候都是用着已经建好的表,里面也提前在一些字段上加好了索引,但是真正在平时的项目与需求中,才发现索引重要在哪,索引对读取速度的提升,以及索引失效会造成什么后果

过程

通常在开发的时候,即使使用gorm这种orm框架,在debug的时候也会打印一下实际运行的SQL,结果在join两张表拿数据的时候,发现仅仅5000条数据,这个sql执行的耗时竟然超过1s

于是我使用explain执行了一下这个语句,看看优化器怎么说,一看,发现两张表的type类型都是ALL, 即都是走的全表
备注,type字段对应的意思:

  • All: 走全表,效率最差(大概率会从硬盘加载数据页)
  • Index: 通过索引走全表,由于索引列一般不包含所有的字段,所以效率更高(整个索引可能已经在内存中, 但是一般还要回表)
  • Range: 检索范围内的行,(between、in、<、>),从符合条件的那个点开始遍历,结束于另一个点
  • Ref: 非唯一索引扫描,返回匹配某个单独值的所有行,索引值不一定唯一,属于查找和扫描的结合体
  • Eq_ref: 唯一索引扫描(联表查询),对于每个索引键,表只有唯一一条记录匹配,常见于主键或唯一索引扫描
  • Const: 只通过一次索引就能找到,常见于主键或者唯一键,只用匹配一条数据,如 id = 1
  • System: Const的特例,表示表只有一行数据,相当于系统表,常见于临时表

index在count(*)的情况下,mysql不会找聚簇索引树而是一个二级索引树去遍历整个表,这时候走index全表会快很多

需要说明的是 const 类型和 eq_ref 都使用了主键或唯一索引,不过这两个类型有所区别,const 是与常量进行比较,查询效率会更快,而 eq_ref 通常用于多表联查中。

于是我果断地在被连接和连接表的连接字段上加了索引,再一跑一看

显然,走了索引,时间也从原来的1s+大大地降到不到30ms,简直惊人

话说回来,只有连接表走索引,也是因为被连接表的所有行都要保留,取得所有记录,所有还是走全表

然后我突然想起,之前的另一个sql好像也是类似的情况,于是我打算如法炮制 :ku:

查询语句如下

EXPLAIN
SELECT
  tb_patrol_result_1h.*,
  tb_patrol_operate.status
FROM
  tb_patrol_result_1h
  LEFT JOIN tb_patrol_operate ON tb_patrol_result_1h.place_id = tb_patrol_operate.place_id
  AND tb_patrol_result_1h.set_id = tb_patrol_operate.set_id
  AND tb_patrol_result_1h.patrol_item_id = tb_patrol_operate.patrol_item_id
WHERE
  day IN (20230730, 20230731)
  AND `tb_patrol_result_1h`.`deleted_at` IS NULL

自信一跑,结果发现

咦,怎么两张表还都是ALL :pen: 不应该啊

  1. 怀疑where条件影响,去掉后,没有变化
  2. 两个表的字段类型是相同的,但是索引类型不同,一边是三个独立的普通索引,另一边是三个字段的联合唯一索引
    1. 怀疑顺序导致的没走索引,但是三个顺序和联合索引中的顺序是一致的,且都是等于
    2. 会不会是因为数据量很小,mysql认为没有必要走索引(一开始右表只有一条数据)

于是我试着往右表添加了几条数据后,其他没变,查询执行计划

芜湖,确实走了索引 :tushe:

至于第一个表,与前面的例子不同,这个语句带where条件,且列在第一个表中是有的,按理说表一应该也会走索引才对,但是我在day字段上加了key后还是没走索引,估计是字段内容区分度过小

这个是区分度的估计值,如果表变化很大,可以使用analyse table tb_xxx进行重新计算

补充

除了 type,我们也要关注 extra 显示的结果。

  • Using filesort :当查询语句中包含 order by 操作,而且无法利用索引完成排序操作的时候, 这时不得不选择相应的排序算法进行,甚至可能会通过文件排序,效率是很低的,所以要避免这种问题的出现。
  • Using temporary:使了用临时表保存中间结果,MySQL 在对查询结果排序时使用临时表,常见于排序 order by 和分组查询 group by。效率低,要避免这种问题的出现。
  • Using index:所需数据只需在索引即可全部获得,不须要再到表中取数据,也就是使用了覆盖索引,避免了回表操作,效率不错。

总结

索引优化是一门学问,简单来说在连接字段加上加索引肯定是正确的,但是要分析为什么最后没按照常理走索引的原因,就得结合多个方面了

参考链接

EXPLAIN用法和结果分析

mysql索引访问方式实践

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注