【mysql】sql查询速度不变?不同数据量下,查询速度不会变化的问题

一、前言

如题所示,博主在测试sql的时候,发现有一条sql的速度是固定的?因为是时间搜索,所以我们通过更改时间区间来测试如下:

sql 复制代码
between '2018-05-20 00:00:00' and '2019-05-30 23:59:59        66646 rows   19s
between '2019-05-20 00:00:00' and '2019-05-30 23:59:59         9046 rows	  19s

大概就是这个样子的,查询数据量在6W9千的时候,速度竟然一样,而且是真的慢,这是为什么呢?刚发现时候还是挺郁闷的,不过通过这个问题也让博主学到不少东西,下面听我娓娓道来。

sql的来源是博主的上一篇文章: mysql使用group by实现组内排序实战

二、sql的explain对比

1、原sql的explain

sql 复制代码
mysql> desc select max(pay_id) as max_pay_id,sum(pay_money) as total_pay_money,receiver_id from pay_info  force index(receiver_id) where pay_date between '2019-05-20 00:00:00' and '2019-05-30 23:59:59'  and order_state > 0 and server_unique_flag > 0 group by receiver_id;
+----+-------------+----------+-------+---------------+-------------+---------+------+---------+-------------+
| id | select_type | table    | type  | possible_keys | key         | key_len | ref  | rows    | Extra       |
+----+-------------+----------+-------+---------------+-------------+---------+------+---------+-------------+
|  1 | SIMPLE      | pay_info | index | receiver_id   | receiver_id | 8       | NULL | 2974170 | Using where |
+----+-------------+----------+-------+---------------+-------------+---------+------+---------+-------------+

大眼一瞟,用到了索引,extra没有显示其他的东西,sql优化看起来还不错,但是速度为何这么慢呢,难道是因为咱们强制指定索引的原因吗?

2、去掉force index强制索引

sql 复制代码
mysql> desc select max(pay_id) as max_pay_id,sum(pay_money) as total_pay_money,receiver_id from pay_info where pay_date between '2019-05-20 00:00:00' and '2019-05-30 23:59:59'  and order_state > 0 and server_unique_flag > 0 group by receiver_id;                          
+----+-------------+----------+-------+----------------------------------+----------+---------+------+--------+---------------------------------------------------------------------+
| id | select_type | table    | type  | possible_keys                    | key      | key_len | ref  | rows   | Extra                                                               |
+----+-------------+----------+-------+----------------------------------+----------+---------+------+--------+---------------------------------------------------------------------+
|  1 | SIMPLE      | pay_info | range | receiver_id,order_state,pay_date | pay_date | 5       | NULL | 109814 | Using index condition; Using where; Using temporary; Using filesort |
+----+-------------+----------+-------+----------------------------------+----------+---------+------+--------+---------------------------------------------------------------------+

大眼一瞟,用到了索引,但是extra显示使用了临时表和文件排序,看起来是真难看。但是执行查询看了下,速度竟然提升到了3s。这是为什么呢?明明看起来sql很差劲的

3、分析结果

可以看到使用force index之后,explainextra确实是好看了很多,但是为什么查询比不用force还慢呢?不加force的话,extra里面用到了临时表和文件排序呢。后来仔细看一下执行计划,用force_index之后,sql一共扫描了297W的数据。而不加force,一共扫描了10W的数据。数据量的差异过大,所以说哪怕不加force使用到了临时表也无所谓,速度也会更快一筹。

三、如何优化

1、force index 的问题吗

其实这里的主要问题并不是force index这个语法的影响,而是force index里面的索引列的影响。我们为了消除group by的临时表和文件排序,所以选用了receiver_id这个字段作为强制索引。但是我们忽略了索引关于筛选数据的作用。receiver_id在这里基本没有起到忽略数据的作用,所以扫描行数是297W。 这里我们使用force index(pay_date)的话,扫描的函数会直线下降。

2、有人可能会说,加联合索引可以吗?

大家都知道,group by会产生临时表,order by会产生文件排序。根据sql的执行顺序,先where条件,后group by,为啥不加个联合索引,让where中的条件字段和group by的字段都包含在这个索引中呢?这样group by 就不会使用临时表了。

答案是不可以 。首先我们要了解联合索引的最左原则,范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引。 我们sqlwhere条件里面有三个范围查询,所以加联合索引的话,也只能用到最左前缀的那个索引,而后续的字段是用不到这个联合索引的。group by中的字段也用不了这个联合索引。 博主本地测试是这个样子的,如果大家有不同的看法,一定要提出来,大家一起讨论。

参考:blog.csdn.net/LJFPHP/arti...

四、最终选择

1、方案

erlang 复制代码
1、使用force index消除临时表,避免扫描过多数据
2、选用能尽可能的筛选数据的列,使用这个列的索引
		例如:
		order_state缩小范围有限,缩小50%
		server缩小范围10%
		pay_date如果搜索范围小的话,能缩小到1%以下,如果搜索范围大,那速度慢点也正常,何况还能用到索引。因此选用pay_date
3、在扫描行数和extra优化之间选一个更合适的方案
4、选用force index(pay_date) 之后,耗时:  19s ---> 4s
5、给where 条件的字段都加联合索引(pay_date,order_state,server_unique_flag ,receiver_id) ,经过测试效率和单纯使用pay_date相当

2、测试效果

scss 复制代码
force index(pay_date)	2018/5/20		4.01s	148w rows	
force index(receiver_id)	2018/5/20		18.76s	297w rows	
---------------------------------------------------------------
force index(pay_date)	2019/5/20		0.13s	12w   rows		
force index(receiver_id)	2019/5/20		18.63s	297w rows

博主这里最终选用的是用pay_date这个时间索引,因为在sql里面,时间筛选是最明显的,其他的where字段辨识度都不是很高,不能起到很好的筛选效果。通过这个问题,博主才发现,原来并不是explain看起来好看就行了,很多时候是要根据实际的情况来决定的,目标只有一个,那就是让sql快一点,再快一点

3、优秀文章

(1) testerhome.com/topics/1650...

利用 group byLoose Index Scan 优化 sql (利用mysql自带的松散索引扫描策略,实现优化的方案,我这边因为where条件里面不是常量

,因此不能使用,但是思想很值得学习)

(2)blog.csdn.net/u014044812/...

(关于sqlMySQL的语句执行顺序)

优化水平还是有限,加油!

end

相关推荐
bear_794 小时前
Go操作MySQL
开发语言·go
慕城南风1 天前
Go语言中的defer,panic,recover 与错误处理
golang·go
桃园码工3 天前
1-Gin介绍与环境搭建 --[Gin 框架入门精讲与实战案例]
go·gin·环境搭建
云中谷3 天前
Golang 神器!go-decorator 一行注释搞定装饰器,v0.22版本发布
go·敏捷开发
苏三有春3 天前
五分钟学会如何在GitHub上自动化部署个人博客(hugo框架 + stack主题)
git·go·github
我是前端小学生4 天前
Go语言中的方法和函数
go
探索云原生4 天前
在 K8S 中创建 Pod 是如何使用到 GPU 的: nvidia device plugin 源码分析
ai·云原生·kubernetes·go·gpu
自在的LEE4 天前
当 Go 遇上 Windows:15.625ms 的时间更新困局
后端·kubernetes·go
Gvto5 天前
使用FakeSMTP创建本地SMTP服务器接收邮件具体实现。
go·smtp·mailtrap
白泽来了5 天前
【Go进阶】手写 Go websocket 库(一)|WebSocket 通信协议
开源·go