最近在看《mysql實戰45講》中遇到乙個比較困惑的問題。問題是:
有如下表
create table `geek` (
`a` int(11) not null,
`b` int(11) not null,
`c` int(11) not null,
`d` int(11) not null,
primary key (`a`,`b`),
key `c` (`c`),
key `ca` (`c`,`a`),
key `cb` (`c`,`b`)
) engine=innodb;
有兩條sql如下:
select * from geek where c=n order by a limit 1;
select * from geek where c=n order by b limit 1;
問題:哪個索引是多餘的?
作者認為索引c與聯合索引(c,a,b)效果相同。
看到這塊就很不理解,為什麼走索引c的時候會用到主鍵聯合索引a,b?我們知道二級索引葉子節點儲存的是主鍵id的值,在查詢的時候,會先根據二級索引找到主鍵id的值,再進行回表去主鍵id的聚簇索引樹中拿到具體的行資料。舉個例子,k為普通索引,id為主鍵索引。對於sqlselect * from t where k = 5
,執行過程是先去k索引樹找到k=5對於的id值:500;再到id索引樹找到id =500對應的行資料,並返回;接著再去k索引樹取下條資料k=6,不符合則返回。
mysql官網中介紹了索引擴充套件的原理:
innodb
innodb通過向每個二級索引新增主鍵列來自動擴充套件它。
舉個例子, t1表有主鍵索引(i1,i2) 和輔助索引 k_d,但是innodb在內部擴充套件了這個索引k_d,會把主鍵值追加到索引列後,擴充套件後的索引變為 (d, i1, i2)。mysql優化器通過使用擴充套件的輔助索引來進行更有效率地 連線、排序、ref、range查詢。
optimizer_switch系統變數的use_index_extensions標誌允許控制優化器在確定如何使用innodb表的輔助索引時是否考慮主鍵列。預設情況下,啟用「使用索引」擴充套件。如果禁用使用系統擴充套件,可以通過以下指令:
set optimizer_switch = 'use_index_extensions=off';
下面通過實驗來驗證這個結論,注意,本次實驗基於資料庫版本5.7。
(1)準備工作,建表和插入資料
create table t1 (
i1 int not null default 0,
i2 int not null default 0,
d date default null,
primary key (i1, i2),
index k_d (d)
) engine = innodb;
插入資料
insert into t1 values
(1, 1, '1998-01-01'), (1, 2, '1999-01-01'),
(1, 3, '2000-01-01'), (1, 4, '2001-01-01'),
(1, 5, '2002-01-01'), (2, 1, '1998-01-01'),
(2, 2, '1999-01-01'), (2, 3, '2000-01-01'),
(2, 4, '2001-01-01'), (2, 5, '2002-01-01'),
(3, 1, '1998-01-01'), (3, 2, '1999-01-01'),
(3, 3, '2000-01-01'), (3, 4, '2001-01-01'),
(3, 5, '2002-01-01'), (4, 1, '1998-01-01'),
(4, 2, '1999-01-01'), (4, 3, '2000-01-01'),
(4, 4, '2001-01-01'), (4, 5, '2002-01-01'),
(5, 1, '1998-01-01'), (5, 2, '1999-01-01'),
(5, 3, '2000-01-01'), (5, 4, '2001-01-01'),
(5, 5, '2002-01-01');
(2)關閉索引擴充套件
檢視優化器配置開關:show variables like '%optimizer_switch%';
可以看到mysql預設是開啟索引擴充套件功能的 use_index_extensions=on:
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on
執行關閉索引擴充套件的命令:set optimizer_switch = 'use_index_extensions=off';
執行explain指令檢視:explain select count(*) from t1 where i1 = 3 and d = '2000-01-01'
分析結果:
(3)開啟索引擴充套件
執行開啟索引擴充套件命令:set optimizer_switch = 'use_index_extensions=on';
執行explain指令檢視:explain select count(*) from t1 where i1 = 3 and d = '2000-01-01'
分析結果:
以上分析結果說明mysql在執行的時候會使用擴充套件索引(d -> (d,i))來提高執行效率。
注:5.7版本的mysql文件的乙個小漏洞
在上述第2步(2)關閉索引擴充套件的實驗中,我們分析結果表面關閉索引擴充套件的情況下,explain分析得出的使用的key是主鍵key - primary,key_len = 4, extra = using where.
但是官方5.7的文件結果卻說使用的k_d單列索引,extra卻走得是using index覆蓋索引,跟我分析的結果完全不一樣。
經過多次實驗發現mysql 5.6版本下實驗結果與文件中分析結果一致。因此,我猜測mysql 5.7文件 在關閉索引擴充套件的分析結果是直接抄了5.6的文件,沒有做任何改動,上述的分析結果是錯誤的。
實際在mysql底層實現時5.7可能已經有了變化,在count(*)時不會使用二級索引 + using index的查詢,而是直接選擇主鍵primary的查詢。
參考:
全文本索引 擴充套件索引 布林索引
一般在建立表時啟用全文本搜尋。create table語句接受fulltext子句,它給出被索引列的乙個逗號分隔的列表。creat table tb c1 int not null,c2 varchar 32 not null,note text text null,primary key c1 f...
MySQL 擴充套件
一 概述 mysql 水平拓展的策略分為三個部分 二 複製 最簡單也最常見的向外擴充套件的方法是通過複製將資料分發到多個伺服器上,然後將備庫用於讀查詢。這種技術對於以讀為主的應用很有效,它也有一些缺點,例如重複快取等等。三 按功能拆分 按功能拆分,每個節點只包含特定應用所需要的資料,各個部分無須共享...
mysql 索引 手冊 MySQL 索引
mysql 索引 mysql索引的建立對於mysql的高效執行是很重要的,索引可以大大提高mysql的檢索速度。打個比方,如果合理的設計且使用索引的mysql是一輛蘭博基尼的話,那麼沒有設計和使用索引的mysql就是乙個人力三輪車。索引分單列索引和組合索引。單列索引,即乙個索引只包含單個列,乙個表可...