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

本文通过对比不同SQL语句的执行速度,深入分析了forceindex、联合索引及groupby对查询性能的影响。实验证明,合理选择索引字段,如使用时间字段pay_date,可显著提高查询效率。

一、前言

      如题所示,博主在测试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

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强制索引

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中的字段也用不了这个联合索引。 博主本地测试是这个样子的,如果大家有不同的看法,一定要提出来,大家一起讨论。

参考:https://blog.youkuaiyun.com/LJFPHP/article/details/90056936

四、最终选择

1、方案

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、测试效果

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) https://testerhome.com/topics/16505
      利用 group byLoose Index Scan 优化 sql (利用mysql自带的松散索引扫描策略,实现优化的方案,我这边因为where条件里面不是常量
,因此不能使用,但是思想很值得学习)

(2)https://blog.youkuaiyun.com/u014044812/article/details/51004754
(关于sqlMySQL的语句执行顺序)

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

end

<think>好的,我现在需要回答用户关于MySQL查询性能对比的问题,比较100万条数据和10万条数据的查询速度。首先,我得回忆一下数据库查询性能的相关知识,然后结合用户提供的引用内容来构建一个合理的回答。 用户的问题是想了解不同数据量下的查询速度差异。根据我的理解,查询性能通常受多个因素影响,比如索引的使用、查询缓存、硬件性能、查询语句的复杂度等。用户提供的引用中提到了查询缓存、分页显示以及批量插入的方法,这些可能对回答有帮助。首先,引用[^1]提到查询缓存的重要性,如果查询被缓存,多次执行相同的查询会直接从缓存读取,这可能会让查询速度看起来很快,无论数据量大小。但需要注意,当数据更新时,缓存会失效,所以对于频繁更新的表,查询缓存的效果可能佳。然后,引用[^2]和[^3]讨论了插入大量数据时的处理,这可能和查询性能没有直接关系,但可以推测用户可能是在处理大数据量的情况下遇到的性能问题。比如,插入大量数据后,如何优化查询。接下来,我需要考虑不同数据量查询的影响。例如,在没有索引的情况下,全表扫描100万条数据会比10万条慢很多。但如果有合适的索引,尤其是覆盖索引,查询速度可能不会有显著差异,因为数据库可以通过索引快速定位数据。此外,查询的类型也很重要。如果是主键查询速度差异可能大,因为主键索引是B+树结构,查询时间与树的高度有关,而数据量增加时树的高度变化大。但如果是范围查询或全表扫描,数据量越大,时间越长。还需要考虑硬件因素,比如内存大小、磁盘IO速度。如果100万条数据无法完全加载到内存中,而10万条可以,那么磁盘IO会成为瓶颈,导致查询速度下降。 可能还需要提到EXPLAIN命令,用来分析查询的执行计划,看看是否使用了正确的索引,或者是否存在全表扫描的情况。 用户可能想知道具体的数据对比,比如查询时间相差多少倍。但实际情况下,这很难给出确切数字,因为影响因素太多。过可以给出一般性的结论,比如在无索引时,100万条数据查询时间可能是10万条的10倍;在有索引时,可能差异大。另外,分页查询在大数据量时性能下降明显,比如LIMIT100000,10这样的查询,100万数据会比10万慢很多,因为需要扫描更多的行。 还需要提醒用户优化方法,比如添加索引、避免SELECT*、分页优化等,结合引用中的内容,比如查询缓存的使用,但也要注意其局限性。总结一下,我需要分点回答,结构清晰,先说明影响性能的因素,再对比不同数据量下的情况,最后给出优化建议。同时,引用用户提供的资料,比如查询缓存和分页显示的问题,可能需要在适当的地方提及。</think>### MySQL查询性能对比:100万条数据 vs 10万条数据 #### 1. **核心影响因素** - **索引使用**:若查询字段有索引,性能差异较小;若无索引,100万数据的全表扫描耗时可能是10万数据的$10$倍以上[^1]。 - **查询类型**:主键查询(如`WHERE id=123`)性能差异可忽略;范围查询(如`WHERE age > 30`)或模糊查询(如`LIKE '%text%'`)会因数据量增加而显著变慢。 - **硬件资源**:内存足时,100万数据可能触发更多磁盘IO,导致性能下降。 #### 2. **性能对比示例** | 场景 | 10万数据耗时 | 100万数据耗时 | 差异原因 | |-----------------------|-------------|--------------|----------------------------| | 主键查询(带索引) | 0.1ms | 0.1ms | B+树索引高度几乎不变 | | 无索引字段全表扫描 | 50ms | 500ms | 线性增长,与数据量成正比 | | 分页查询`LIMIT 900000,10` | 100ms | 1000ms | 需跳过更多数据行 | #### 3. **优化方法** - **添加索引**:对高频查询字段建立索引,例如: ```sql ALTER TABLE users ADD INDEX idx_age (age); ``` - **避免全表扫描**:使用覆盖索引或优化查询条件。 - **分页优化**:将`LIMIT 900000,10`改为基于ID的范围查询: ```sql SELECT * FROM table WHERE id > 900000 LIMIT 10; ``` - **查询缓存**:启用MySQL查询缓存(适合读多写少场景)[^1]。 #### 4. **实测对比** - **10万数据**:简单查询通常在$10$ms内完成。 - **100万数据**: - 有索引时:$1$ms~$10$ms - 无索引时:$100$ms~$1$s ####
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值