MySQL 之 索引進階

一、正確使用索引

(1)、一 索引未命中

​ 並非說建立了索引就必定會加快查詢速度,若想利用索引達到預想的提升查詢速度的效果,在添加索引時,必須注意如下問題:mysql

<1>、範圍問題

​ 或者說條件不明確,條件中出現這些符號或關鍵字:>、>=、<、<=、!= 、between...and...、likesql

大於號、小於號數據庫

img

不等於 號函數

img

between ...and...大數據

img

like優化

img

<2>、 儘可能選擇區分度高的列做爲索引

​ 區分度的公式是 count( distinct col ) / count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,這個比例有使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,即平均1條掃描10條記錄3d

img

# 分析緣由
  咱們編寫存儲過程爲表s1批量添加記錄,name字段的值均爲egon,也就是說name這個字段的區分度很低(gender字段也是同樣的,咱們稍後再搭理它)

回憶b+樹的結構,查詢的速度與樹的高度成反比,要想將樹的高低控制的很低,須要保證:在某一層內數據項均是按照從左到右,從小到大的順序依次排開,即左1<左2<左3<...

而對於區分度低的字段,沒法找到大小關係,由於值都是相等的,毫無疑問,還想要用b+樹存放這些等值的數據,只能增長樹的高度,字段的區分度越低,則樹的高度越高。極端的狀況,索引字段的值都同樣,那麼b+樹幾乎成了一根棍。本例中就是這種極端的狀況,name字段全部的值均爲'egon'

# 如今咱們得出一個結論:爲區分度低的字段創建索引,索引樹的高度會很高,然而這具體會帶來什麼影響呢???

# 1:若是條件是name='xxxx',那麼確定是能夠第一時間判斷出'xxxx'是不在索引樹中的(由於樹中全部的值均爲'egon’),因此查詢速度很快
# 2:若是條件正好是name='egon',查詢時,咱們永遠沒法從樹的某個位置獲得一個明確的範圍,只能往下找,往下找,往下找。。。這與全表掃描的IO次數沒有多大區別,因此速度很慢

<3>、索引列不能在條件中參與計算

​ 保持列「乾淨」,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,緣由很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。因此語句應該寫成create_time = unix_timestamp(’2014-05-29’)unix

img

<4>、and / or

# 一、and與or的邏輯
    條件1 and 條件2:全部條件都成立纔算成立,但凡要有一個條件不成立則最終結果不成立
    條件1 or 條件2:只要有一個條件成立則最終結果就成立

# 二、and的工做原理
    條件:
        a = 10 and b = 'xxx' and c > 3 and d =4
    索引:
        製做聯合索引(d,a,b,c)
    工做原理:
        對於連續多個and:mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣即可以快速鎖定很小的範圍),加速查詢,即按照d—>a->b->c的順序

# 三、or的工做原理
    條件:
        a = 10 or b = 'xxx' or c > 3 or d =4
    索引:
        製做聯合索引(d,a,b,c)
    工做原理:
        對於連續多個or:mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d

img

在左邊條件成立可是索引字段的區分度低的狀況下(name與gender均屬於這種狀況),會依次往右找到一個區分度高的索引字段,加速查詢code

img

img

通過分析,在條件爲name='egon' and gender='male' and id>333 and email='xxx'的狀況下,咱們徹底不必爲前三個條件的字段加索引,由於只能用上email字段的索引,前三個字段的索引反而會下降咱們的查詢效率blog

img

<5>、最左前綴匹配原則

​ 很是重要的原則,對於組合索引mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配(指的是範圍大了,有索引速度也慢),好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。

img

<6>、其餘狀況

- 使用函數
    select * from tb1 where reverse(email) = 'egon';
            
- 類型不一致
    若是列是字符串類型,傳入條件是必須用引號引發來
    select * from tb1 where email = 999;
    
# 排序條件爲索引,則select字段必須也是索引字段,不然沒法命中
- order by
    select name from s1 order by email desc;
    當根據索引排序時候,select查詢的字段若是不是索引,則速度仍然很慢
    select email from s1 order by email desc;
    特別的:若是對主鍵排序,則仍是速度很快:
        select * from tb1 order by nid desc;
 
- 組合索引最左前綴
    若是組合索引爲:(name,email)
    name and email       -- 命中索引
    name                 -- 命中索引
    email                -- 未命中索引

- count(1)或count(列)代替count(*)在mysql中沒有差異了

- create index xxxx  on tb(title(19)) # text類型,必須制定長度

(2)、其餘注意事項

- 避免使用 select *
- 使用 count(*)
- 建立表時儘可能使用 char 代替 varchar
- 表的字段順序固定長度的字段優先
- 組合索引代替多個單列索引(因爲mysql中每次只能使用一個索引,因此常用多個條件查詢時更適合使用組合索引)
- 儘可能使用短索引
- 使用鏈接(JOIN)來代替子查詢(Sub-Queries)
- 連表時注意條件類型需一致
- 索引散列值(重複少)不適合建索引,例:性別不適合

二、 聯合索引與覆蓋索引

(1)、聯合索引

​ 聯合索引是指對錶上的多個列合起來作一個索引。聯合索引的建立方法與單個索引的建立方法同樣,不一樣之處僅在於有多個索引列,以下

mysql> create table t(
    -> a int,
    -> b int,
    -> primary key(a),
    -> key idx_a_b(a,b)
    -> );
Query OK, 0 rows affected (0.11 sec)

​ 從本質上來講,聯合索引就是一棵B+樹,不一樣的是聯合索引的鍵值的數量不是1,而是>=2。接着來討論兩個整型列組成的聯合索引,假定兩個鍵值得名稱分別爲a、b如圖

img

​ 能夠看到這與以前看到的單個鍵的B+樹並無什麼不一樣,鍵值都是排序的,經過葉子結點能夠邏輯上順序地讀出全部數據,就上面的例子來講,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),數據按(a,b)的順序進行了存放。

​ 所以,對於查詢 select * from table where a=xxx and b=xxx, 顯然是可使用(a,b) 這個聯合索引的,對於單個列a的查詢 select * from table where a=xxx,也是可使用(a,b)這個索引的。

​ 但對於b列的查詢 select * from table where b=xxx,則不可使用(a,b) 索引,其實不難發現緣由,葉子節點上b的值爲一、二、一、四、一、2顯然不是排序的,所以對於b列的查詢使用不到(a,b) 索引。

​ 聯合索引的第二個好處是在第一個鍵相同的狀況下,已經對第二個鍵進行了排序處理。

(2)、覆蓋索引

​ InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就能夠獲得查詢記錄,而不須要查詢彙集索引中的記錄。

​ 使用覆蓋索引的一個好處是:輔助索引不包含整行記錄的全部信息,故其大小要遠小於彙集索引,所以能夠減小大量的IO操做。


注意:覆蓋索引技術最先是在InnoDB Plugin中完成並實現,這意味着對於InnoDB版本小於1.0的,或者MySQL數據庫版本爲5.0如下的,InnoDB存儲引擎不支持覆蓋索引特性。


​ 對於InnoDB存儲引擎的輔助索引而言,因爲其包含了主鍵信息,所以其葉子節點存放的數據爲(primary key1,priamey key2,...,key1,key2,...)。

​ 覆蓋索引的另一個好處是對某些統計問題而言的。基於上一小結建立的表buy_log,查詢計劃以下

mysql> explain select count(*) from buy_log;
+--+-----------+-------+-----+-------------+------+-------+----+----+-----------+
|id|select_type|table  | type|possible_keys|key   |key_len|ref |rows|Extra      |
+--+-----------+-------+-----+-------------+------+-------+----+----+-----------+
| 1| SIMPLE    |buy_log|index| NULL        |userid| 4     |NULL|  7 |Using index|
+--+-----------+-------+-----+-------------+------+-------+----+----+-----------+
1 row in set (0.00 sec)

# Using index表明覆蓋索引

​ innodb存儲引擎並不會選擇經過查詢彙集索引來進行統計。因爲buy_log表有輔助索引,而輔助索引遠小於彙集索引,選擇輔助索引能夠減小IO操做,故優化器的選擇如上key爲userid輔助索引

​ 對於(a,b)形式的聯合索引,通常是不能夠選擇b中所謂的查詢條件。但若是是統計操做,而且是覆蓋索引,則優化器仍是會選擇使用該索引,以下

# 聯合索引userid_2(userid,buy_date),通常狀況,按照buy_date是沒法使用該索引的,但特殊狀況下:查詢語句是統計操做,且是覆蓋索引,則按照buy_date當作查詢條件時,也可使用該聯合索引
mysql> explain select count(*) from buy_log where buy_date >= '2011-01-01' and buy_date < '2011-02-01';
+--+-----------+-------+-----+-------------+--------+-------+----+----+------------------------+
|id|select_type| table |type |possible_keys| key    |key_len|ref |rows|Extra                   |
+--+-----------+-------+-----+-------------+--------+-------+----+----+------------------------+
| 1| SIMPLE    |buy_log|index| NULL        |userid_2| 8     |NULL|  7 |Using where; Using index|
+--+-----------+-------+-----+-------------+--------+-------+----+----+------------------------+
1 row in set (0.00 sec)
# 合併索引
mysql> explain select count(email) from index_t where   id = 1000000  or email='eva100000@oldboy';
+--+-----------+------+--------------+--------------------------------+---------------+--------+-----+----+-----------------------------------------+
| id | select_type| table  | type                | possible_keys                              | key                   | key_len | ref    |rows | Extra                                                           |
+--+-----------+------+--------------+--------------------------------+---------------+--------+-----+----+-----------------------------------------+
|  1 | SIMPLE      | index_t| index_merge | PRIMARY,email,ind_id,ind_email | PRIMARY,email | 4,51   |NULL|   2    |Using union(PRIMARY,email); Using where |
+--+-----------+------+--------------+--------------------------------+---------------+--------+-----+----+-----------------------------------------+
1 row in set (0.01 sec)
相關文章
相關標籤/搜索