前提:开发反应一条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操作对内存内的行排序。