[Mysql] mysql limit参数详解及使用过程遇到的问题


本文总阅读量

前提:开发反应一条sql查询很慢,查看sql执行计划:


查看到没有走设置的索引,而是走了主键索引,尝试去掉limit行限制之后:

发现去掉limit走索引正常。
是用group by和limit测试执行计划是否正常:


group by与limit组合会影响执行计划。
只使用limit,去掉order by和group by实验:


执行计划没有受影响。
结论:order by、group by与limit在一起执行的时候要注意执行计划是否影响,如果不得不使用该组合,担心执行计划被更改,建议使用force index(index_name),或者多次测试执行计划不会被影响再放到生产环境里。
下面介绍官方文档就limit参数的解释:

0、如果是想要在结果集中获取固定的行数,在查询语句中使用limit子句,而不是获取全部的数据之后扔掉额外的数据。

1、如果使用limit子句选择少数行,正常情况下倾向于进行全表扫描的时候,mysql会使用索引。

2、如果使用order by与limit row_count组合,mysql只要找到排序结果row_count行数就会停止排序,而不是把所有的结果排序。排序使用索引效率更高。如果一个文件排序必须完成,在row_count行被找到之前,选择不带限制语句的所有行对他们进行排序。在初始化行被找到之后,mysql将不在对结果集排序。如果order by列多行值相同,服务器返回这些行没有任何顺序,根据全部的执行计划会有所不同。换句话说,这些行的排序顺序是不确定的相对于非排序列。

limit会影响执行计划,因此orderby带有limit和不带有limit返回行会是不同排序。

3、官方文档的实验如下:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT * FROM ratings ORDER BY category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+

带有limit子句会影响每一个行category的返回值:

1
2
3
4
5
6
7
8
9
10
mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 4 | 2 | 3.5 |
| 3 | 2 | 3.7 |
| 6 | 2 | 3.5 |
+----+----------+--------+

对于带有limit和不带有limit的子句返回相同的行排序是很重要的,增加合适的列在order by子句中确保顺序。例如在order by后加一个id的排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> SELECT * FROM ratings ORDER BY category, id;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
+----+----------+--------+

4、根据官方文档提供的信息,自己动手实验,创建测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
create table ratings (id int,category int,rating varchar(10));
insert into ratings values(5,1,'3.2'),(1,1,'4.5'),(4,2,'3.5'),(3,2,'3.7'),(6,2,'3.5'),(7,3,'2.7'),(2,3,'5.0');
root@db 08:47: [test]> SELECT * FROM ratings order by category;
+------+----------+--------+
| id | category | rating |
+------+----------+--------+
| 5 | 1 | 3.2 |
| 1 | 1 | 4.5 |
| 4 | 2 | 3.5 |
| 3 | 2 | 3.7 |
| 6 | 2 | 3.5 |
| 7 | 3 | 2.7 |
| 2 | 3 | 5.0 |
+------+----------+--------+
7 rows in set (0.00 sec)
root@db 08:47: [test]> SELECT * FROM ratings order by category limit 5;
+------+----------+--------+
| id | category | rating |
+------+----------+--------+
| 5 | 1 | 3.2 |
| 1 | 1 | 4.5 |
| 4 | 2 | 3.5 |
| 3 | 2 | 3.7 |
| 6 | 2 | 3.5 |
+------+----------+--------+
5 rows in set (0.00 sec)

id列没有做任何排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
root@db 08:47:  [test]> SELECT * FROM ratings order by category,id;
+------+----------+--------+
| id | category | rating |
+------+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+------+----------+--------+
7 rows in set (0.00 sec)

在order by后多加一个id列,category和id列都按大小做了排序。

5、尝试在对id列添加主键索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root@db 09:10:  [test]> alter table ratings add primary key(id);
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
root@db 09:10: [test]> SELECT * FROM ratings order by category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
| 2 | 3 | 5.0 |
| 7 | 3 | 2.7 |
+----+----------+--------+
7 rows in set (0.00 sec)
root@db 09:10: [test]> SELECT * FROM ratings order by category limit 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 | 1 | 4.5 |
| 5 | 1 | 3.2 |
| 3 | 2 | 3.7 |
| 4 | 2 | 3.5 |
| 6 | 2 | 3.5 |
+----+----------+--------+
5 rows in set (0.00 sec)

添加主键之后id和category列都按大小排序。

6、如果使用limit row_count和distinct联合,mysql停止查询一旦找到row_count唯一行数。

7、一些情况下,group by通过顺序读取索引来解决(或者在索引上做一个排序),然后统计信息直到索引值发生变化。在一些情况下,limit row_count不计算任何不必要group by值。

8、只要mysql发送需要的行数据到客户端,并中止查询除非使用sql_cal_found_rows函数。在这种情况下,可以使用select found_rows()获取行数。

9、limit 0快速返回一个空集。对于检查查询有效性很用帮助。

10、如果服务使用临时表来解决查询,使用limit row_count子句来计算需要多少空间。

11、如果order by没有用索引并且limit子句存在,优化器为了避免合并文件会使用内存filesort操作对内存内的行排序。

目录
  1. 1. 前提:开发反应一条sql查询很慢,查看sql执行计划:
  2. 2. 0、如果是想要在结果集中获取固定的行数,在查询语句中使用limit子句,而不是获取全部的数据之后扔掉额外的数据。
  3. 3. 1、如果使用limit子句选择少数行,正常情况下倾向于进行全表扫描的时候,mysql会使用索引。
  4. 4. 2、如果使用order by与limit row_count组合,mysql只要找到排序结果row_count行数就会停止排序,而不是把所有的结果排序。排序使用索引效率更高。如果一个文件排序必须完成,在row_count行被找到之前,选择不带限制语句的所有行对他们进行排序。在初始化行被找到之后,mysql将不在对结果集排序。如果order by列多行值相同,服务器返回这些行没有任何顺序,根据全部的执行计划会有所不同。换句话说,这些行的排序顺序是不确定的相对于非排序列。
  5. 5. 3、官方文档的实验如下:
  6. 6. 6、如果使用limit row_count和distinct联合,mysql停止查询一旦找到row_count唯一行数。
  7. 7. 7、一些情况下,group by通过顺序读取索引来解决(或者在索引上做一个排序),然后统计信息直到索引值发生变化。在一些情况下,limit row_count不计算任何不必要group by值。
  8. 8. 8、只要mysql发送需要的行数据到客户端,并中止查询除非使用sql_cal_found_rows函数。在这种情况下,可以使用select found_rows()获取行数。
  9. 9. 9、limit 0快速返回一个空集。对于检查查询有效性很用帮助。
  10. 10. 10、如果服务使用临时表来解决查询,使用limit row_count子句来计算需要多少空间。
  11. 11. 11、如果order by没有用索引并且limit子句存在,优化器为了避免合并文件会使用内存filesort操作对内存内的行排序。

Proudly powered by Hexo and Theme by Lap
本站访客数人次
© 2020 zeven0707's blog