文章目录
mysql版本
:
mysql> select version();
+------------+
| version() |
+------------+
| 5.6.44-log |
+------------+
1 row in set (0.00 sec)
一、尽量不使用select * from查出所有列
有时候一个表字段太多,有的字段内容还很大,查询用到的列,节省资源、减少网络开销。
二、避免使用or进行查询
其中
sname
和age
字段均设置成为了索引
- 使用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字段类型更换为int
、tinyint
等这种数字类型的时候:
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)
就又用到了索引。
四、建立联合索引要遵循最左侧匹配原则
其中
sname
、age
、cid
依次顺序为联合索引
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用法和结果的含义