1. 程式人生 > >mysql 索引之索引基礎

mysql 索引之索引基礎

mysql  索引在 mysql 優化中來說是非常重要的一個環節 。索引本質上不難,但要構建高效的索引卻又不是那麼容易的。在這裡打算分三個環節來描述下索引:

  1. 索引基礎
  2. 索引的使用
  3. 索引優化

其中索引基礎,就是這篇文章要說的問題,第二部分索引的使用包括字首索引,全文索引等內容,第三部分想要說的是索引的優化,這裡提下全文索引,一起使用全文索引的時候基本上是myql 結合 sphinx 或者 elasticsearch 全文搜尋引擎的使用,不過在mysql 5.7 中已內建 ngram 分詞外掛。

下面是這篇文章的概述:

  • 1.理解索引
  • 2.索引的型別
  • 3.索引的管理
  • 4.索引的優缺點
  • 5.索引使用的原則

1.理解索引

   在說索引前,我們先在腦海中回憶下,我們以前使用新華詞典的過程。想一下,我們從詞典查詢某個詞的釋義的過程 -- 大多數情況下是先從拼音或部首開始查到該詞大概所在的頁數,然後把詞典翻到相應到頁數查到我們需要的資料。如果把這一過程抽象出來,那麼我們可以把拼音或部首稱之為索引。

   細想下,我們要查 "木" 這個詞的釋義過程,先把詞典翻到拼音部分,找到 "m" 部分,然後再在''m" 部分查詢 "u" ,再然後找到 "木" 這個詞,從而找到 "木" 這個詞位於詞典的第幾頁,然後就能迅速地找到改詞的釋義(也就是我們要查詢的資料)。我們把詞典想象成mysql資料表,拼音或部首想象成索引,我們可以用上面查詞典的過程在腦海中描繪出mysql查資料的過程,是不是很簡單(雖然並不很準確,但卻很容易理解)。

  索引是從資料中提取關鍵字,並與資料記錄建立對應關係的資料結構。索引的本質是資料結構。

 

2.索引的型別

 

名稱 語法 關鍵詞
主鍵索引 primary key 要求關鍵字不能重複,也不能為NULL。同時增加主鍵約束
唯一索引 unique index 要求關鍵字不能重複。同時增加唯一約束
全文索引 fulltext key 關鍵字的來源不是所有欄位的資料,而是從欄位中提取的特別關鍵詞
普通索引 index 對關鍵字沒有要求

關鍵詞可以使記錄的部分資料(某個欄位,某些欄位,某個欄位的一部分),當是某些欄位的時候,那就是複合索引;當是某個欄位的一部分的時候,那就是字首索引。前面四種索引型別都可以為複合索引。

 

3. 索引的管理

 3.1建立索引

      3.1.1 更新表結構

       先看一下,現有表結構:

       

mysql> show create table dye_production_schedules \G;
*************************** 1. row ***************************
       Table: dye_production_schedules
Create Table: CREATE TABLE `dye_production_schedules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '染訂單id',
  `order_detail_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '染訂單分錄id',
  `dye_code` varchar(64) NOT NULL DEFAULT '' COMMENT '染訂單編號',
  `product_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '產品id',
  `batch_num` varchar(64) NOT NULL DEFAULT '' COMMENT '缸號',
  `customer_color_name` varchar(128) NOT NULL DEFAULT '' COMMENT '客戶顏色',
  `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否刪除,預設 0 沒有刪除, 1 -刪除',
  `scheduling_bacth` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '投胚卷數',
  `scheduling_qty` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '投胚數量,以最小單位來計算,比如單位是kg ,那麼就存g',
  `finished_batch` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '成品卷數',
  `finished_qty` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '成品重量,以最小單位來計算,比如單位是kg ,那麼就存g',
  `prodction_state` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '生產進度狀態',
  `dye_factory_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '染廠id',
  `is_pCode` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '0 沒有疋號,1 有疋號',
  `is_need_knit` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否需要領胚布,0 不需要 ,1 - 需要',
  `remark` text COMMENT '備註',
  `created_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1011 DEFAULT CHARSET=utf8

 

       更新表結構,加入以下索引:

  alter table dye_production_schedules
  add index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 複合索引
  add index `dye_factory_id` (`dye_factory_id`),
  add index `dye_code` (`dye_code`),
  add index `order_id_batch_num` (`order_id`,`batch_num`) ,
  add fulltext index `customer_color_name` (`customer_color_name`);
  其中`dye_factory_id` 和 `dye_code` 是普通索引,`customer_color_name` 是全文索引

 

注:這裡沒有列出主鍵索引是之前建表就指定了主鍵索引,我這邊資料庫表資料是採用定時任務執行指令碼往資料表插資料,如果建立唯一索引,也會對錶資料加入唯一約束,所以這裡暫不討論唯一索引,建立唯一索引只需要在index 前加上關鍵詞  unique 即可!

 

       3.1.2 建表時建立索引

  create table index_test_table(

    id int auto_increment,
    order_id int  unsigned not null default 0 comment '染訂單id',
    order_detail_id int unsigned not null default 0 comment '染訂單分錄id',
    dye_code varchar(64) not null default '' comment '染訂單編號',
    product_id smallint unsigned not null default 0 comment '產品id',
    batch_num varchar(64) not null default '' comment '缸號',
    customer_color_name varchar(128) not null default '' comment '客戶顏色',
    is_delete tinyint unsigned not null default  0 comment '是否刪除,預設 0 沒有刪除, 1 -刪除',
    scheduling_bacth int unsigned not null default 0 comment '投胚卷數',
    scheduling_qty bigint unsigned not null default 0 comment '投胚數量,以最小單位來計算,比如單位是kg ,那麼就存g',
    `finished_batch` int unsigned not null default 0 comment '成品卷數',
    `finished_qty` bigint unsigned not null default 0 comment '成品重量,以最小單位來計算,比如單位是kg ,那麼就存g',
    prodction_state  tinyint unsigned not null default 0 comment '生產進度狀態',
    `dye_factory_id`  smallint unsigned  not null default 0 comment '染廠id',
    is_pCode  tinyint unsigned not null default 0 comment '0 沒有疋號,1 有疋號',
    is_need_knit tinyint unsigned not null default 0 comment '是否需要領胚布,0 不需要 ,1 - 需要',
    `remark` text comment '備註',
    created_at timestamp,
    primary key(id),
    index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 複合索引
    index `dye_factory_id` (`dye_factory_id`),
    index `dye_code` (`dye_code`),
    index `order_id_batch_num` (`order_id`,`batch_num`) ,
    fulltext index `customer_color_name` (`customer_color_name`)

  )engine = InnoDB default charset = utf8;

如果你使用過mysql 5.6之前的版本,相信你肯定會對上面的ddl 語句有所疑問(可能在想,不是說mysql innodb 不支援全文索引麼?),我這裡使用的是mysql 5.7 ,mysql InnoDB 是在5.6後開始支援全文索引的。

在建立索引時,不一定要指定索引名,如果不指定,那麼mysql 會自己指定,預設是欄位名。我一般喜歡在索引名和欄位名加上反引號(``,鍵盤上 tab 上面的鍵),這樣可以降低出錯的概率。

 

3.2 刪除索引  

alter table dye_production_schedules
  drop index `order_id_order_detail_id`,
  drop index `dye_factory_id`,
  drop index `dye_code`,
  drop index `order_id_batch_num`,
  drop index `customer_color_name`;

 

 

4.索引的優缺點

  在說具體的優缺點前,先看下有索引和沒索引查詢相應時間的一個區別。

  由於資料量小體現不出差異,所以這裡寫了個定時任務,插入了60多萬條資料。使用show index from tableName 可以檢視tableName 的索引。

注:本機配置:8G 記憶體 + SATA 老硬碟, + ubuntu 16.04 系統。

這裡是使用主鍵查詢的

 

mysql> select count(*) from dye_production_schedules;
+----------+
| count(*) |
+----------+
|   622070 |
+----------+
1 row in set (1.86 sec)
mysql> select id ,dye_code ,customer_color_name from dye_production_schedules where id = 40000 ;
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的頭略略偏右仰著,嘴脣輕輕的動著,嘴脣以上,盡是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> explain select id ,dye_code ,customer_color_name from dye_production_schedules where id = 40000 ;
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table                    | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

 

這裡根據dye_code 這個欄位來查詢

 

mysql> select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a';
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的頭略略偏右仰著,嘴脣輕輕的動著,嘴脣以上,盡是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (1.66 sec)

mysql> select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a' limit 1;
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的頭略略偏右仰著,嘴脣輕輕的動著,嘴脣以上,盡是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.10 sec)

mysql> explain select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a' limit 1;
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table                    | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 471005 |    10.00 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)

mysql> 

 

不知道你還記不記得,前面第一部分內容說理解索引的時候,舉的查詞典栗子,說根據查詞典流程去理解mysql  查詢並不很準確,這是因為mysql在一次查詢完畢後,會做很多後續工作,其中就會嘗試把查詢結果快取起來,這裡的sql_no_cache 是指示mysql 不要對這條sql查詢結果進行快取。

通過explain 可以看到,當沒有索引查詢的時候,哪怕是指定了limit 1 ,也是全表掃描(type 值為ALL ),至於explain 的用法可以參考 https://blog.csdn.net/zhang_referee/article/details/83041301 。

我們再看一個排序的栗子:

我們按照`order_detail_id` 這個欄位來排序:

 

mysql> select sql_no_cache id ,order_id,order_detail_id ,dye_code ,customer_color_name from dye_production_schedules order by order_detail_id desc limit 10;
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
| id     | order_id | order_detail_id | dye_code       | customer_color_name                                                                                       |
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
| 595950 |     1076 |          100000 | e1004ec21380a5 | 35Fe6vimkq  你為我的撈什子書也費了不少神;第一回讓你父親的男傭人從家HyR4O                               |
| 107857 |     7301 |          100000 | a279af0d418d38 | QOvptosZXN字與字間的時距,我不能指明,只覺比普通人說話延長罷了;最令我USfR9                               |
| 249517 |     5463 |          100000 | a7682ca9b246a9 | 3ySBwHWLbG過的荷塘,在這滿月的光裡,總該另有一番樣子吧。月亮漸漸地升高dsZzQ                               |
| 218765 |     9783 |          100000 | 40a8c5c97ffa0c | ElAJFnO4W7。那邊學校當局要我約聖陶去。聖陶來信說:“我們要痛痛快快遊西a8k1d                                |
| 566500 |     3779 |           99999 | be6bc4fb7d2e00 | qjvSLdR6b3字。“人”讓他站著,“牛”也讓它站著;所饒不過的是“女”人,zcTJ9                                     |
| 164685 |     5037 |           99999 | 8b737b07d85bb3 | 8eMkXJrWqS快,不覺七點還欠五分了。這時票子還有許多人沒買著,大家都著急06RxO                               |
| 137008 |     1860 |           99999 | 9872f5c73533b5 | C9BaK4wsbm去,直到現在——中間又被朋友拉到福州一次,有一篇《將離》抒寫dplOM                                 |
| 370503 |     1334 |           99999 | 5efb7037d40e47 | eH7481tAlK,始終筆直的站著,幾乎不曾移過一步,真像石像一般,有著可怕的LN53s                               |
| 597563 |     7425 |           99999 | 0b95c11a4e30ee | 1POtQT2GdX的地方便是護城河,曼衍開去,曲曲折折,直到平山堂,——這是你oMwJb                                 |
| 507070 |     6182 |           99999 | dd3dc56d921e92 | nbfxzOZy62動都像不是他們自己的。好容易費了二虎之力,居然買了幾張票,憑cH8Gp                               |
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
10 rows in set, 1 warning (3.94 sec)

mysql> explain select sql_no_cache id ,order_id,order_detail_id ,dye_code ,customer_color_name from dye_production_schedules order by order_detail_id desc limit 10;
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table                    | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 471005 |   100.00 | Using filesort |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set, 2 warnings (0.01 sec)

該查詢不僅耗時久,通過explain  可以發現,使用了檔案排序。

 

下面就新增適當的索引來進行比較:

mysql>   alter table dye_production_schedules
    ->   add index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 複合索引
    ->   add index `dye_factory_id` (`dye_factory_id`),
    ->   add index `dye_code` (`dye_code`),
    ->   add index `order_detail_id` (`order_detail_id`),
    ->   add index `order_id_batch_num` (`order_id`,`batch_num`) ,
    ->   add fulltext index `customer_color_name` (`customer_color_name`);
Query OK, 0 rows affected (1 min 26.31 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules where dye_code = 'b5ac612c7a0139' limit 1;
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| id     | dye_code       | order_id | order_detail_id | customer_color_name                                                                                       |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| 100423 | b5ac612c7a0139 |     9006 |           54461 | 3CN92UGwlo是逃不了的。我說北平看花,比別處有意思,也正在此。這時候,我Wsrdi                               |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules where dye_code = 'b5ac612c7a0139' limit 1;
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table                    | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ref  | dye_code      | dye_code | 194     | const |    1 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 2 warnings (0.00 sec)

可以發現,在dye_code 上加上索引,查詢速度快了很多。

 

我們再次按照`order_detail_id` 這個欄位來排序:

mysql>  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules order by order_detail_id desc  limit 10;
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| id     | dye_code       | order_id | order_detail_id | customer_color_name                                                                                       |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| 595950 | e1004ec21380a5 |     1076 |          100000 | 35Fe6vimkq  你為我的撈什子書也費了不少神;第一回讓你父親的男傭人從家HyR4O                               |
| 249517 | a7682ca9b246a9 |     5463 |          100000 | 3ySBwHWLbG過的荷塘,在這滿月的光裡,總該另有一番樣子吧。月亮漸漸地升高dsZzQ                               |
| 218765 | 40a8c5c97ffa0c |     9783 |          100000 | ElAJFnO4W7。那邊學校當局要我約聖陶去。聖陶來信說:“我們要痛痛快快遊西a8k1d                                |
| 107857 | a279af0d418d38 |     7301 |          100000 | QOvptosZXN字與字間的時距,我不能指明,只覺比普通人說話延長罷了;最令我USfR9                               |
| 597563 | 0b95c11a4e30ee |     7425 |           99999 | 1POtQT2GdX的地方便是護城河,曼衍開去,曲曲折折,直到平山堂,——這是你oMwJb                                 |
| 566500 | be6bc4fb7d2e00 |     3779 |           99999 | qjvSLdR6b3字。“人”讓他站著,“牛”也讓它站著;所饒不過的是“女”人,zcTJ9                                     |
| 507070 | dd3dc56d921e92 |     6182 |           99999 | nbfxzOZy62動都像不是他們自己的。好容易費了二虎之力,居然買了幾張票,憑cH8Gp                               |
| 370503 | 5efb7037d40e47 |     1334 |           99999 | eH7481tAlK,始終筆直的站著,幾乎不曾移過一步,真像石像一般,有著可怕的LN53s                               |
| 164685 | 8b737b07d85bb3 |     5037 |           99999 | 8eMkXJrWqS快,不覺七點還欠五分了。這時票子還有許多人沒買著,大家都著急06RxO                               |
| 137008 | 9872f5c73533b5 |     1860 |           99999 | C9BaK4wsbm去,直到現在——中間又被朋友拉到福州一次,有一篇《將離》抒寫dplOM                                 |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
10 rows in set, 1 warning (0.11 sec)

mysql> explain  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules order by order_detail_id desc  limit 10;
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
| id | select_type | table                    | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | index | NULL          | order_detail_id | 4       | NULL |   10 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+

 

通過explain 分析得知,在沒有索引的時候,按照`order_detail_id` 這個欄位來排序,會做全表掃描檔案排序(哪怕是把整個表載入記憶體做排序也是檔案排序,因為explain 並不會告訴你這個區別,具體explain 用法可參考 :https://blog.csdn.net/zhang_referee/article/details/83041301),而在排序欄位上加了索引後,mysql 已無需再去做耗時的排序操作。

 

mysql> select count(*) from dye_production_schedules;
+----------+
| count(*) |
+----------+
|   622070 |
+----------+
1 row in set (0.17 sec)

mysql> explain select count(*) from dye_production_schedules;
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
| id | select_type | table                    | partitions | type  | possible_keys | key            | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | index | NULL          | dye_factory_id | 2       | NULL | 471005 |   100.00 | Using index |
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

 

 

優缺點:

  1. 索引能大大提高了查詢速度,卻會降低更新表的速度,如對錶進行insert、update和delete。因為更新表時,不僅要儲存資料,還要維護索引。
  2. 建立索引會佔用磁碟空間的索引檔案。一般情況這個問題不太嚴重,但如果你在一個大表上建立了多種組合索引,索引檔案的會增長很快。

 

在mysql 優化中,價效比最高的就是建立索引,建立索引不難,建立合適的索引(三星索引,這一說法來自於《高效能mysql》),卻不總是那麼容易的。

 

文章參考自

《高效能mysql 第三版》 (下載地址:https://pan.baidu.com/s/1haFdY7c9xb6VNtlfPUaidQ)

  mysql 官方手冊: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html