提高性能的幾條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用法和結果的含義

在這裏插入圖片描述

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