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用法和結果的含義