MySQL - 索引詳解

索引是用來快速檢索出具備特定值的記錄。若是沒有索引,數據庫就必須從第一條記錄開始進行全表掃描,直到找出相關的行。數據越多,檢索的代價就越高,檢索時若是表的列存在索引,那麼 MySQL就能快速到達指定位置去搜索數據文件,而沒必要查看全部數據。

<!-- more -->java

概述

索引依託於存儲引擎的實現,所以,每種存儲引擎的索引都不必定徹底相同,而且每種存儲引擎也不必定支持全部索引類型。全部存儲引擎支持每一個表至少16個索引,總索引長度至少爲256字節。大多數存儲引擎有更高的額限制。mysql

MySQL中索引的存儲類型有兩種:BTREE和HASH,具體和表的存儲引擎相關;sql

MyISAM和InnoDB存儲引擎只支持BTREE索引,MEMORY/HEAP存儲引擎能夠支持HASH和BTREE索引數據庫

優勢
  • 加快數據的查詢速度
  • 惟一索引,能夠保證數據庫表中每一行數據的惟一性
  • 在實現數據的參考完整性方面,能夠加速表和表之間的鏈接
  • 在使用分組和排序子句進行數據查詢時,也能夠顯著減小查詢中分組和排序的時間
缺點
  • 佔用磁盤空間,除了數據表佔數據空間以外,每個索引還要佔必定的物理空間,若是有大量的索引,索引文件可能比數據文件更快達到最大文件尺寸(合理運用,問題不大
  • 損耗性能(添加、修改、刪除) 索引須要動態地維護

分類

普通索引和惟一索引
  • 普通索引: 數據庫中的基本索引類型,容許在定義索引的列中插入重複值和空值
  • 惟一索引:索引列的值必須惟一,但容許有空值,主鍵索引是一種特殊的惟一索引,不容許有空值(好比自增ID)
單列索引和組合索引
  • 單列索引: 即一個索引只包含單個列,一個表能夠有多個單列索引
  • 組合索引: 指在表的多個字段組合上建立的索引,只有在查詢條件中使用了這些字段的左邊字段時,索引纔會被使用
全文索引
  • 全文索引: 類型爲FULLTEXT,在定義索引的列上支持值的全文查找,容許在這些索引列中插入重複值和空值。全文索引能夠在CHAR、VARCHAR或者TEXT類型的列上建立,MySQL中只有MyISAM存儲引擎支持全文索引

設計原則

索引設計不合理或者缺乏索引都會對數據庫和應用程序的性能形成障礙,高效的索引對於得到良好的性能很是重要。微信

注意事項
  1. 索引並不是越多越好,一個表中若有大量的索引,不只佔用磁盤空間,並且會影響INSERT、DELETE、UPDATE等語句的性能,由於當表中的數據更改的同時,索引也會進行調整和更新
  2. 避免對常常更新的表設計過多的索引,而且索引中的列儘量要少,而對常常用於查詢的字段應該建立索引,但要避免添加沒必要要的字段
  3. 數據量小的表最好不要使用索引,因爲數據較少,查詢花費的時間可能比遍歷索引時間還要短,索引可能不會產生優化效果
  4. 在條件表達式中常常用到的不一樣值較多的列上創建索引,在不一樣值較少的列上不要創建索引,好比性別字段只有男和女,就不必創建索引。若是創建索引不但不會提升查詢效率,反而會嚴重下降更新速度
  5. 當惟一性是某種數據自己的特徵時,指定惟一索引。使用惟一索引需能確保定義的列的數據完整性,以提升查詢速度
  6. 在頻繁排序或分組(即group by或order by操做)的列上創建索引,若是待排序的列有多個,能夠在這些列上創建組合索引

使用

使用 CREATE TABLE 建立表的時候,除了能夠定義列的數據類型,還能夠定義主鍵約束、外鍵約束或者惟一性約束,而不論建立哪一種約束,在定義約束的同時至關於在指定列上建立了一個索引。架構

建立表時建立索引的基本語法以下:
CREATE TABLE table_name[col_name data_type]
[UNIQUE|FULLTEXT|SPATIAL]
[INDEX|KEY]
[index_name](col_name[length])
[ASC|DESC]
釋義
  1. UNIQUE、FULLTEXT和SPATIAL爲可選參數,分別表示惟一索引、全文索引和空間索引
  2. INDEX和KEY爲同義詞,兩者做用相同,用來指定建立索引
  3. col_name爲須要建立索引的字段列,該列必須從數據表中該定義的多個列中選擇
  4. index_name爲指定索引的名稱,爲可選參數,若是不指定則MySQL默認col_name爲索引值
  5. length爲可選參數,表示索引的長度,只有字符串類型的字段才能指定索引長度
  6. ASC或DESC指定升序或者降序的索引值存儲

普通索引

-- 這句做用是,若是 customer1 存在就刪除
DROP TABLE IF EXISTS customer1;
CREATE TABLE `customer1` (
  `customer_id` bigint(20) NOT NULL COMMENT '客戶ID',
  `customer_name` varchar(30) DEFAULT NULL COMMENT '客戶姓名',
  INDEX `idx_customer_id` (`customer_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客戶表';
測試
-- 查看當前表的索引狀況
SHOW INDEX FROM customer1; 

-- 使用 EXPLAIN 分析 SQL語句 是否使用了索引
EXPLAIN SELECT * FROM customer1 WHERE customer_id = 1;

查看結果

釋義

EXPLAIN 語法下章會詳細講解,本章重心是索引性能

  • select_type: 指定所使用的SELECT查詢類型,這裏值爲SIMPLE,表示簡單的SELECT,不使用UNION或者子查詢。其餘取值有PRIMARY、UNION、SUBQUERY、等
  • table: 指定數據庫讀取的數據表的名字,它們按照被讀取的前後順序排列
  • type: 指定了本數據表與其餘數據表之間的關聯關係,其它取值有system、const、eq_ref、ref、range、index和All
  • possible_keys: MySQL在搜索數據記錄時可選用的各個索引
  • key: MySQL使用的實際索引
  • key_len: 給出了索引按字節計算的長度,key_len數值越小,表示越快
  • ref: 提供了關聯關係中另一個數據表裏的數據列的名字
  • rows:MySQL執行查詢時預計從當前數據表中讀出的數據行數
  • Extra: 提供了與關聯操做有關的信息

SHOW INDEX FROM 語法測試

  • table: 表示建立索引的表
  • Non_unique: 表示索引不是一個惟一索引,1表示非惟一索引,0表示惟一索引
  • Key_name: 表示索引的名稱
  • Seq_in_index: 表示該字段在索引中的位置,單列索引改值該值爲1,組合索引爲每一個字段在索引中定義的順序
  • Column_name: 表示定義索引的列字段
  • Sub_part: 表示索引的長度
  • Null: 表示該字段是否能爲空值
  • Index_type: 表示索引類型

possible_keyskey 都爲 idx_customer_id,說明查詢時使用了索引優化

惟一索引

單列索引是在數據表中的某一個字段上建立的索引,一個表中能夠建立多個單列索引,前面兩個例子中建立的索引都是單列索引,好比:spa

DROP TABLE
IF EXISTS customer1;

CREATE TABLE `customer1` (
    `customer_id` BIGINT (20) NOT NULL COMMENT '客戶ID',
    `customer_name` VARCHAR (30) DEFAULT NULL COMMENT '客戶姓名',
    UNIQUE INDEX `idx_customer_id` (`customer_id`) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = '客戶表';

這樣就表明在表的customer_id字段上建立了一個名爲idx_customer_id的惟一索引

組合索引

組合索引是在多個字段上建立一個索引,好比:

DROP TABLE
IF EXISTS customer1;

CREATE TABLE `customer1` (
    `customer_id` BIGINT (20) NOT NULL COMMENT '客戶ID',
    `customer_name` VARCHAR (30) DEFAULT NULL COMMENT '客戶姓名',
     INDEX `idx_group_customer` (`customer_id`,`customer_name`) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = '客戶表';

SHOW INDEX FROM customer1;

這就爲customer_id、customer_name兩個字段成功建立了一個名爲idx_group_customer的組合索引,經過SHOW INDEX FROM customer1; 將會看到兩條記錄(附圖)

查看結果

全文索引

全文索引能夠對全文進行搜索,只有MyISAM存儲引擎支持全文索引,而且只爲CHAR、VARCHAR和TEXT列,索引老是對整個列進行,不支持局部索引,好比:

DROP TABLE
IF EXISTS customer1;

CREATE TABLE `customer1` (
    `customer_id` BIGINT (20) NOT NULL COMMENT '客戶ID',
    `customer_name` VARCHAR (255) DEFAULT NULL COMMENT '客戶姓名',
     FULLTEXT INDEX `idx_fulltext_customer_name` (`customer_name`)
) ENGINE = MyISAM DEFAULT CHARSET = utf8mb4 COMMENT = '客戶表';

SHOW INDEX FROM customer1;

由於默認的存儲引擎爲InnoDB,而全文索引只支持MyISAM,因此這裏建立表的時候要手動指定一下引擎。

看到這麼建立,就在info字段上成功創建了一個名爲idx_fulltext_customer_name的FULLTEXT全文索引,全文索引很是適合大型數據庫,而對於小的數據集,它的用處可能比較小

在已經存在的表上建立索引

在已經存在的表上建立索引,可使用ALTER TABLE語句或者CREATE INDEX語句,因此,分別講解一下如何使用ALTER TABLE和CREATE INDEX語句在已知的表字段上建立索引。

ALTER TABLE 語法

ALTER TABLE建立索引的基本語法爲:

ALTER TABLE table_name ADD [UNIQUE|FUUTEXT|SPATIAL]
[INDEX|KEY] [index_name] (col_name[length],...) [ASC|DESC]
普通索引
ALTER TABLE customer1 ADD INDEX idx_customer_id(`customer_id`);

ALTER TABLE customer1 ADD INDEX idx_customer_id(customer_name(50));

意思是查詢的時候,只須要檢索前面50個字符。這裏專門提一下,對字符串類型的字段進行索引,若是能夠儘量的指定一個前綴長度,例如,一個CHAR(255)的列,若是在前10個或者前30個字符內,多數值是惟一的,則不須要對整個列進行索引,短索引不只能夠提升查詢速度並且能夠節省磁盤空間、減小I/O操做。

惟一索引
ALTER TABLE customer1 ADD UNIQUE INDEX `idx_customer_id` (`customer_id`);
組合索引
ALTER TABLE customer1 ADD INDEX `idx_group_customer` (`customer_id`,`customer_name`);

CREATE TABLE 語法

CREATE INDEX語句能夠在已經存在的表上添加索引,MySQL中CREATE INDEX被映射到一個ALTER TABLE語句上,基本語法結構爲:

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name ON table_name(col_name[length],...)[ASC|DESC]

看到和ALTER INDEX語句的語法基本同樣,下面把 customer1 表刪除了再建立,全部字段都沒有索引,用CREATE INDEX語句建立一次索引:

CREATE INDEX idx_customer_id ON customer1(`customer_id`);

CREATE UNIQUE INDEX idx_customer_id ON customer1(`customer_id`);

CREATE INDEX idx_group_customer ON customer1(`customer_id`,`customer_name`);

刪除索引

最後一項工做就是刪除索引了,可使用ALTER TABLE和DROP INDEX刪除索引。

ALTER TABLE 語法

ALTER TABLE的基本語法爲:

ALTER TABLE table_name DROP EXISTS index_name;

ALTER TABLE table_name DROP INDEX IF EXISTS index_name;

建議你們使用第二條

DROP INDEX 語法

DROP INDEX的基本語法爲:

DROP INDEX index_name ON table_name

DROP INDEX IF EXISTS  index_name ON table_name

建議你們使用第二條

注意一個細節,刪除表中的列時,若是要刪除的列爲整個索引的組成部分,則該列也會從索引中刪除;若是組成索引的全部列都被刪除,則整個索引將被刪除

說點什麼

關注微信公衆號:battcn 後臺回覆 mysql 便可得到 《打造扛得住的MySQL數據庫架構》

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659
相關文章
相關標籤/搜索