MySQL索引的設計、使用與優化
一、使用索引的優缺點
索引是資料庫用來提高效能的最常用工具,所有的MySQL列都可以被索引,對相關的列進行索引是提高select效能的最佳途徑。使用索引的優點和缺點如下:
優點:
- 快速訪問資料表中的特定資訊,提高檢索速度
- 建立唯一性索引,保證資料庫表中每一行資料的唯一性。
- 加速表和表之間的連線
缺點:
- 建立索引和維護索引需要耗費時間,這個時間隨著資料量的增加而增加
- 索引需要佔用物理空間,不光是表需要佔用資料空間,每個索引也需要佔用物理空間
- 當對錶進行增、刪、改、的時候索引也要動態維護,這樣就降低了資料的維護速度
二、設計索引的原則
為了提高索引的使用效率,設計索引時應遵循以下規則:
- 新增索引的列不一定是要搜尋的列:最適合索引的列是出現在WHERE子句中的列,或連線子句中指定的列,而不是SELECT關鍵字後的選擇列表中的列。
- 使用唯一索引:索引的列的基數(不重複數)越大,索引的效果越好。比如出生日期列資料各不相同,適合新增索引,而性別列只含有‘M’和‘F’兩個值,則不適合新增索引。
- 使用短索引:如果對字串列進行索引,應當制定一個字首長度,例如,一個CHAR(200)的列如果前10個或者20個字元多數是唯一的,就不要對整個列進行索引。較短的索引涉及的磁碟IO較少,佔用空間也小,查詢更快。
- 不要過度索引:每個額外的索引都要佔用額外的磁碟空間,並降低寫操作效能。在修改表內容時,索引必須進行更新,可能還會重構,從而降低表的修改速度。
三、索引的儲存分類
1、分類
索引是在MySQL儲存引擎層實現的,而不是在伺服器層,所以每種儲存引擎的索引都不一定相同,MySQL目前有以下四種索引:
- B-Tree索引:最常見的索引型別,大部分儲存引擎都支援B樹索引。
- HASH索引:只有Memory、Heap引擎支援,使用場景簡單。
- R-Tree索引(空間索引):MyISAM的一個特殊索引型別,使用較少。
- Full-text(全文索引):特殊索引型別,MyISAM和InnoDB支援。
MyISAM、InnoDB、Memory三個常用引擎支援的索引型別比較:
索引 | MyISAM引擎 | InnoDB引擎 | Memory引擎 |
---|---|---|---|
B-Tree索引 | 支援 | 支援 | 支援 |
HASH索引 | 不支援 | 不支援 | 支援 |
R-Tree索引 | 支援 | 不支援 | 不支援 |
Full-text索引 | 支援 | 支援 | 不支援 |
2、HASH索引與B-Tree索引
使用HASH索引時應注意:
- HASH索引只能用於=或<=>操作符的等式比較。
- 優化器不能使用HASH索引來加速ORDER BY 操作。
- HASH索引只能使用整個關鍵字來搜尋一行,而不能使用萬用字元。
下列範圍查詢適用於HASH索引和B-Tree索引:
select * from demo where id=2 or id in(4,5,8,9);
下列範圍查詢只適用於B-Tree索引:
select * from demo where id>5 and id<10;
select * from demo where birthday like '1996-08-%' or salary between 5000 and 10000;
此處如果表型別是Memory/Heap,id是HASH索引,則查詢使用全表掃描的方式,不會用到索引。
四、SQL優化中的索引問題
SQL優化中對索引的優化實際上是將對添加了索引,但sql執行計劃沒有選擇使用索引的場景的優化。
以下內容預設使用B-Tree索引。
案例用表:
mysql> show create table demo \G;
*************************** 1. row ***************************
Table: demo
Create Table: CREATE TABLE `demo` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '張三' COMMENT '姓名',
`sex` enum('男','女') NOT NULL DEFAULT '男' COMMENT '性別',
`age` int(10) DEFAULT NULL,
`birthday` date DEFAULT '1970-01-01',
`address` varchar(50) DEFAULT NULL,
`salary` double DEFAULT '0',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
ERROR:
No query specified
mysql> select * from demo;
+----+--------+-----+------+------------+---------+--------+
| id | name | sex | age | birthday | address | salary |
+----+--------+-----+------+------------+---------+--------+
| 2 | tom | 男 | 22 | 1996-08-08 | 武漢 | 2000 |
| 4 | 趙六 | 男 | 60 | 1958-08-08 | 上海 | 3000 |
| 6 | 李四 | 男 | 23 | 1996-08-08 | 武漢 | 50000 |
| 7 | lucy | 女 | 22 | 1996-07-08 | 武漢 | 8000 |
| 8 | 李四 | 男 | 36 | 1982-06-08 | 杭州 | 6000 |
| 9 | sunnie | 女 | 22 | 1996-08-28 | NULL | 0 |
| 11 | 王五 | 女 | 30 | 1988-08-08 | 北京 | 10000 |
| 12 | sherly | 女 | 18 | 2000-02-20 | 深圳 | 12000 |
+----+--------+-----+------+------------+---------+--------+
8 rows in set (0.00 sec)
1、MySQL中能使用索引的場景
(1)匹配全值。
對索引中所有的列都指定具體值。例如:
mysql> create index member on demo(name,sex,address(10));
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from demo where name='王五' and sex='女' and address='北京' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ref
possible_keys: member
key: member
key_len: 96
ref: const,const,const
rows: 1
Extra: Using index condition; Using where
1 row in set (0.00 sec)
explain語句輸出結果中type=ref表示使用非唯一索引或唯一索引的字首進行掃描,key=member表示使用了組合索引member。
Extra中的Using index condition表示使用ICP(Index Condition Pushdown)優化查詢。
(2)匹配值的範圍查詢
對索引值能夠進行範圍查詢。例如:
mysql> explain select * from demo where id>5 and id<10 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 3
Extra: Using where
1 row in set (0.00 sec)
type為range說明優化器選擇範圍查詢,key為primary說明使用主鍵查詢。
(3)匹配最左字首
僅僅使用複合索引中最左邊的列進行查詢,例如:
mysql> explain select * from demo where name='tom' and address in ('武漢','北京','上海') \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ref
possible_keys: member
key: member
key_len: 62
ref: const
rows: 1
Extra: Using index condition; Using where
1 row in set (0.00 sec)
mysql> explain select * from demo where sex='男' and address in ('武漢','北京','上海') \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 7
Extra: Using where
1 row in set (0.00 sec)
從(1)中知道建立的member索引包含三列:name、sex、address,只要查詢條件包含第一列(最左字首)時,優化器使用複合索引member進行查詢,當查詢條件不包含第一列時,執行計劃不會利用到複合索引member。
(4)僅僅對索引進行查詢
當查詢的列都在索引的欄位中時,查詢效率更高,例如:
mysql> explain select name,sex from demo where name='tom' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ref
possible_keys: member
key: member
key_len: 62
ref: const
rows: 1
Extra: Using where; Using index
1 row in set (0.00 sec)
要查詢的name欄位和sex欄位都在member索引中,Extra中Using index,意味著直接訪問索引就能獲取到所需資料,也就是‘覆蓋索引掃描’。Using where表示優化器需要通過索引回表查詢資料。
(5)匹配列字首
僅僅使用索引中的第一列的開頭一部分資訊進行查詢,例如:
mysql> explain select * from demo where name like 'sh%' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: range
possible_keys: member
key: member
key_len: 62
ref: NULL
rows: 1
Extra: Using index condition
1 row in set (0.00 sec)
(6)索引一部分精確匹配,其他部分範圍匹配
例如:
mysql> explain select * from demo where name='李四' and address<'武漢' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ref
possible_keys: member
key: member
key_len: 62
ref: const
rows: 2
Extra: Using index condition; Using where
1 row in set (0.00 sec)
key=member說明優化器使用了member索引。
(7)如果列名是索引,那麼使用 列名 is null 會使用索引。(區別於oracle)
例如:
mysql> create index city on demo(address);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from demo where address is null \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ref
possible_keys: city
key: city
key_len: 153
ref: const
rows: 1
Extra: Using index condition
1 row in set (0.00 sec)
這裡給address列建立一個索引city,使用列名is null 會使用索引。
2、MySQL中存在索引但不能使用索引的場景
(1)以%開頭的like查詢不能利用B-Tree索引。
例如:
mysql> explain select * from demo where name like '%m' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: demo
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 7
Extra: Using where
1 row in set (0.00 sec)
以%或_開頭的查詢不能利用B-Tree索引的由於B-Tree索引的平衡樹結構。
解決辦法:
先獲得滿足條件的主鍵列表,之後根據主鍵回表檢索記錄,這樣可以避免全表掃描產生大量的IO請求。
語句如下:
mysql> explain select * from (select id from demo where name like '%m') a,demo b where a.id=b.id \G;
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: b
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 7
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ref
possible_keys: <auto_key0>
key: <auto_key0>
key_len: 4
ref: test.b.id
rows: 2
Extra: Using index
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: demo
type: index
possible_keys: NULL
key: member
key_len: 96
ref: NULL
rows: 7
Extra: Using where; Using index
3