一、前言
本文主要解釋以下幾個問題:
1.什么是回表查詢?
2.什么是索引覆蓋?
3.如何實現索引覆蓋?
4.那些場景可以利用索引覆蓋優化sql?
本文實驗基於8.0版本innodb
二、回表查詢
1.建表
CREATE TABLE `user` ( `id` int(11) NOT NULL, `name` varchar(20) DEFAULT NULL, `sex` varchar(5) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.分析下面兩個查詢
explain select id,name from user where name='lihua'
explain select id,name,sex from user where name='lihua'
通過explain可以看出當我們增加了sex字段做查詢時extra為NULL,意味着本次查詢進行了“回表”操作,我們知道innodb采用B+樹聚集索引,主鍵和數據綁定在一起,主鍵索引b+樹的葉子節點存儲了數據信息,而普通索引葉子節點存儲的是主鍵值。因此,我們可以得知當通過普通索引查詢時無法直接定位行記錄,通常情況下,需要掃描兩遍索引樹。
select * from user where name='lisi';
還是以現有表舉例,它是如何執行的?
1)先掃描name索引樹,找到主鍵值id=5。
2)再掃描主鍵索引,找到對應行。
這就是“回表查詢”,先定位主鍵值,再通過主鍵值定位行記錄,性能上較之直接查詢索引樹定位行記錄更慢。
三、覆蓋索引
1.什么是覆蓋索引?
1)只需要在一棵索引樹上就可以獲取sql所需所有的列數據,不需要回表,較之回表速度要更快。
2)explain輸出結果extra字段為Using index時,觸發了索引覆蓋。
2.如何實現覆蓋索引?
辦法:將被查詢的字段建立到聯合索引中
接我們上面的例子,因為我們對name字段建立了普通索引,且基於name的索引葉子節點存有主鍵id值,因此滿足了在一顆索引樹上獲得sql所需的所有列數據這一條件,通過觀察extra也可發現是Using Index無需回表。
select id,name from user where name='lihua'
觀察第二個例子,因為sex並沒有被建立到聯合索引中,且在name索引樹上也無法直接獲得,因此只能通過回表查詢,兩次掃描索引樹,效率更低。
explain select id,name,sex from user where name='lihua'
針對第二個例子,我們將sex建立到聯合索引中去。
ALTER TABLE `test`.`user` DROP INDEX `name`, ADD INDEX `idx_name_sex`(`name`, `sex`);
再次執行查詢,可以看到extra已經變為Using index了,命中了索引覆蓋無需回表。
四、使用索引覆蓋的場景
1.count查詢優化
先對表做修改增加一個address字段,直接count(address)全表查詢,可以發現extra為NULL,沒有利用到索引覆蓋。
ALTER TABLE `test`.`user` ADD COLUMN `address` varchar(255) NULL AFTER `sex`;
explain select count(address) from user
現對address加索引,再做查詢,可以觀察到extra變為Using index使用了索引覆蓋。
ALTER TABLE `test`.`user` DROP INDEX `idx_name_sex`, ADD INDEX `idx_name_sex`(`name`, `sex`, `address`) USING BTREE;
2.列查詢回表優化,上述例二建立聯合索引解決。
3.分頁查詢,也可建立聯合索引解決,針對下例可以建立(name,sex)覆蓋索引。
select id,name,sex ... order by name limit 500,100;
五、結語
本文主要記錄mysql學習過程,如有錯誤請指正。