1. 程式人生 > >Mysql優化(出自官方文件) - 第八篇(索引優化系列)

Mysql優化(出自官方文件) - 第八篇(索引優化系列)

目錄

  • Mysql優化(出自官方文件) - 第八篇(索引優化系列)
    • Optimization and Indexes
    • 1 Foreign Key Optimization
    • 2 Column Indexes
    • 3 Column Indexes && Multiple-Column Indexes
    • 4 Comparison of B-Tree and Hash Indexes
    • 5 Use of Index Extensions
    • 6 Invisible Indexes
    • 7 Descending Indexes

Mysql優化(出自官方文件) - 第八篇(索引優化系列)

Optimization and Indexes

正確的建立索引往往會加快查詢速度,但是,沒有必要的索引往往只會浪費空間,並且增加插入,更新和刪除的開銷,因為進行這些操作要同時更新索引。

但是,索引並不是萬能的,在下面的幾個場景中,索引將會顯得不是那麼有用:

  • 小表,或者大表,但是需要請求所有行
  • 當一個請求需要表中的大部分行時,連續讀往往效率會比索引高,因為連續讀會減少磁碟seek的時間。

1 Foreign Key Optimization

如果經常讀取一個表中的多個列,那麼,把最少訪問的列分出來單獨作為一個小表,然後使用外來鍵的方式和主表聯絡起來,這樣子在讀取資料的時候就能儘量減少磁碟的I/O讀寫。

2 Column Indexes

  • Index Prefixes

    Mysql允許只使用一些欄位的一部分來作為索引,這其中包括所有的TEXTBLOB欄位,如下面的例子:

    CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));

    需要注意的是,對於CHAR, VARCHAR, TEXT型別的欄位,長度為字元個數的意思,但是對於非字串欄位,如BINARY, VARBINARY, BLOB

    欄位,長度為位元組個數的意思。

  • FULLTEXT Indexes

    該索引適用於CHAR, VARCHAR, and TEXT的列,並且全文索引不支援字首,只支援全列的索引。當查詢符合如下特徵的時候,全文索引將會非常有用:

    • FULLTEXT查詢語句值需要document ID或者search rank(分級??)
    • FULLTEXT查詢語句對查詢到的結果進行一個降序排序,並且有一個LIMIT語句選取top N項,這種情況下為了使用優化必須保證查詢語句沒有WHEREORDER BY只有一列。
    • FULLTEXT查詢只獲取COUNT(*)的結果,且沒有WHERE語句,如果需要WHERE過濾,請將WHERE寫為WHERE MATCH(TEXT) AGAINST('other_text'),並且沒有任何> 0的比較操作符。
  • Indexes in the MEMORY Storage Engine

    對於記憶體儲存引擎,預設使用HASH索引,但是同樣也支援BTREE索引。

3 Column Indexes && Multiple-Column Indexes

Mysql支援單獨的一列作為索引,也可以使用多列作為索引,當使用多列的時候,使用最左邊(leftmost prefix of the index)的列進行查詢,可以用到索引,否則,Mysql將無法使用索引,舉例如下:

假設一個表含有下面的索引:

 INDEX name (last_name,first_name)

對於下面的查詢語句都可以使用到name索引:

SELECT * FROM test WHERE last_name='Jones';

SELECT * FROM test
  WHERE last_name='Jones'
  AND (first_name='John' OR first_name='Jon');
  
SELECT * FROM test
  WHERE last_name='Jones'
  AND first_name >='M' AND first_name < 'N';

下面的語句因為沒有使用最左邊的列進行查詢,所以無法使用索引:

SELECT * FROM test WHERE first_name='John';

SELECT * FROM test
  WHERE last_name='Jones' OR first_name='John';

假設有兩列col1col2,如果索引剛好建在col1col2上,那麼可以直接使用索引,但是如果col1col2是單獨的索引(即不是多列索引),那麼Mysql會嘗試使用 Index Merge optimization (see Section 8.2.1.3, “Index Merge Optimization”)技術,或者判斷兩個索引中哪個的條件是最嚴苛的,能夠排除儘可能的行,在決定使用哪個索引。

4 Comparison of B-Tree and Hash Indexes

  • B-Tree Index Characteristics

    B樹索引支援 =, >, >=, <, <=, or BETWEEN操作符,也支援LIKE操作符,但是必須保證LIKE欄位不是以萬用字元開頭的。

    如果一個索引沒有覆蓋所有的AND,那麼Mysql將無法使用該索引,換句話說,為了能夠使用到該索引,必須保證一個索引的字首(最左邊部分)出現在每一個AND中,舉例如下:

    ... WHERE index_part1=1 AND index_part2=2 AND other_column=3
    
        /* index = 1 OR index = 2 */
    ... WHERE index=1 OR A=10 AND index=2
    
        /* optimized like "index_part1='hello'" */
    ... WHERE index_part1='hello' AND index_part3=5
    
        /* Can use index on index1 but not on index2 or index3 */
    ... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3;

    下面的語句將無法使用索引:

        /* index_part1 is not used */
    ... WHERE index_part2=1 AND index_part3=2
    
        /*  Index is not used in both parts of the WHERE clause  */
    ... WHERE index=1 OR A=10
    
        /* No index spans all rows  */
    ... WHERE index_part1=1 OR index_part2=10

    (這裡不是很懂?)

    需要注意的是:

    Mysql並不會任何時候都選擇使用索引,有的時候,當需要讀取特別多的行時,使用索引的效率可能要低於table scan的效率,因為索引會導致磁碟出現更多的尋道消耗,而table scan由於是連續讀,則能更大效率的利用磁碟I/O

  • Hash Index Characteristics

    Hash索引相對比於B樹索引,可能會出現下面的不同:

    • Hash索引只能用於=或者<=>操作符,不能用於範圍查詢
    • 優化器無法使用Hash索引來加速ORDER BY操作
    • Mysql無法評估兩個value之間有多少行(BETWEEN語句)
    • 只有完整的索引才能正常工作,不能像BTREE那樣,使用leftmost prefix key

5 Use of Index Extensions

InnoDB會自動給二級索引增加primary key作為新的索引,這是在內部完成的,使用者無法感知,比如下面的表:

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;

此時,對於索引k_d,其內部真正的實現方式為: (d, i1, i2). 即自動將primary key(i1, i2)新增到k_d後面構成新的二級索引。

這種優化方式,可用於refrangeindex_merge的索引訪問場景,Loose Index Scanjoinsort,以及MIN()/MAX()。

比如下面的語句:

SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'

此時由於內部將i1新增到了k_d索引,那麼這條語句就可以使用到(d, i1)這樣的索引((d, i1)是索引(d, i1, i2)的最左邊字首),由於使用到了更多的列,所以需要掃描的行數將大大減少。

6 Invisible Indexes

這種索引指的是那種真實存在,但是優化器卻不會使用的索引,只能對非primary key進行該種設定,其他型別的索引均支援設定為INVISIBLE

例如下面的例子:

CREATE TABLE t1 (
  i INT,
  j INT,
  k INT,
  INDEX i_idx (i) INVISIBLE
) ENGINE = InnoDB;
CREATE INDEX j_idx ON t1 (j) INVISIBLE;
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;

這種索引一般的用途:測試移除一個索引對於效能的影響,因為這種索引不是真正的將其移除了,只是優化器不再考慮該索引,所以並不會對錶造成太大的影響。

注意雖然索引是Invisible的,但是這種索引和普通索引一樣具有完備的功能,也就是說增刪改的時候,Mysql同樣會更新Invisible Index,同理,如果在一個非常大的表上建立Invisible Index時,和普通索引一樣,開銷也是非常巨大的。

7 Descending Indexes

Mysql支援降序索引,不像以前的版本,索引定義裡面的DESC不再被忽略,內部對於索引的儲存方式也採取降序的方式;而在此之前,Mysql是採用對index進行逆序掃描的方式,當前採用降序的方式效率比以前要高很多。

使用降序索引主要針對下面的場景:

  • 降序索引只支援InnoDB引擎,並且有如下限制:
    • 如果一個二級索引有降序key或者說一個主鍵包含降序key,那麼Mysql無法支援修改Buffering
    • InnoDB的SQL解析器不會使用降序索引,對於full-text查詢,這意味著已經被索引的表上面的FTS_DOC_ID 列不能被定義為降序索引。
  • 一個含有降序key的索引無法使用MIN/MAX優化,特指那種含有聚合函式但是卻不帶GROUP BY的語句。
  • 降序索引只支援BTREE,並不支援HASH索引,降序索引無法支援FULLTEXT或者SPATIAL索引。