淺談mysql數據庫索引

索引類型

索引的建立對于MySQL的高效運行是很重要的,索引可以大大提高MySQL的檢索速度。mysql索引大致可以分為普通索引、唯一索引、主鍵索引、組合索引、全文索引,下面我們就來具體了解下各個索引的區別:

普通索引

基本的索引類型,值可以為空,沒有唯一性的限制。

- 直接創建索引
CREATE INDEX index_name ON table_name(col_name);
-- 修改表結構的方式添加索引
ALTER TABLE table_name ADD INDEX index_name(col_name);

-- 創建表的時候同時創建索引
CREATE TABLE `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `name` varchar(255)  NOT NULL ,
    `age` int(4)  NULL ,
    PRIMARY KEY (`id`),
    INDEX index_name (age(4))
)

-- 刪除索引
DROP INDEX index_name ON table_name;
--  或
alter table `表名` drop index 索引名;
唯一索引

索引列中的值必須是唯一的,但是允許為空值(只允許存在一條空值)。

-- 創建單個索引
CREATE UNIQUE INDEX index_name ON table_name(col_name);
-- 創建多個索引
CREATE UNIQUE INDEX index_name on table_name(col_name,...);

-- 修改表結構
-- 單個
ALTER TABLE table_name ADD UNIQUE index index_name(col_name);
-- 多個
ALTER TABLE table_name ADD UNIQUE index index_name(col_name,...);
主鍵索引

主鍵是一種唯一性索引,但它必須指定為PRIMARY KEY,每個表只能有一個主鍵。

-- 主鍵索引(創建表時添加)
CREATE TABLE `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `name` varchar(255)  NOT NULL ,
    `age` int(4)  NULL ,
    PRIMARY KEY (`id`)
)

-- 主鍵索引(創建表后添加)
CREATE TABLE `order` (
    `orderId` varchar(36) NOT NULL,
    `productId` varchar(36)  NOT NULL ,
    `time` varchar(20) NULL DEFAULT NULL
)
alter table `order` add primary key(`orderId`);
組合索引

在多個字段上創建的索引。組合索引遵守“最左前綴”原則,即在查詢條件中使用了復合索引的第一個字段,索引才會被使用。因此,在復合索引中索引列的順序至關重要。

-- 創建一個復合索引
create index index_name on table_name(col_name1,col_name2,...);

-- 修改表結構的方式添加索引
alter table table_name add index index_name(col_name,col_name2,...);
全文索引

全文索引的索引類型為FULLTEXT。全文索引可以在varchar、char、text類型的列上創建。可以通過ALTER TABLECREATE INDEX命令創建。對于大規模的數據集,通過ALTER TABLE(或CREATE INDEX)命令創建全文索引要比把記錄插入帶有全文索引的空表更快。MyISAM支持全文索引,InnoDB在mysql5.6之后支持了全文索引。 全文索引不支持中文需要借sphinx(coreseek)迅搜<、code>技術處理中文。

-- 創建表的適合添加全文索引
CREATE TABLE `news` (
    `id` int(11) NOT NULL AUTO_INCREMENT ,
    `title` varchar(255)  NOT NULL ,
    `content` text  NOT NULL ,
    `time` varchar(20) NULL DEFAULT NULL ,
     PRIMARY KEY (`id`),
    FULLTEXT (content)
)

-- 修改表結構添加全文索引
ALTER TABLE table_name ADD FULLTEXT index_fulltext_content(col_name);

索引實現

下面我們來具體看下載不同的數據庫引擎下上述索引的實現原理:

innodb索引
主鍵索引

每個InnoDB表都有一個主鍵索引(也叫聚簇索引) ,聚簇索引使用B+樹構建,葉子節點存儲的數據是整行記錄。。InnoDB創建索引的具體規則如下:

  • 在表上定義主鍵PRIMARY KEY,InnoDB將主鍵索引用作聚簇索引

  • 如果表沒有定義主鍵,且存在非空unique列,則選擇第一個非空unique列為主鍵,構建聚簇索引;

  • 如果以上兩個都沒有,InnoDB 會使用一個6 字節長整型的隱式字段 ROWID字段構建聚簇索引。該ROWID字段會在插入新行時自動遞增

除聚簇索引之外的所有索引都稱為輔助索引。在中InnoDB,輔助索引中的葉子節點存儲的數據是該行的主鍵值。 在檢索時,InnoDB使用此主鍵值在聚簇索引中搜索行記錄。 主鍵索引的葉子節點會存儲數據行(所以一般進行的非主鍵索引檢索至少需要經歷兩次磁盤IO),輔助索引只會存儲主鍵值。具體主鍵索引存儲結構如下:

202010241146330.png

等值查詢數據

  • 先在主鍵樹中從根節點開始檢索,將根節點加載到內存,比較28<75,走左路。(1次磁盤IO)

  • 將左子樹節點加載到內存中,比較16<28<47,向下檢索。(1次磁盤IO)

  • 檢索到葉節點,將節點加載到內存中遍歷,比較16<28,18<28,28=28。查找到值等于28的索引項,直接可以獲取整行數據。將改記錄返回給客戶端。(1次磁盤IO)

磁盤IO數量:3次。

20201024114716460 (1).png
輔助索引

除聚簇索引之外的所有索引都稱為輔助索引,InnoDB的輔助索引只會存儲主鍵值而非磁盤地址。以表user的age列為例,age索引的索引結果如下圖:

20201024114750255.png

底層葉子節點的按照(age,id)的順序排序,先按照age列從小到大排序,age列相同時按照id列從小到大排序。使用輔助索引需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后使用主鍵到主索引中檢索獲得記錄。

場景一:等值查詢的情況

select * from t_user_innodb where age=19;
2020102411481097.png

根據在輔助索引樹中獲取的主鍵id,到主鍵索引樹檢索數據的過程稱為回表查詢。

磁盤IO數:輔助索引3次+獲取記錄回表3次

組合索引

還是以自己創建的一個表為例:表 abc_innodb,id為主鍵索引,創建了一個聯合索引idx_abc(a,b,c)。

CREATE TABLE `abc_innodb`
(
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a`  int(11)     DEFAULT NULL,
  `b`  int(11)     DEFAULT NULL,
  `c`  varchar(10) DEFAULT NULL,
  `d`  varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_abc` (`a`, `b`, `c`)
) ENGINE = InnoDB;

組合索引的數據結構:

20201024114900213.png

組合索引的查詢過程:

select * from abc_innodb where a = 13 and b = 16 and c = 4;
20201024115012887.png

最左匹配原則

最左前綴匹配原則和聯合索引的索引存儲結構和檢索方式是有關系的。

在組合索引樹中,最底層的葉子節點按照第一列a列從左到右遞增排列,但是b列和c列是無序的,b列只有在a列值相等的情況下小范圍內遞增有序,而c列只能在a,b兩列相等的情況下小范圍內遞增有序。

就像上面的查詢,B+樹會先比較a列來確定下一步應該搜索的方向,往左還是往右。如果a列相同再比較b列。但是如果查詢條件沒有a列,B+樹就不知道第一步應該從哪個節點查起。

可以說創建的idx_abc(a,b,c)索引,相當于創建了(a)、(a,b)(a,b,c)三個索引。、

組合索引的最左前綴匹配原則:使用組合索引查詢時,mysql會一直向右匹配直至遇到范圍查詢(>、<、between、like)就停止匹配。

覆蓋索引

覆蓋索引并不是說是索引結構,覆蓋索引是一種很常用的優化手段。因為在使用輔助索引的時候,我們只可以拿到主鍵值,相當于獲取數據還需要再根據主鍵查詢主鍵索引再獲取到數據。但是試想下這么一種情況,在上面abc_innodb表中的組合索引查詢時,如果我只需要abc字段的,那是不是意味著我們查詢到組合索引的葉子節點就可以直接返回了,而不需要回表。這種情況就是覆蓋索引。可以看一下執行計劃:

MyIsam索引

MyISAM的數據文件和索引文件是分開存儲的。MyISAM使用B+樹構建索引樹時,葉子節點中存儲的鍵值為索引列的值,數據為索引所在行的磁盤地址。

主鍵索引
20201024114325883.png

表user的索引存儲在索引文件user.MYI中,數據文件存儲在數據文件 user.MYD中。簡單分析下查詢時的磁盤IO情況:

場景一:根據主鍵等值查詢數據

20201024114404727.png
  • 先在主鍵樹中從根節點開始檢索,將根節點加載到內存,比較28<75,走左路。(1次磁盤IO)

  • 將左子樹節點加載到內存中,比較16<28<47,向下檢索。(1次磁盤IO)

  • 檢索到葉節點,將節點加載到內存中遍歷,比較16<28,18<28,28=28。查找到值等于30的索引項。(1次磁盤IO)

  • 從索引項中獲取磁盤地址,然后到數據文件user.MYD中獲取對應整行記錄。(1次磁盤IO)

  • 將記錄返給客戶端。

磁盤IO次數:3次索引檢索+記錄數據檢索。

場景二:根據主鍵范圍查詢數據

20201024114510253.png
  • 先在主鍵樹中從根節點開始檢索,將根節點加載到內存,比較28<75,走左路。(1次磁盤IO)

  • 將左子樹節點加載到內存中,比較16<28<47,向下檢索。(1次磁盤IO)

  • 檢索到葉節點,將節點加載到內存中遍歷比較16<28,18<28,28=28<47。查找到值等于28的索引項。

    • 根據磁盤地址從數據文件中獲取行記錄緩存到結果集中。(1次磁盤IO)

    • 我們的查詢語句時范圍查找,需要向后遍歷底層葉子鏈表,直至到達最后一個不滿足篩選條件。

  • 向后遍歷底層葉子鏈表,將下一個節點加載到內存中,遍歷比較,28<47=47,根據磁盤地址從數據文件中獲取行記錄緩存到結果集中。(1次磁盤IO)

  • 最后得到兩條符合篩選條件,將查詢結果集返給客戶端。

磁盤IO次數:4次索引檢索+記錄數據檢索。

備注:以上分析僅供參考,MyISAM在查詢時,會將索引節點緩存在MySQL緩存中,而數據緩存依賴于操作系統自身的緩存,所以并不是每次都是走的磁盤,這里只是為了分析索引的使用過程。

輔助索引

在 MyISAM 中,輔助索引和主鍵索引的結構是一樣的,沒有任何區別,葉子節點的數據存儲的都是行記錄的磁盤地址。只是主鍵索引的鍵值是唯一的,而輔助索引的鍵值可以重復。查詢數據時,由于輔助索引的鍵值不唯一,可能存在多個擁有相同的記錄,所以即使是等值查詢,也需要按照范圍查詢的方式在輔助索引樹中檢索數據。

索引失效情況

情況一:where語句中包含or時,可能會導致索引失效

使用or并不是一定會使索引失效,你需要看or左右兩邊的查詢列是否命中相同的索引。

-- 假設user表中的user_id列有索引,age列沒有索引
-- 能命中索引
select * from user where user_id = 1 or user_id = 2;
-- 無法命中索引
select * from user where user_id = 1 or age = 20;
-- 假設age列也有索引的話,依然是無法命中索引的
select * from user where user_id = 1 or age = 20;

可以根據情況盡量使用union all或者in來代替,這兩個語句的執行效率也比or好些。

情況二:where語句中索引列使用了負向查詢,可能會導致索引失效

負向查詢包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等。其實負向查詢并不絕對會索引失效,這要看MySQL優化器的判斷,全表掃描或者走索引哪個成本低了。

情況三:索引字段可以為null,使用is null或is not null時,可能會導致索引失效

其實單個索引字段,使用is null或is not null時,是可以命中索引的。

情況四:在索引列上使用內置函數,一定會導致索引失效

比如下面語句中索引列login_time上使用了函數,會索引失效:

select * from user where DATE_ADD(login_time, INTERVAL 1 DAY) = 7;

情況五:隱式類型轉換導致的索引失效

如下面語句中索引列user_id為varchar類型,不會命中索引:

select * from user where user_id = 12;

情況六:對索引列進行運算,一定會導致索引失效

運算如+,-,*,/等,如下:

select * from user where age - 1 = 10;

優化的話,要把運算放在值上,或者在應用程序中直接算好,比如:

select * from user where age = 10 - 1;

情況七:like通配符可能會導致索引失效

like查詢以%開頭時,會導致索引失效。解決辦法有兩種:

  • 將%移到后面,如:
select * from user where `name` like '李%';
  • 利用覆蓋索引來命中索引:
select name from user where `name` like '%李%';

情況八:MySQL優化器的最終選擇,不走索引
上面有提到,即使完全符合索引生效的場景,考慮到實際數據量等原因,最終是否使用索引還要看MySQL優化器的判斷。當然你也可以在sql語句中寫明強制走某個索引。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容