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允許只使用一些欄位的一部分來作為索引,這其中包括所有的
TEXT
和BLOB
欄位,如下面的例子:CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));
需要注意的是,對於
CHAR, VARCHAR, TEXT
型別的欄位,長度為字元個數的意思,但是對於非字串欄位,如BINARY, VARBINARY, BLOB
FULLTEXT Indexes
該索引適用於
CHAR
,VARCHAR
, andTEXT
的列,並且全文索引不支援字首,只支援全列的索引。當查詢符合如下特徵的時候,全文索引將會非常有用:FULLTEXT
查詢語句值需要document ID
或者search rank
(分級??)FULLTEXT
查詢語句對查詢到的結果進行一個降序排序,並且有一個LIMIT
語句選取top N
項,這種情況下為了使用優化必須保證查詢語句沒有WHERE
且ORDER 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';
假設有兩列col1
和col2
,如果索引剛好建在col1
和col2
上,那麼可以直接使用索引,但是如果col1
和col2
是單獨的索引(即不是多列索引),那麼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樹索引支援
=
,>
,>=
,<
,<=
, orBETWEEN
操作符,也支援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/OHash 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後面構成新的二級索引。
這種優化方式,可用於ref
, range
和index_merge
的索引訪問場景,Loose Index Scan
,join
和sort
,以及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索引。