提高性能的几条sql语句

mysql版本

mysql> select version();
+------------+
| version()  |
+------------+
| 5.6.44-log |
+------------+
1 row in set (0.00 sec)

在这里插入图片描述

一、尽量不使用select * from查出所有列

有时候一个表字段太多,有的字段内容还很大,查询用到的列,节省资源、减少网络开销。

为什么大家都说SELECT * 效率低


二、避免使用or进行查询

其中snameage字段均设置成为了索引

  • 使用or进行查询的时候(未命中索引):
mysql> explain select * from student where sname="李磊" or age=20;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | sname,age     | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
  • 可以使用union进行查询(命中了索引):
mysql> explain select * from student where sname="李磊" union select * from student where age=20;
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
| id | select_type  | table      | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
|  1 | PRIMARY      | student    | ref  | sname         | sname | 62      | const |    1 | Using index condition |
|  2 | UNION        | student    | ref  | age           | age   | 4       | const |    1 | NULL                  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL  | NULL    | NULL  | NULL | Using temporary       |
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
3 rows in set (0.00 sec)

三、如果被查询的列是字符串,查询条件则用引号括起来

其中sname为索引列

3.1 当sname字段类型为varchar,不用双引号查询:
mysql> explain select * from student where sname=666;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | sname         | NULL | NULL    | NULL |    6 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

没用到索引,进行了全表扫描。

3.2 当sname字段类型为varchar,用双引号查询:
mysql> explain select * from student where sname="666";
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
| id | select_type | table   | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | student | ref  | sname         | sname | 62      | const |    1 | Using index condition |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
1 row in set (0.00 sec
3.3 当你把sname字段类型更换为inttinyint等这种数字类型的时候:
mysql> explain select * from student where sname=666;
+----+-------------+---------+------+---------------+-------+---------+-------+------+-------+
| id | select_type | table   | type | possible_keys | key   | key_len | ref   | rows | Extra |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-------+
|  1 | SIMPLE      | student | ref  | sname         | sname | 4       | const |    1 | NULL  |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-------+
1 row in set (0.00 sec)

就又用到了索引。


四、建立联合索引要遵循最左侧匹配原则

其中snameagecid依次顺序为联合索引

4.1 全值匹配查询时
mysql> explain select sname from student where sname="李磊" and age=20 and cid=1;
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
| id | select_type | table   | type | possible_keys | key         | key_len | ref               | rows | Extra                    |
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
|  1 | SIMPLE      | student | ref  | snameagecid   | snameagecid | 70      | const,const,const |    1 | Using where; Using index |
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
1 row in set (0.00 sec)

不管sname、age、cid三列在哪个位置,只要三者都有,Mysql中有查询优化器,会自动优化查询顺序,都能够使用索引。
关于mysql最左侧匹配原则

4.2 匹配左边的列时
mysql> explain select sname from student where sname="李磊";
+----+-------------+---------+------+---------------+-------------+---------+-------+------+--------------------------+
| id | select_type | table   | type | possible_keys | key         | key_len | ref   | rows | Extra                    |
+----+-------------+---------+------+---------------+-------------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | student | ref  | snameagecid   | snameagecid | 62      | const |    1 | Using where; Using index |
+----+-------------+---------+------+---------------+-------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
mysql> explain select sname from student where sname="李磊" and age=20;
+----+-------------+---------+------+---------------+-------------+---------+-------------+------+--------------------------+
| id | select_type | table   | type | possible_keys | key         | key_len | ref         | rows | Extra                    |
+----+-------------+---------+------+---------------+-------------+---------+-------------+------+--------------------------+
|  1 | SIMPLE      | student | ref  | snameagecid   | snameagecid | 66      | const,const |    1 | Using where; Using index |
+----+-------------+---------+------+---------------+-------------+---------+-------------+------+--------------------------+
1 row in set (0.00 sec)
mysql> explain select sname from student where sname="李磊" and age=20 and cid=1;
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
| id | select_type | table   | type | possible_keys | key         | key_len | ref               | rows | Extra                    |
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
|  1 | SIMPLE      | student | ref  | snameagecid   | snameagecid | 70      | const,const,const |    1 | Using where; Using index |
+----+-------------+---------+------+---------------+-------------+---------+-------------------+------+--------------------------+
1 row in set (0.00 sec)

遵循mysql最左侧匹配原则,也都使用上了索引。

4.3 既不是全值匹配,又不从最左侧开始
mysql> explain select sname from student where cid=1;
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key         | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | student | index | NULL          | snameagecid | 70      | NULL |    6 | Using where; Using index |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
mysql> explain select sname from student where age=20;
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key         | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | student | index | NULL          | snameagecid | 70      | NULL |    6 | Using where; Using index |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
mysql> explain select sname from student where age=20 and cid=1;
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
| id | select_type | table   | type  | possible_keys | key         | key_len | ref  | rows | Extra                    |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | student | index | NULL          | snameagecid | 70      | NULL |    6 | Using where; Using index |
+----+-------------+---------+-------+---------------+-------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

type为index:只不过它的扫描顺序是按照索引的顺序。这种扫描根据索引然后回表取数据,和all相比,他们都是取得了全表的数据,而且index要先读索引而且要回表随机取数据。


五、避免在索引列进行计算

其中,age字段为索引

mysql> explain select * from student where age-1=19;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

索引失效,全盘扫描。


六、使用like查询的时候,%在前面不走索引

其中sname字段为索引

mysql> select * from student;
+----+-----------+-----+-----+
| id | sname     | age | cid |
+----+-----------+-----+-----+
|  1 | 李磊      |  20 |   1 |
|  2 | 李关亮    |  21 |   2 |
|  3 | 李腾      |  21 |   3 |
|  4 | 黄成志    |  23 |   4 |
|  5 | 张国栋    |  21 |   2 |
+----+-----------+-----+-----+
5 rows in set (0.00 sec)
  • 当%在前面的时候(全表扫描):
mysql> explain select * from student where sname like '%李';
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
  • 当%在后面的时候(使用了索引):
mysql> explain select * from student where sname like '磊%';
+----+-------------+---------+-------+---------------+-------+---------+------+------+-----------------------+
| id | select_type | table   | type  | possible_keys | key   | key_len | ref  | rows | Extra                 |
+----+-------------+---------+-------+---------------+-------+---------+------+------+-----------------------+
|  1 | SIMPLE      | student | range | sname         | sname | 62      | NULL |    1 | Using index condition |
+----+-------------+---------+-------+---------------+-------+---------+------+------+-----------------------+
1 row in set (0.01 sec)
  • %在两边的时候(全表扫描):
mysql> explain select * from student where sname like "%国%";
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

如果需求是必须要百分号在前面,可参考:MySQL模糊查询用法大全(正则、通配符、内置函数等)

七、limit分页的优化

这个表存了1千多万条数据:

mysql> select count(*) from fa_ceshi;
+----------+
| count(*) |
+----------+
| 11651681 |
+----------+
1 row in set (2.43 sec)

当看第1000000页时,查询效率很低下:

mysql> select * from fa_ceshi limit 1000000,20;
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 1000001 | 2020-05-10 00:04:20 |
| 1000002 | 2020-05-10 00:04:20 |
| 1000003 | 2020-05-10 00:04:21 |
| 1000004 | 2020-05-10 00:04:21 |
| 1000005 | 2020-05-10 00:04:21 |
| 1000006 | 2020-05-10 00:04:21 |
| 1000007 | 2020-05-10 00:04:21 |
| 1000008 | 2020-05-10 00:04:21 |
| 1000009 | 2020-05-10 00:04:21 |
| 1000010 | 2020-05-10 00:04:21 |
| 1000011 | 2020-05-10 00:04:21 |
| 1000012 | 2020-05-10 00:04:22 |
| 1000013 | 2020-05-10 00:04:22 |
| 1000014 | 2020-05-10 00:04:22 |
| 1000015 | 2020-05-10 00:04:22 |
| 1000016 | 2020-05-10 00:04:22 |
| 1000017 | 2020-05-10 00:04:22 |
| 1000018 | 2020-05-10 00:04:22 |
| 1000019 | 2020-05-10 00:04:22 |
| 1000020 | 2020-05-10 00:04:22 |
+---------+---------------------+
20 rows in set (0.26 sec)

这时候mysql并不是跳过1000000偏移量去直接取后面的数据,而是需要查询1000000条数据然后只返回最后的20条,前面1000000条将会抛弃,那自然而然分页越往后效率越低。

《高性能mysql》:如果所有的页面被访问的频率都相同,那么这样的查询平均需要访问半个表的数据。要优化这种查询,要么是在页面限制分页数量,要么是优化大偏移量的性能。

  • 优化方案一:
mysql> select * from fa_ceshi where id>1000000 limit 20;
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 1000001 | 2020-05-10 00:04:20 |
| 1000002 | 2020-05-10 00:04:20 |
| 1000003 | 2020-05-10 00:04:21 |
| 1000004 | 2020-05-10 00:04:21 |
| 1000005 | 2020-05-10 00:04:21 |
| 1000006 | 2020-05-10 00:04:21 |
| 1000007 | 2020-05-10 00:04:21 |
| 1000008 | 2020-05-10 00:04:21 |
| 1000009 | 2020-05-10 00:04:21 |
| 1000010 | 2020-05-10 00:04:21 |
| 1000011 | 2020-05-10 00:04:21 |
| 1000012 | 2020-05-10 00:04:22 |
| 1000013 | 2020-05-10 00:04:22 |
| 1000014 | 2020-05-10 00:04:22 |
| 1000015 | 2020-05-10 00:04:22 |
| 1000016 | 2020-05-10 00:04:22 |
| 1000017 | 2020-05-10 00:04:22 |
| 1000018 | 2020-05-10 00:04:22 |
| 1000019 | 2020-05-10 00:04:22 |
| 1000020 | 2020-05-10 00:04:22 |
+---------+---------------------+
20 rows in set (0.00 sec)

用id返回最大查询记录(偏移量),这样可以跳过偏移量,从而提升不少效率。

  • 优化方案二(其中id为索引):
mysql> select * from fa_ceshi where id>1000000 order by id limit 20;
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 1000001 | 2020-05-10 00:04:20 |
| 1000002 | 2020-05-10 00:04:20 |
| 1000003 | 2020-05-10 00:04:21 |
| 1000004 | 2020-05-10 00:04:21 |
| 1000005 | 2020-05-10 00:04:21 |
| 1000006 | 2020-05-10 00:04:21 |
| 1000007 | 2020-05-10 00:04:21 |
| 1000008 | 2020-05-10 00:04:21 |
| 1000009 | 2020-05-10 00:04:21 |
| 1000010 | 2020-05-10 00:04:21 |
| 1000011 | 2020-05-10 00:04:21 |
| 1000012 | 2020-05-10 00:04:22 |
| 1000013 | 2020-05-10 00:04:22 |
| 1000014 | 2020-05-10 00:04:22 |
| 1000015 | 2020-05-10 00:04:22 |
| 1000016 | 2020-05-10 00:04:22 |
| 1000017 | 2020-05-10 00:04:22 |
| 1000018 | 2020-05-10 00:04:22 |
| 1000019 | 2020-05-10 00:04:22 |
| 1000020 | 2020-05-10 00:04:22 |
+---------+---------------------+
20 rows in set (0.00 sec)
mysql> explain select * from fa_ceshi where id>1000000 order by id limit 20;
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | fa_ceshi | range | PRIMARY,id    | PRIMARY | 4       | NULL | 5138205 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
1 row in set (0.00 sec)

用id返回最大查询记录(偏移量),跳过偏移量,在配合order by+索引,提高查询效率。

  • 优化方案三(其中id为索引):
mysql> select * from fa_ceshi where id BETWEEN 10000000 AND 10000020 order by id;
+----------+---------------------+
| id       | times               |
+----------+---------------------+
| 10000000 | 2020-05-21 15:16:50 |
| 10000001 | 2020-05-21 15:16:50 |
| 10000002 | 2020-05-21 15:16:50 |
| 10000003 | 2020-05-21 15:16:50 |
| 10000004 | 2020-05-21 15:16:50 |
| 10000005 | 2020-05-21 15:16:50 |
| 10000006 | 2020-05-21 15:16:50 |
| 10000007 | 2020-05-21 15:16:50 |
| 10000008 | 2020-05-21 15:16:50 |
| 10000009 | 2020-05-21 15:16:51 |
| 10000010 | 2020-05-21 15:16:51 |
| 10000011 | 2020-05-21 15:16:51 |
| 10000012 | 2020-05-21 15:16:51 |
| 10000013 | 2020-05-21 15:16:51 |
| 10000014 | 2020-05-21 15:16:51 |
| 10000015 | 2020-05-21 15:16:51 |
| 10000016 | 2020-05-21 15:16:51 |
| 10000017 | 2020-05-21 15:16:51 |
| 10000018 | 2020-05-21 15:16:52 |
| 10000019 | 2020-05-21 15:16:52 |
| 10000020 | 2020-05-21 15:16:52 |
+----------+---------------------+
21 rows in set (0.00 sec)

mysql> explain select * from fa_ceshi where id BETWEEN 10000000 AND 10000020 order by id;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | fa_ceshi | range | PRIMARY,id    | PRIMARY | 4       | NULL |   21 | Using where |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

《高性能mysql》:有时候也可以将limit转化为已知位置的查询,让Mysql通过范围扫描获得到对应的结果,例如,id为索引,并且预先列出了边界值。

  • 优化方案四:

基本需求用户是查不到这么多页的,如果产品经理硬要让你查那么多页,这和让你根据手机壳改变颜色有什么区别(貌似有人实现了)?那就干一架吧!
在这里插入图片描述

八、判断是否为NULL用is

先看下表内数据:

mysql> select * from student;
+----+-----------+-------+-----+---------------------+
| id | sname     | age   | cid | times               |
+----+-----------+-------+-----+---------------------+
|  2 | 李关亮    | 21.00 |   2 | 2020-07-16 18:56:03 |
|  3 | 李腾      | 21.00 |   3 | 2020-07-16 18:56:07 |
|  4 | 黄成志    | 23.00 |   4 | 2020-07-29 18:56:11 |
|  5 | 张国栋    | 21.00 |   2 | 2020-07-31 18:56:15 |
|  8 | NULL      | 54.00 |   1 | 2020-07-02 20:54:03 |
+----+-----------+-------+-----+---------------------+
5 rows in set (0.00 sec)
  • =号查询:
mysql> select * from student where sname=null;
Empty set (0.00 sec)
  • is null查询
mysql> select * from student where sname is null;
+----+-------+-------+-----+---------------------+
| id | sname | age   | cid | times               |
+----+-------+-------+-----+---------------------+
|  8 | NULL  | 54.00 |   1 | 2020-07-02 20:54:03 |
+----+-------+-------+-----+---------------------+
1 row in set (0.00 sec)
  • 查询!=李关亮的所有数据:
mysql> select * from student where sname!="李关亮";
+----+-----------+-------+-----+---------------------+
| id | sname     | age   | cid | times               |
+----+-----------+-------+-----+---------------------+
|  3 | 李腾      | 21.00 |   3 | 2020-07-16 18:56:07 |
|  4 | 黄成志    | 23.00 |   4 | 2020-07-29 18:56:11 |
|  5 | 张国栋    | 21.00 |   2 | 2020-07-31 18:56:15 |
+----+-----------+-------+-----+---------------------+
3 rows in set (0.00 sec)

发现那条空数据没有查出。

mysql> select * from student where sname!="李关亮" or sname is null;
+----+-----------+-------+-----+---------------------+
| id | sname     | age   | cid | times               |
+----+-----------+-------+-----+---------------------+
|  3 | 李腾      | 21.00 |   3 | 2020-07-16 18:56:07 |
|  4 | 黄成志    | 23.00 |   4 | 2020-07-29 18:56:11 |
|  5 | 张国栋    | 21.00 |   2 | 2020-07-31 18:56:15 |
|  8 | NULL      | 54.00 |   1 | 2020-07-02 20:54:03 |
+----+-----------+-------+-----+---------------------+
4 rows in set (0.00 sec)

再加个条件,或者sname is null即可查出。

mysql> explain select * from student where sname is not null;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | sname         | NULL | NULL    | NULL |    4 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

is not null导致索引失效。

以上实例都是null引起的一系列问题,最根本的解决方案禁止设置字段为null,给个默认值也可

九、反向查询导致索引失效

其中id为唯一索引

mysql> explain select * from student where id!=2;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | PRIMARY,id    | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

不等于导致索引失效,全表扫描

其中cid为单列索引

mysql> explain select * from student where cid<>2;
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | student | ALL  | cid           | NULL | NULL    | NULL |    4 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

不等于导致索引失效,全表扫描。

十、 在只取一条并已知查询会有重复数据的情况下用limit1提高查询效率

先看下表内数据量:

mysql> select count(*) from fa_ceshi;
+----------+
| count(*) |
+----------+
| 11651681 |
+----------+
1 row in set (2.08 sec)

查询某个时间点(耗时挺长):

mysql> select * from fa_ceshi where times="2020-05-15 04:09:24";
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 4999657 | 2020-05-15 04:09:24 |
| 4999658 | 2020-05-15 04:09:24 |
| 4999659 | 2020-05-15 04:09:24 |
| 4999660 | 2020-05-15 04:09:24 |
| 4999661 | 2020-05-15 04:09:24 |
| 4999662 | 2020-05-15 04:09:24 |
| 4999663 | 2020-05-15 04:09:24 |
| 4999664 | 2020-05-15 04:09:24 |
| 4999665 | 2020-05-15 04:09:24 |
+---------+---------------------+
9 rows in set (4.10 sec)

加上limit 1后(速度提升一大半):

mysql> select * from fa_ceshi where times="2020-05-15 04:09:24" limit 1;
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 4999657 | 2020-05-15 04:09:24 |
+---------+---------------------+
1 row in set (1.77 sec)

设置times为索引的情况下(有无limit差别不大):

mysql> select * from fa_ceshi where times="2020-05-15 04:09:24";
+---------+---------------------+
| id      | times               |
+---------+---------------------+
| 4999657 | 2020-05-15 04:09:24 |
| 4999658 | 2020-05-15 04:09:24 |
| 4999659 | 2020-05-15 04:09:24 |
| 4999660 | 2020-05-15 04:09:24 |
| 4999661 | 2020-05-15 04:09:24 |
| 4999662 | 2020-05-15 04:09:24 |
| 4999663 | 2020-05-15 04:09:24 |
| 4999664 | 2020-05-15 04:09:24 |
| 4999665 | 2020-05-15 04:09:24 |
+---------+---------------------+
9 rows in set (0.00 sec)

在有索引的情况下就没必要使用limit,使用limit的目的就是为了防止全表扫描。

十一、减少重复索引和冗余索引

11.1 重复索引

重复索引是指在相同的列上按照相同的顺序创建的相同类型的索引。

例如:创建一个表,设置id为主键、唯一约束,又将id设置为单列索引。

《高性能mysql》:事实上,mysql的唯一限制和主键限制都是通过索引实现的,因此,上面这个例子就等于创建了三个重复的索引。通过没有理由这样做,除非是在同一列上创建不同的类型的索引来满足不同的需求。

11.2 冗余索引

如果创建了联合索引(A,B),在去创建索引A,那么索引A就属于冗余索引,因为A在联合索引(A,B)最左侧,能作为索引A来使用(这种冗余只是对B-tree索引来说的)。

但是如果在创建索引(B,A)或者索引B,则不是冗余索引,因为B不在索引(A,B)的最左侧。

另外,其他不同类型的索引(例如哈希索引或全文索引)也不会是B-tree索引的冗余索引,而无论覆盖索引的列是什么。

在这里插入图片描述

十二、where条件与order by一起使用

12.1 where条件字段与order by条件字段不一致的情况

其中,sname为单列索引,age为单列索引。

mysql> explain select * from student where sname="李关亮" order by age;
+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------------------------------------------+
| id | select_type | table   | type | possible_keys | key   | key_len | ref   | rows | Extra                                              |
+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------------------------------------------+
|  1 | SIMPLE      | student | ref  | sname         | sname | 63      | const |    1 | Using index condition; Using where; Using filesort |
+----+-------------+---------+------+---------------+-------+---------+-------+------+----------------------------------------------------+
1 row in set (0.00 sec)

发现出现了Using filesort。

关于FileSort排序:一般在内存中进行排序,占用CPU较多。如果待排结果较大,会产生临时文件I/O到磁盘进行排序,效率较低。

进行改进,将sname,age设置为联合索引:

mysql> explain select * from student where sname="李关亮" order by age;
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+------------------------------------+
| id | select_type | table   | type | possible_keys   | key             | key_len | ref   | rows | Extra                              |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+------------------------------------+
|  1 | SIMPLE      | student | ref  | index_sname_age | index_sname_age | 63      | const |    1 | Using index condition; Using where |
+----+-------------+---------+------+-----------------+-----------------+---------+-------+------+------------------------------------+
1 row in set (0.00 sec)

发现FileSort排序已经消失,从而实现了索引覆盖。因此,使用联合索引在where条件字段与order by条件字段不一致的情况下能够提高效率,使用索引扫描。

12.2 where条件字段与order by条件字段一致的情况

其中,sname为单列索引,不使用where条件直接order by进行排序:

mysql> explain select * from student order by sname;
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | student | ALL  | NULL          | NULL | NULL    | NULL |    4 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

直接全盘扫描,加上where条件后,实现了覆盖索引:

mysql> explain select * from student where sname="李关亮" order by sname;
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
| id | select_type | table   | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | student | ref  | sname         | sname | 63      | const |    1 | Using index condition |
+----+-------------+---------+------+---------------+-------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)

总结:加WHERE子句可避免全表扫描。使用ORDER BY子句提高排序速度。


十三、在多表关联查询时,两大原则

13.1 让小表在前,大表在后

第一张表会涉及到全表扫描,先扫小表,在扫描后面的大表,节省运算,以此提高效率。

13.2 让重复关联键少的表在前,重复关键键多的在后

关联左侧的表每有1条重复的关联键时底层就会多1次运算处理。具体解析参考:SQL Join连接大小表在前在后的重要性(小表在前提高执行效率)

十四、总结

如有错误,敬请更正。衷心感谢,不胜荣幸。

关于explain:mysql explain用法和结果的含义

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章