復(fù)合索引(又稱為聯(lián)合索引),是在多個(gè)列上創(chuàng)建的索引。創(chuàng)建復(fù)合索引最重要的是列順序的選擇,這關(guān)系到索引能否使用上,或者影響多少個(gè)謂詞條件能使用上索引。復(fù)合索引的使用遵循最左匹配原則,只有索引左邊的列匹配到,后面的列才能繼續(xù)匹配。本文主要探究復(fù)合索引的創(chuàng)建順序與使用情況。
在單個(gè)列上創(chuàng)建的索引我們稱為單列索引,在2個(gè)以上的列上創(chuàng)建的索引稱為復(fù)合索引。在單個(gè)列上創(chuàng)建索引相對簡單,通常只需要考慮列的選擇率即可,選擇性越好,代表數(shù)據(jù)越分散,創(chuàng)建出來的索引性能也就更好。通常,某列選擇率的計(jì)算公式為:
selectivity = 施加謂詞條件后返回的記錄數(shù) / 未施加謂詞條件后返回的記錄數(shù)
可選擇率的取值范圍是(0,1],值越小,代表選擇性越好。
對于復(fù)合索引(又稱為聯(lián)合索引),是在多個(gè)列上創(chuàng)建的索引。創(chuàng)建復(fù)合索引最重要的是列順序的選擇,這關(guān)系到索引能否使用上,或者影響多少個(gè)謂詞條件能使用上索引。復(fù)合索引的使用遵循最左匹配原則,只有索引左邊的列匹配到,后面的列才能繼續(xù)匹配。
復(fù)合索引遵循最左匹配原則,只有索引中最左列匹配到,下一列才有可能被匹配。如果左邊列使用的是非等值查詢,則索引右邊的列將不會(huì)被查詢使用,也不會(huì)被排序使用。
實(shí)驗(yàn):哪些情況下會(huì)使用到復(fù)合索引
復(fù)合索引中的哪些字段被使用到了,是我們非常關(guān)心的問題。網(wǎng)絡(luò)上一個(gè)經(jīng)典的例子:
-- 創(chuàng)建測試表 CREATE TABLE t1( c1 CHAR(1) not null, c2 CHAR(1) not null, c3 CHAR(1) not null, c4 CHAR(1) not null, c5 CHAR(1) not null )ENGINE innodb CHARSET UTF8; -- 添加索引 alter table t1 add index idx_c1234(c1,c2,c3,c4); --插入測試數(shù)據(jù) insert into t1 values('1','1','1','1','1'),('2','2','2','2','2'), ('3','3','3','3','3'),('4','4','4','4','4'),('5','5','5','5','5');
需要探索下面哪些查詢語句使用到了索引idx_c1234,以及使用到了索引的哪些字段?
(A) where c1=? and c2=? and c4>? and c3=?
(B) where c1=? and c2=? and c4=? order by c3
(C) where c1=? and c4=? group by c3,c2
(D) where c1=? and c5=? order by c2,c3
(E) where c1=? and c2=? and c5=? order by c2,c3
(F) where c1>? and c2=? and c4>? and c3=?
A選項(xiàng):
mysql> explain select c1,c2,c3,c4,c5 from t1 where c1='2' and c2='2' and c4>'1' and c3='2'; +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | idx_c1234 | idx_c1234 | 12 | NULL | 1 | 100.00 | Using index condition | +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
使用的索引長度為12,代表4個(gè)字段都使用了索引。由于c1、c2、c3都是等值查詢,所以后面的c4列也可以用上。
注:utf8編碼,一個(gè)索引長度為3,這里12代表4個(gè)字段都用到該索引。
B選項(xiàng):
mysql> explain select c1,c2,c3,c4,c5 from t1 where c1='2' and c2='2' and c4='2' order by c3; +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 6 | const,const | 1 | 20.00 | Using index condition | +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+-----------------------+
使用的索引長度為6,代表2個(gè)字段使用了索引。根據(jù)最左使用原則,c1、c2使用了索引。因?yàn)椴樵冎袥]有c3謂詞條件,所以索引值使用到c2后就發(fā)生了中斷,導(dǎo)致只使用了c1、c2列。這里SQL使用了order by排序,但是在執(zhí)行計(jì)劃Extra部分未有filesort關(guān)鍵字,說明在索引中按照c3字段順序讀取數(shù)據(jù)即可。
這里特別留意,雖然索引中的c3字段沒有放在索引的最后,但是確實(shí)使用到了索引中c2字段的有序特性,因?yàn)閳?zhí)行計(jì)劃的Extra部分未出現(xiàn)"fileasort"關(guān)鍵字。這是為什么呢?這里用到了MySQL5.6版本引入的Index Condition Pushdown (ICP) 優(yōu)化。其核心思想是使用索引中的字段做數(shù)據(jù)過濾。我們來整理一下不使用ICP和使用ICP的區(qū)別:
如果沒有使用ICP優(yōu)化,其SQL執(zhí)行步驟為:
1.使用索引列c1,c2獲取滿足條件的行數(shù)據(jù)。where c1='2' and c2='2'
2.回表查詢數(shù)據(jù),使用where c4='2'來過濾數(shù)據(jù)
3.對數(shù)據(jù)排序輸出
如果使用了ICP優(yōu)化,其SQL執(zhí)行步驟為:
1.使用索引列c1,c2獲取滿足條件的行數(shù)據(jù)。where c1='2' and c2='2'
2.在索引中使用where c4='2'來過濾數(shù)據(jù)
3.因?yàn)閿?shù)據(jù)有序,直接按順序取出滿足條件的數(shù)據(jù)
C選項(xiàng):
mysql> explain select c2,c3 from t1 where c1='2' and c4='2' group by c3,c2; +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 3 | const | 2 | 14.29 | Using where; Using index; Using temporary; Using filesort | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+-----------------------------------------------------------+
使用的索引長度為3,代表1個(gè)字段使用了索引。根據(jù)最左使用原則,c1使用了索引。因?yàn)椴樵冎袥]有c2謂詞條件,所以索引值使用到c1后就發(fā)生了中斷,導(dǎo)致只使用了c1列。該SQL執(zhí)行過程為:
1.在c1列使用索引找到c1='2'的所有行,然后回表使用c4='2'過濾掉不匹配的數(shù)據(jù)
2.根據(jù)上一步的結(jié)果,對結(jié)果中的c3,c2聯(lián)合排序,以便于得到連續(xù)變化的數(shù)據(jù),同時(shí)在數(shù)據(jù)庫內(nèi)部創(chuàng)建臨時(shí)表,用于存儲(chǔ)group by的結(jié)果。
C選項(xiàng)擴(kuò)展:
mysql> explain select c2,c3 from t1 where c1='2' and c4='2' group by c2,c3; +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 3 | const | 2 | 14.29 | Using where; Using index | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+--------------------------+
使用的索引長度為3,代表1個(gè)字段使用了索引。根據(jù)最左使用原則,c1使用了索引。
D選項(xiàng):
mysql> explain select c2,c3 from t1 where c1='2' and c5='2' order by c2,c3; +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 3 | const | 2 | 14.29 | Using index condition; Using where | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+------------------------------------+
使用的索引長度為3,代表1個(gè)字段都使用了索引。根據(jù)最左使用原則,c1使用了索引。因?yàn)椴樵冎袥]有c2謂詞條件,所以索引值使用到c1后就發(fā)生了中斷,導(dǎo)致只使用了c1列。
D選項(xiàng)擴(kuò)展:
mysql> explain select c2,c3 from t1 where c1='2' and c5='2' order by c3,c2; +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+----------------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 3 | const | 2 | 14.29 | Using index condition; Using where; Using filesort | +----+-------------+-------+------------+------+---------------+-----------+---------+-------+------+----------+----------------------------------------------------+
使用的索引長度為3,代表1個(gè)字段都使用了索引。根據(jù)最左使用原則,c1使用了索引。因?yàn)椴樵冎袥]有c2謂詞條件,所以索引值使用到c1后就發(fā)生了中斷,導(dǎo)致只使用了c1列。
E選項(xiàng):
mysql> explain select c1,c2,c3,c4,c5 from t1 where c1='2' and c2='2' and c5='2' order by c2,c3; +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+------------------------------------+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1234 | idx_c1234 | 6 | const,const | 2 | 14.29 | Using index condition; Using where | +----+-------------+-------+------------+------+---------------+-----------+---------+-------------+------+----------+------------------------------------+
使用的索引長度為6,代表2個(gè)字段都使用了索引。根據(jù)最左使用原則,c1、c2使用了索引。這里SQL使用了order by排序,但是在執(zhí)行計(jì)劃Extra部分未有filesort關(guān)鍵字,說明在索引中按照c3字段順序讀取數(shù)據(jù)即可(c2是常量)。
F選項(xiàng):
mysql> explain select c1,c2,c3,c4,c5 from t1 where c1>'4' and c2='2' and c3='2' and c4='1'; +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | t1 | NULL | range | idx_c1234 | idx_c1234 | 3 | NULL | 1 | 20.00 | Using index condition | +----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+
使用的索引長度為3,代表1個(gè)字段都使用了索引。根據(jù)最左使用原則,c1使用了索引。這里c1使用了不等值查詢,導(dǎo)致后面的c2查詢無法使用索引。該案例非常值得警惕,謂詞條件中含有等值查詢和范圍查詢時(shí),如果范圍查詢在索引前面,則等值查詢將無法使用索引;如果等值查詢在前面,范圍查詢在后面,則都可以使用到索引。
復(fù)合索引創(chuàng)建的難點(diǎn)在于字段順序選擇,我的觀點(diǎn)如下:
此外,《阿里巴巴Java開發(fā)手冊-2020最新嵩山版》中有幾個(gè)關(guān)于復(fù)合索引的規(guī)約,我們可以看一下:
1.如果有order by的場景,請注意利用索引的有序性。order by后的字段是組合索引的一部分,并且放在組合索引的最后,避免出現(xiàn)filesort的情況,影響查詢性能。
正例:where a=? b=? order by c; 索引a_b_c
反例:索引如果存在范圍查詢,那么索引有序性將無法使用。如:where a>10 order by b; 索引a_b無法排序。
2.建復(fù)合索引的時(shí)候,區(qū)分度最高的在最左邊,如果where a=? and b=?,a列的值幾乎接近唯一值,那么只需建單列索引idx_a即可。
說明:存在等號和非等號混合判斷條件時(shí),在建索引時(shí),請把等號條件的列前置。如:where c>? and d=?,那么即使c的區(qū)分度
更高,也必須把d放在索引的最前列,即創(chuàng)建索引idx_d_c。
實(shí)驗(yàn):應(yīng)該如何創(chuàng)建復(fù)合索引
在有的文檔里面講到過復(fù)合索引的創(chuàng)建規(guī)則:ESR原則:精確(Equal)匹配的字段放在最前面,排序(Sort)條件放中間,范圍(Range)匹配的字段放在最后面。接下來我們來探索一下該方法是否正確。
例子:存在員工表employees
mysql> show create table employees; +-----------+------------------------------- | Table | Create Table +-----------+------------------------------------- | employees | CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 | +-----------+------------------------------------- -- 數(shù)據(jù)量約30萬行 mysql> select count(*) from employees; +----------+ | count(*) | +----------+ | 300024 | +----------+
現(xiàn)在需要查詢1998年后入職的first_name為"Ebbe"員工,并按照出生日期升序排序。
其SQL語句如下:
select emp_no,birth_date,first_name,last_name,gender,hire_date from employees where hire_date >= '1998-01-01' and first_name = 'Ebbe' order by birth_date;
為了優(yōu)化該SQL語句的性能,需要在表上創(chuàng)建索引,為了保證where與order by都使用到索引,決定創(chuàng)建復(fù)合索引,有如下創(chuàng)建順序:
(A)hire_date,first_name,birth_date
(B)hire_date,birth_date,first_name
(C)first_name,hire_date,birth_date
(D)first_name,birth_date,hire_date
(E)birth_date,first_name,hire_date
(F)birth_date,hire_date,first_name
確認(rèn)哪種順序創(chuàng)建索引是最優(yōu)的。
Note:
1.date類型占3個(gè)字節(jié)的空間,hire_date和 birth_date都占用3個(gè)字節(jié)的空間。
2.first_name是變長字段,多使用2個(gè)字節(jié),如果允許為NULL值,還需多使用1個(gè)字節(jié),占用16個(gè)字節(jié)
A選項(xiàng):hire_date,first_name,birth_date
create index idx_a on employees(hire_date,first_name,birth_date);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_a | idx_a | 19 | NULL | 5678 | 10.00 | Using index condition; Using filesort | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+
這里key_len長度為19,令人不解,hire_date是非等值查詢,理論上key_len應(yīng)該為3,通過使用MySQL workbench查看執(zhí)行計(jì)劃,也可以發(fā)現(xiàn)索引只使用了hire_date列(如下圖)。為什么會(huì)是19而不是3呢?實(shí)在令人費(fèi)解,思考了好久也沒有想明白,如有知道,望各位大神不吝解答。
B選項(xiàng):hire_date,birth_date,first_name
為避免干擾,刪除上面創(chuàng)建的索引idx_a,然后創(chuàng)建idx_b。
create index idx_b on employees(hire_date,birth_date,first_name);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_b | idx_b | 3 | NULL | 5682 | 10.00 | Using index condition; Using filesort | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+
這里key_len長度為3,hire_date是非等值查詢,導(dǎo)致后面的索引列無法使用到。
C選項(xiàng):first_name,hire_date,birth_date
為避免干擾,刪除上面創(chuàng)建的索引idx_b,然后創(chuàng)建idx_c。
create index idx_c on employees(first_name,hire_date,birth_date);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_c | idx_c | 19 | NULL | 5 | 100.00 | Using index condition; Using filesort | +----+-------------+-----------+------------+-------+---------------+-------+---------+------+------+----------+---------------------------------------+
這里key_len長度為19,first_name是等值查詢,可以繼續(xù)使用hire_date列,因?yàn)閔ire_date列是非等值查詢,導(dǎo)致索引無法繼續(xù)使用birth_date。
D選項(xiàng):first_name,birth_date,hire_date
為避免干擾,刪除上面創(chuàng)建的索引idx_c,然后創(chuàng)建idx_d。
create index idx_d on employees(first_name,birth_date,hire_date);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_d | idx_d | 16 | const | 190 | 33.33 | Using index condition | +----+-------------+-----------+------------+------+---------------+-------+---------+-------+------+----------+-----------------------+
這里key_len長度為16,first_name是等值查詢,在謂詞過濾中未使用birth_date,導(dǎo)致只有first_name列使用上索引,但是birth_date列用于排序,上面執(zhí)行計(jì)劃顯示SQL最終并沒有排序,說明數(shù)據(jù)是從索引按照birth_date有序取出的。
E選項(xiàng):birth_date,first_name,hire_date
為避免干擾,刪除上面創(chuàng)建的索引idx_d,然后創(chuàng)建idx_e。
create index idx_e on employees(birth_date,first_name,hire_date);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 299468 | 3.33 | Using where; Using filesort | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
這里未使用到索引,說明排序列放在復(fù)合索引的最前面是無法被使用到的。
F選項(xiàng):birth_date,hire_date,first_name
為避免干擾,刪除上面創(chuàng)建的索引idx_e,然后創(chuàng)建idx_f。
create index idx_f on employees(birth_date,hire_date,first_name);
其執(zhí)行計(jì)劃如下:
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+ | 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 299468 | 3.33 | Using where; Using filesort | +----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-----------------------------+
與E選項(xiàng)一樣,這里未使用到索引,說明排序列放在復(fù)合索引的最前面是無法被使用到的。
通過上面的6個(gè)索引測試,我們發(fā)現(xiàn),等值查詢列和范圍查詢列放在復(fù)合索引前面,復(fù)合索引都能被使用到,只是使用到的列可能不一樣。哪種方式創(chuàng)建索引最好呢?MySQL的查詢優(yōu)化器是基于開銷(cost)來選擇最優(yōu)的執(zhí)行計(jì)劃的,我們不妨來看看上面的6個(gè)索引的執(zhí)行開銷。
索引 開銷cost
---------- ------------
idx_a 8518
idx_b 8524
idx_c 13
idx_d 228
idx_e 78083
idx_f 78083
通過上面的開銷,可以看到:
更進(jìn)一步,idx_c和idx_d如何選擇呢?idx_c使用索引進(jìn)行等值查詢+范圍查詢,然后對數(shù)據(jù)進(jìn)行排序;idx_d使用索引進(jìn)行等值查詢+索引條件下推查詢,然后按照順序直接獲取數(shù)據(jù)。兩種方式各有優(yōu)劣,我們不妨再來看一個(gè)例子:
把上面6個(gè)索引都加到表上,看看如下SQL會(huì)選擇哪個(gè)索引。
mysql> show index from employees; +-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | employees | 0 | PRIMARY | 1 | emp_no | A | 299468 | NULL | NULL | | BTREE | | | | employees | 1 | idx_a | 1 | hire_date | A | 5355 | NULL | NULL | | BTREE | | | | employees | 1 | idx_a | 2 | first_name | A | 290745 | NULL | NULL | | BTREE | | | | employees | 1 | idx_a | 3 | birth_date | A | 299468 | NULL | NULL | | BTREE | | | | employees | 1 | idx_b | 1 | hire_date | A | 6237 | NULL | NULL | | BTREE | | | | employees | 1 | idx_b | 2 | birth_date | A | 297591 | NULL | NULL | | BTREE | | | | employees | 1 | idx_b | 3 | first_name | A | 299468 | NULL | NULL | | BTREE | | | | employees | 1 | idx_c | 1 | first_name | A | 1260 | NULL | NULL | | BTREE | | | | employees | 1 | idx_c | 2 | hire_date | A | 293517 | NULL | NULL | | BTREE | | | | employees | 1 | idx_c | 3 | birth_date | A | 299468 | NULL | NULL | | BTREE | | | | employees | 1 | idx_d | 1 | first_name | A | 1218 | NULL | NULL | | BTREE | | | | employees | 1 | idx_d | 2 | birth_date | A | 294525 | NULL | NULL | | BTREE | | | | employees | 1 | idx_d | 3 | hire_date | A | 298095 | NULL | NULL | | BTREE | | | | employees | 1 | idx_e | 1 | birth_date | A | 4767 | NULL | NULL | | BTREE | | | | employees | 1 | idx_e | 2 | first_name | A | 292761 | NULL | NULL | | BTREE | | | | employees | 1 | idx_e | 3 | hire_date | A | 299468 | NULL | NULL | | BTREE | | | | employees | 1 | idx_f | 1 | birth_date | A | 4767 | NULL | NULL | | BTREE | | | | employees | 1 | idx_f | 2 | hire_date | A | 297864 | NULL | NULL | | BTREE | | | | employees | 1 | idx_f | 3 | first_name | A | 299468 | NULL | NULL | | BTREE | | | +-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
SQL1
mysql> explain select emp_no,birth_date,first_name,last_name,gender,hire_date from employees where hire_date >= '1998-01-01' and first_name = 'Ebbe' order by birth_date; +----+-------------+-----------+------------+-------+-------------------------+-------+---------+------+------+----------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-------------------------+-------+---------+------+------+----------+---------------------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_a,idx_b,idx_c,idx_d | idx_c | 19 | NULL | 5 | 100.00 | Using index condition; Using filesort | +----+-------------+-----------+------------+-------+-------------------------+-------+---------+------+------+----------+---------------------------------------+
這里MySQL自動(dòng)選擇了idx_c,是因?yàn)閒irst_name+hire_date兩個(gè)字段已經(jīng)將數(shù)據(jù)過濾了只有5行,由于數(shù)據(jù)少,排序非??臁7粗?,如果選擇idx_d,則需要先通過first_name字段過濾出符合條件的190行數(shù)據(jù),然后再使用hire_date篩選數(shù)據(jù),工作量較大。
SQL2
mysql> explain select emp_no,birth_date,first_name,last_name,gender,hire_date from employees where hire_date >= '1980-01-01' and first_name = 'Ebbe' order by birth_date; +----+-------------+-----------+------------+------+-------------------------+-------+---------+-------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-------------------------+-------+---------+-------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | ref | idx_a,idx_b,idx_c,idx_d | idx_d | 16 | const | 190 | 50.00 | Using index condition | +----+-------------+-----------+------------+------+-------------------------+-------+---------+-------+------+----------+-----------------------+
如果選擇idx_c,first_name+hire_date兩個(gè)字段通過索引過濾數(shù)據(jù)之后,數(shù)據(jù)量較大,導(dǎo)致排序非常慢。MySQL自動(dòng)選擇了idx_d,通過索引的first_name列過濾數(shù)據(jù),并通過索引條件下推過濾hire_date字段,然后從索引中有序的取出數(shù)據(jù),相對來說,由于使用idx_d無需排序,速度會(huì)更快。
1.復(fù)合索引的創(chuàng)建,如果存在多個(gè)等值查詢,則將選擇性好的列放在最前面,選擇性差的列放在后面;
2.復(fù)合索引的創(chuàng)建,如果涉及到等值查詢和范圍查詢,不管非等值查詢的列的選擇性如何好,等值查詢的字段要放在非等值查詢的前面;
3.復(fù)合索引的創(chuàng)建,如果涉及到等值查詢和范圍查詢和排序(order by、group by),則等值查詢放在索引最前面,范圍查詢和排序哪個(gè)在前,哪個(gè)在后,需要根據(jù)實(shí)際場景決定。如果范圍查詢在前,則無法使用到索引的有序性,需filesort,適用于返回結(jié)果較少的SQL,因?yàn)榻Y(jié)果少則排序開銷小;如果排序在前,則可以使用到索引的有序性,但是需要回表(或者索引條件下推)去查詢數(shù)據(jù),適用于返回結(jié)果較多的SQL,因?yàn)闊o需排序,直接取出數(shù)據(jù)。
4.復(fù)合索引的創(chuàng)建,一定不能把order by、group by的列放在索引的最前面,因?yàn)椴樵冎锌偸莣here先于order by執(zhí)行;
5.使用索引進(jìn)行范圍查詢會(huì)導(dǎo)致后續(xù)索引字段無法被使用,如果有排序,無法消除filesort排序。例子:a_b_c索引,where a>? and b = ? order by c,則a可以被使用到,b無法被使用,c字段需filesort。
到此這篇關(guān)于MySQL復(fù)合索引的文章就介紹到這了,更多相關(guān)MySQL復(fù)合索引內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
標(biāo)簽:荊州 滄州 甘南 隨州 資陽 錦州 黑河 吉林
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL復(fù)合索引的深入探究》,本文關(guān)鍵詞 MySQL,復(fù)合,索引,的,深入,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。