此篇由於篇幅的關係為前文的連貫。
如沒有看到前文請先去看看再來唷~
前情提要-我們前面為了方便解釋,建了個表還有索引
mysql> create table single_table(
-> id int not null auto_increment,
-> key1 varchar(10),
-> key2 int,
-> key3 varchar(100),
-> key_part1 varchar(100),
-> key_part2 varchar(100),
-> key_part3 varchar(100),
-> common_field varchar(100),
-> primary key (id),
-> key idx_key1 (key1),
-> unique key uk_key2 (key2),
-> key idx_key3 (key3),
-> key idx_key_part(key_part1, key_part2, key_part3)
-> ) engine=InnoDB charset=utf8;
Query OK, 0 rows affected, 1 warning (0.05 sec)
為id列建立的聚簇索引
為key1列建立的idx_key1二級索引
為key2列建立的uk_key2二級索引,且是唯一
為key3列建立的idx_key3二級索引
為key_part1、key_part2、key_part3列建立的idx_key_part二級索引,這也是個聯合索引
今兒先繼續說明掃描區間與邊界條件
使用聯合索引執行查詢時對應的掃描區間
這裏需要多幾個例子來說明,比較能夠理解
1.
select * from single_table where key_part1 = 'a' and key_part2 = 'b' and key_part3 = 'c';
聯合索引idx_key_part是先照key_part1的值小到大排序,key_part1的值相等再照key_part2的值小到大排序,最後key_part1跟key_part2的值都相等,再照key_part3的值小到大排序。所以我們可以知道符合key_part1 = 'a' and key_part2 = 'b' and key_part3 = 'c'條件的紀錄必定相鄰。
因此搜尋的流程就是先快速定位到第一筆符合key_part1 = 'a' and key_part2 = 'b' and key_part3 = 'c'的紀錄,然後沿著單向鏈結串列往後掃描,直到紀錄不符合此條件為止。這個敘述的掃描區間就是[('a','b','c'),('a','b','c')],形成的邊界條件就是key_part1 = 'a' and key_part2 = 'b' and key_part3 = 'c'。
select * from single_table where key_part2 = 'a';
由於聯合索引idx_key_part不是直接照key_part2的值來排序的,所以符合key_part2 = 'a'的紀錄可能並不相鄰,也就表示我們不能透過key_part2 = 'a'來減少需要掃描的紀錄數量。在這樣的情況下不用索引idx_key_part來查詢反而是比較快的。
select * from single_table where key_part1 = 'a' and key_part3 = 'c';
由於聯合索引idx_key_part是先照key_part1的值小到大排序,在key_part1的值相等再按照key_part2的值小到大排序,並不是照key_part2的值小到大來排序的,也就是說我們不能根據key_part3 = 'c'來進一步減少需掃描的紀錄。
如果我們依然使用聯合索引idx_key_part來執行查詢,可以先定位到符合key_part1 = 'a'的第一筆紀錄,然後沿著紀錄的單向鏈結串列往後掃,直到某筆紀錄不符合key_part1 = 'a'為止,所以其對應的掃描區間其實是['a','a'],形成該區間的邊界條件是key_part1 = 'a',與key_part3 = 'c'無關。
(額外補充說明:針對得到的每筆二級索引紀錄,如果沒有開啟索引條件下推特性,則必須先執行回表操作,在得到完整的使用者紀錄後再判斷key_part3 = 'c'條件是否成立。如果開啟了索引條件下推特性,可以立即判斷二級索引紀錄是否符合key_part3 = 'c'條件,符合再執行回表操作,不符合則不回表直接跳到下一筆二級索引紀錄。索引條件下推特性是在MySQL5.6中引入的,預設為開啟唷)
select * from single_table where key_part1 < 'b' and key_part2 = 'a';
由於聯合索引idx_key_part是先照key_part1的值小到大排序,所以符合key_part1 < 'b'條件的紀錄肯定相鄰。但是對符合key_part1 < 'b'條件的紀錄並不是直接照key_part2列來排序的(我們前面所說的必須是在key_part1的值相等時,再按照key_part2的值小到大排序,而現在key_part1 < 'b'條件表示key_part1的值並不一定會相等唷,因此可說是不直接照key_part2列來排序)。
也就是說,我們不能根據key_part2 = 'a'來進一步減少需要掃描的紀錄。因此這個敘述對應的掃描區間其實是[-無限大,'b'],形成該區間的邊界條件是key_part1 < 'b',與key_part2 = 'a'無關。
select * from single_table where key_part1 <= 'b' and key_part2 = 'a';
很顯然這個查詢跟第4個查詢很像,差別只在於key_part1 = 'b',兩者的差異也在於對符合key_part1 = 'b'的紀錄來說,是按照key_part2的值來排序的,所以可以透過key_part2 = 'a'來減少須掃描的數量(只要掃描到不符合key_part1 = 'b'跟key_part2 = 'a'的第一筆紀錄時,就可以結束掃描了),如用聯合索引idx_key_part來執行這個查詢,形成的掃描區間是[(-無限大,+無限大),('b','a')],形成該區間的邊界條件就是key_part1 <= 'b' and key_part2 = 'a'。與第4個查詢相比就是一個與key_part2 = 'a'有關,一個無關。
燦樹來了休息一下吧~