【背景】sql
對於關係數據庫中的一張表,一般來講數據頁面的總大小要比較某一個索引佔用的頁面要大的多(上面說的索引是不包涵主鍵索引的);數據庫
更進一步咱們能夠推導出,若是咱們經過讀索引就能解決問題,那麼它相比讀數據頁來講要廉價的多;總體上看數據庫會盡量的經過oop
讀索引就解決問題。測試
【index_merge是什麼】優化
爲了說明index_merge是什麼、這裏仍是從一個例子開始;假設數據庫存在以下內容spa
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) );
表中的數據以下code
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;server
給這兩個id的集合作一下交集;這樣就找到了全部知足條件的行id,最後回表把對應的行給查詢出來;若是MySQL這樣作的話blog
在索引頁面數理遠遠小於數據頁面數量的狀況下是有節約成功的優點的索引
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優化快多少呢】
一、測試啓用index_merge狀況下40w行數據時查詢的用時
select name,age from person where name='tom' and age=17; Empty set (0.08 sec)
二、關閉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'; --: 退出從新鏈接(這樣剛纔的設置就生效了)
三、在沒有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的原理能夠知在數據理更大的
狀況下優化的效果會更加明顯
【個人我的站點】