1. 程式人生 > >MySQL索引的設計、使用與優化

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