【背景】
對於關系數據庫中的一張表,通常來說數據頁面的總大小要比較某一個索引占用的頁面要大的多(上面說的索引是不包涵主鍵索引的);
更進一步我們可以推導出,如果我們通過讀索引就能解決問題,那么它相比讀數據頁來說要廉價的多;整體上看數據庫會盡可能的通過
讀索引就解決問題。
【index_merge是什么】
為了說明index_merge是什么、這里還是從一個例子開始;假設數據庫存在如下內容
create table person (id int not null auto_increment primary key, name varchar(8) default null, age tinyint default null, key idx_person_name (name), key idx_person_age (age) );
表中的數據如下
select * from person; +----+-------+------+ | id | name | age | +----+-------+------+ | 1 | tom | 16 | | 2 | jerry | 17 | | 3 | neeky | 3 | +----+-------+------+ 3 rows in set (0.00 sec)
下面的這條SQL語句事實上可以這樣做,讀取idx_person_name找到name='tom'的行id,讀取idx_person_age找到age=17的行id;
給這兩個id的集合做一下交集;這樣就找到了所有滿足條件的行id,最后回表把對應的行給查詢出來;如果MySQL這樣做的話
在索引頁面數理遠遠小於數據頁面數量的情況下是有節約成功的優勢的
select name,age from person where name='tom' and age=17;
事實上MySQL會不會這樣干呢?對於這個還是要看執行記錄比較靠普;從下面的執行計划可以看出MySQL選擇了只用
idx_person_name這一個索引,從innodb中撈到數據后在server層過濾的方式來完成查詢。明顯沒有用到index_merge
explain select name,age from person where name='tom' and age=17; +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | person | NULL | ref | idx_person_name,idx_person_age | idx_person_name | 67 | const | 1 | 33.33 | Using where | +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec
從上面的分析我們可以知道用不用index_merge優化,不光是看可不可以用理加重要的是看代價是否合理;為了讓MySQL知道索引頁面的數量要遠遠小於
數據頁面的數量,我要在表中多插入一些數據(復制執行下面的語句)
insert into person(name,age) select name,age from person; -- 執行n次 select count(*) from person; +----------+ | count(*) | +----------+ | 393216 | +----------+ 1 row in set (0.05 sec)
在數據量差不多40w的情況下我們再看一下優化器的選擇
explain select name,age from person where name='tom' and age=17; +----+-------------+--------+------------+-------------+--------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+-------------+--------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------------------+ | 1 | SIMPLE | person | NULL | index_merge | idx_person_name,idx_person_age | idx_person_name,idx_person_age | 67,2 | NULL | 98237 | 100.00 | Using intersect(idx_person_name,idx_person_age); Using where; Using index | +----+-------------+--------+------------+-------------+--------------------------------+--------------------------------+---------+------+-------+----------+---------------------------------------------------------------------------+ 1 row in set, 1 warning (0.00 sec)
【用了Index_merge優化會比沒有用index_merge優化快多少呢】
1、測試啟用index_merge情況下40w行數據時查詢的用時
select name,age from person where name='tom' and age=17; Empty set (0.08 sec)
2、關閉MySQL數據庫對index_merge的優化
set @@global.optimizer_switch='index_merge=off,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,use_invisible_indexes=off,skip_scan=on'; --: 退出重新連接(這樣剛才的設置就生效了)
3、在沒有index_merge的情況下發起查詢
explain select name,age from person where name='tom' and age=17; +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+--------+----------+-------------+ | 1 | SIMPLE | person | NULL | ref | idx_person_name,idx_person_age | idx_person_name | 67 | const | 196474 | 50.00 | Using where | +----+-------------+--------+------------+------+--------------------------------+-----------------+---------+-------+--------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) -- 從執行計划上可以看出index_merge關閉了 select name,age from person where name='tom' and age=17; Empty set (0.34 sec)
【總結】
對比開啟和關閉index_merge,在數據量為40w這個量級的表上,開啟優化相比不開有4倍以上的優化成績。由index_merge的原理可以知在數據理更大的
情況下優化的效果會更加明顯
【我的個人站點】