优化慢SQL案例(一)

0 阅读3分钟

说明:本文通过一个具体案例,介绍如何优化SQL。

场景

有下面这张表,表结构如下:

CREATE TABLE `i_users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  `password` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '123456',
  `create_date` datetime NOT NULL,
  `deleted` tinyint NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `i_users_create_date_index` (`create_date`)
) ENGINE=InnoDB AUTO_INCREMENT=8010001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

有800万条记录

# 总记录数
select count(1) from i_users;

如下:

在这里插入图片描述


有查询如下:

# 查询2023623153205秒之后的,第100万条~100万零10条记录
select * from i_users where create_date > '2023-06-23 15:32:05' order by create_date limit 1000000, 10;

查询时间,27秒

在这里插入图片描述

分析

先看下执行计划,all查询,索引失效了

在这里插入图片描述

为什么这么慢,简单来说,是因为回表了。回表,指查询结果通过普通索引无法得知,需要借助聚簇索引再查一遍。

举个例子,索引我们一般比作书的目录,我们想知道书的某一章的名字,通过目录就能知道,但是如果我们还想知道某一章的第一句话是什么,是不是就需要借助目录,先知道这章在哪一页,然后再翻到那一页查看,这个过程就是回表。

上面的SQL,因为是全字段查询,只有主键、创建时间字段有索引,其他字段需要借助聚簇索引回表才能得知,所以速度慢。如果只查询主键字段,效率是很高的,如下:

select id from i_users where create_date > '2023-06-23 15:32:05' order by create_date limit 1000000, 10;

1秒不到

在这里插入图片描述

优化

优化一:自关联

使用自关联,即自己和自己关联,避免回表,如下:

# 自表关联
select t1.* from i_users t1 inner join (select id from i_users where create_date > '2023-06-23 15:32:05' limit 1000000, 10) t2 on t1.id = t2.id;

立竿见影

在这里插入图片描述

优化二:借助自增主键

如果你的create_date字段是递增的,还可以借助自增主键,先在子查询里找到符合时间的ID,然后外层就直接用主键查询,如下:

# 借助自增主键(如果你的create_date字段是递增的)
select * from i_users where id > (select id from i_users where create_date > '2023-06-23 15:32:05' limit 1000000, 1) limit 10;

效率也很高,不过我表中的create_date是随机生成的,不是递增的,所以和上面的查询结果不同

在这里插入图片描述

优化三:使用between

这种和上面那种一样,前提是create_date是递增的,可以借助递增主键ID,使用between,如下:

# 使用between范围查询
select * from i_users where create_date > '2023-06-23 15:32:05' and id between 1000000 and 1000010;

效率更快,只不过我的表数据create_date不是递增的,所以只查出了8条记录;

在这里插入图片描述

总结

本文通过一个具体场景,介绍了慢SQL优化的几种方式,参考微信公众号(程序员老猫)这篇文章:

首次发布

hezhongying.blog.csdn.net/article/det…