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/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后面構成新的二級索引。
這種優化方式,可用於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索引。
