【MySQL筆記】正確的理解MySQL的索引機制以及內部實現(一)

正確的理解MySQL的索引機制以及內部實現(一)


如果覺得對你有幫助,能否點個贊或關個注,以示鼓勵筆者呢?!博客目錄 | 先點這裏

因爲數據庫索引的知識點比較多,而且感覺比較複雜和混亂!所以爲了讓文章更加的清晰,最終在按原目錄結構寫了三分之二的時候,還是決定分爲兩個部分分開去描述(雖然還是有很多地方沒有去解釋)

剛花了幾小時擼了個py博客批量備份小工具,有需要可以拿走 https://github.com/SnailMann/CAB-Tool


  • 前提概要
    • 樣例表
    • 如何去學習MySQL的索引知識?
  • MySQL索引
    • 什麼是索引?
    • 索引的優缺點
    • 索引設計的原則
    • 索引相關SQL
  • MySQL有哪些索引
    • MySQL索引的分類
    • 單列索引
    • 組合索引
  • 其他相關問題
    • 索引是越建越多,越好嗎?
    • 不同類型字段隱式轉換導致索引失效

前提概要


說明的表

以下是用於解釋索引的樣例表

建表語句

create table `student` (
	`sid` int(11) not null,
	`name` varchar(20) not null,
	`age` int(11) not null,
	`tel` varchar(11) not null,
	`email` varchar(20) not null,
	`class` varchar(20) not null,
	primary key (`sid`),
	unique name(`name`),
	index age(`age`),
	index tel_email(`tel`,`email`)
) engine=innodb default charset=utf8;

插入數據

insert into student values(1,"Jerry",22,"123451","[email protected]","計算機1班");
insert into student values(2,"Tom",22,"123452","[email protected]","計算機2班");
insert into student  values(3,"Mark",20,"123453","[email protected]","計算機3班");
insert into student  values(4,"Jack",19,"123454","[email protected]","計算機4班");

表與數據
在這裏插入圖片描述

表中的索引

  • sid(學號)是主鍵索引
  • name(姓名)是唯一非空索引
  • age(年齡)是普通索引
  • tel(電話),email(郵箱)是組合索引
  • class(班級)列沒有生成索引

在這裏插入圖片描述


如何去學習MySQL的索引知識?

由於本文的主要性質是自己的筆記總結,是自己學習過程思路的整理。因爲索引的知識點的確比較繁瑣,所以簡單的我說一下我在學習數據庫索引機制的步驟和方式。

(一)首先要了解什麼是索引?索引是幹嘛呢,有什麼好處

  • 瞭解一下什麼是索引,它能給我們帶來什麼好處?
  • 索引的底層實現數據結構一般是什麼,不同存儲引擎的索引底層實現有區別嗎?
  • 我們使用索引需要注意什麼事情,按照什麼樣的原則去建立索引?

(二)瞭解MySQL在功能上,給我們提供了什麼索引?

  • 瞭解單列索引的主鍵索引,唯一索引,普通索引以及全文索引
  • 瞭解一下什麼是組合索引,明白組合索引的 最左前綴原則,然後大概就知道MySQL的索引要怎麼用了
  • 有時間最好要了解如何分析索引命中(explain),至少得知道怎麼纔算走了索引
  • 然後瞭解一下MySQL應用層面上的坑坑哇哇

(三)準備入門MySQL索引的底層實現(B+ Tree索引)

  • 因爲通常的MySQL索引底層實現主要是B+樹,所以需要先把索引會涉及的 基礎數據結構 打個補丁,比如二叉搜索樹,平衡二叉搜索樹,B樹,B+樹。
  • 區分一下索引底層實現的一些內部概念,如 主鍵索引(primary index), 輔助鍵索引(secondary index)
  • 再區分一下 聚簇索引(clustered index)和 非聚簇索引(non-clustered index)的概念分別是什麼意思?
  • 在這個階段,會很容易的出現概念性的混亂,因爲大家都叫索引,但卻有時候不是屬於一個維度的東西
  • 瞭解MySQL下InnoDB引擎MyISAM引擎分別通過B+ Tree索引查詢的過程

(四)如果你還想了解其他索引的實現的話,可以看看

  • 瞭解什麼是 BitMap索引
  • 瞭解 哈希索引自哈希索引
  • 瞭解全文檢索中的 倒排索引

索引


什麼是索引?

什麼是索引:

  • 索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息。如果想按特定職員的姓來查找他或她,則在表中搜索所有的行相比,索引有助於更快地獲取信息
  • 通俗的講,索引就是數據的目錄,就像看書一樣,假如我想看第三章第四節的內容,如果有目錄,我直接翻目錄,找到第三章第四節的頁碼即可。如果沒有目錄,我就需要將從書的開頭開始,一頁一頁翻,直到翻到第三章第四節的內容。

索引的優缺點

索引的優點:

  • 通過創建唯一索引,可以保證每一行數據的唯一性
  • 可以大大提高查詢速度
  • 可以加速表與表的連接
  • 可以顯著的減少查詢中分組和排序的時間
  • 通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

索引的缺點:

  • 創建索引需要時間,後期創建的索引,創建開銷時間與表數據量呈正相關
  • 創建索引時,需要對錶加鎖,在鎖表的同時,可能會影響到其他的數據操作
  • 索引需要磁盤的空間進行存儲,如果針對單表創建了大量的索引,可能比數據文件更快達到大小上限
  • 當對錶中的數據進行CRUD的時,也會觸發索引的維護,而維護索引需要時間,可能會降低數據操作的性能

索引設計的原則

不應該:

  • 索引不是越多越好。索引太多,維護索引需要時間,同時索引也需要佔用磁盤資源
  • 頻繁更新的數據,不宜建索引。 數據頻繁更新,觸發索引頻頻維護,降低寫速度
  • 數據量小的表沒必要建立索引。數據量過小,建索引等於多此一舉,還增加了操作複雜度

應該:

  • 重複率小的列建議生成索引。因爲重複數據少,索引樹查詢更有效率
  • 數據具有唯一性,建議生成唯一性索引。在數據庫的層面,保證數據正確性
  • 頻繁group by、order by的列建議生成索引。可以大幅提高分組和排序效率
  • 經常用於查詢條件的字段建議生成索引。通過索引查詢,速度更快

索引相關SQL

查看錶中的索引

show index from {table_name}

添加索引

# 創建表
CREATE TABLE {table_name}(  
    ...
    INDEX  {index_name}({column_name})  
);
# 修改表
alter table {table_name} add unique index {index_name}({column_name}); # 唯一索引
alter table {table_name} add index {index_name}({column_name});        # 普通索引
create index {index_name} on {table_name}({column_name});              # 普通索引

刪除索引

drop index {index_name} on {table_name};
alter table {table_name} drop index {index_name}

查看索引命中情況

explain select * from {table_name} where {column_name} = xxx;

對於Explain不清楚的同學,可以看這篇文章 理解索引:MySQL執行計劃詳細介紹 - @作者:情情說


MySQL有哪些索引


MySQL索引的分類

我們根據對以列屬性生成的索引大致分爲兩類:

  • 單列索引
    以該表的單個列,生成的索引樹,就稱爲該表的單列索引
  • 組合索引
    以該表的多個列組合,一起生成的索引樹,就稱爲該表的組合索引

然後單列索引又有具體細的劃分:

  • 主鍵索引
    以該表主鍵生成的索引樹,就稱爲該表的主鍵索引
  • 唯一索引
    以該表唯一列生成的索引樹,就稱爲該表的唯一索引
  • 普通索引
    以該表的普通列(非主鍵,非唯一列)生成的索引樹,就稱爲該表的普通索引
  • 全文索引

單列索引

主鍵索引

主鍵索引,既主索引,以主鍵列生成的索引,每張表只有一個主鍵索引,不允許重複,不允許有空值(Null)

alter table student drop index `PRIMARY`; # 刪除主索引
alter table student add primary key pk_index(`sid`); # 添加

基本查詢的索引命中
在這裏插入圖片描述

唯一索引

唯一索引,以唯一列生成的索引,該列不允許有重複值,但允許有空值(Null)

  • 在數據庫的角度中,NULL != NULL , 所以唯一索引列,可以由多個空值,但這樣的一個做飯,不符合我們的常規理解,所以要特別注意
  • 主鍵索引也是一種唯一索引,但主鍵索引不能有空值(Null)
alter table student drop index `name`; # 刪除主索引
alter table student add unique index name(`name`); # 添加

基本查詢的索引命中
在這裏插入圖片描述

普通索引

普通索引,以普通列生成的索引,沒有任何限制

alter table student drop index `age`; # 刪除主索引
alter table student add index age(`age`); # 添加

此時的age是普通索引,class無索引,分別根據age和class進行查詢,使用explain分析語句,會發現第一個的type是ref,代表使用了索引。第二張圖的type是ALL, 代表全部查詢
在這裏插入圖片描述
在這裏插入圖片描述

全文索引

全文索引通常使用大文本對象的列去構建索引,索引底層實現是FULLINDEX

alter table student add fulltext index ft_index(`class`); # 添加全文索引
alter table student drop index `ft_index`;                # 刪除全文索引

在這裏插入圖片描述


組合索引

組合索引

組合索引,相對單列索引,組合索引是用多個列組合構建的索引

alter table student add index tel_email(`tel`,`email`); # 添加tel列和email列的組合索引
alter table student drop index `tel_email`;             # 刪的tel_email組合索引

通常,組合索引又多個列組成,所以有時有可能因爲多個列名過長,導致正棵組合索引樹的鍵大小過大,降低了存儲和查詢的效率。所以爲了避免出現這樣的情況,可以適當的保證每一列的名字不要太長,或只取組合索引每一列前幾個字符組成索引

最左前綴匹配原則

最左前綴匹配原則

  • 在MySQL建立組合索引時會遵循最左前綴匹配的原則,即最左優先,在檢索數據時從組合索引的最左邊開始匹配
alter table student add index tel_email(`tel`,`email`); 

比如我們的說明表中,我們以tel列email列生成一個組合索引,按照最左優先,我們要把最常作爲檢索或排序的列放在最左邊,依次遞減。因爲這個組合索引相當於建立了(tel),(tel,email)兩個索引。因爲我們的樣例只有連個列,效果不夠明顯。我們以三個列的組合索引舉例,如下

alter table table_name add index col1_col2_col3(`col1`,`col2`,col3); 

那麼根據最左前綴匹配原則,最終相當於生成(col1),(col1,col2),(col1,col2,col3)三個索引。哈哈,有看出什麼問題嗎?那就是這樣三個列的組合索引並不是全排列,是有嚴格順序的最左優先匹配,既跟(col2),(col3),(col2,col3)等索引沒有必要聯繫,不會產生與他們等價的效果

所以使用組合索引時一定要記住這一點,最查用的匹配列,在建索引時,優先放在最左邊。另外組合索引實際還是一個索引,並非真的創建了多個索引,只是產生的效果等價於產生多個索引。

使用組合索引的好處
  • 減少開銷
    建一個組合索引(col1,col2,col3),實際產生的作用等價於建了(col1),(col1,col2),(col1,col2,col3)三個索引。而我們知道,索引也是站磁盤空間的,每多一個索引,不僅增加磁盤空間的開銷,還多了一棵索引的查詢。對於大量數據的表,使用聯合索引會大大的減少開銷!
  • 覆蓋索引(下一篇會說)
    對於組合索引(col1,col2,col3),如果有如下的select col1,col2,col3 from table where col1=1 and col2=2那麼MySQL可以通過直接一遍歷次該組合索引,便可以取到col1,col2,col3三列的數據,而無需回表,這就減少了很多的IO操作
  • 效率高
    索引列越多,通過索引篩選出的數據越少。有1000w條數據的表,如果有如下的select from table where col1=1 and col2=2 and col3=3的SQL。假設每個條件可以篩選出10%的數據,如果三個條件均是單列索引,那麼通過col1索引能篩選出1000w * 10%=100w條數據,然後再回表從100w條數據中找到符合col2=2的數據(100w * 10% = 10w), 依次回表,最後從結果數據中找出滿足col3= 3的數據。而如果是組合索引,通過一棵索引樹就能直接篩選出1000w * 10% * 10% * 10% = 1w的最終數據,效率提升可想而知!

Mysql聯合索引最左匹配原則 - @princekin


其他問題


索引是越建越多,越好嗎?

數據庫索引是越多越好嗎?非也非也 。雖然很多情況下, 建立索引能夠很好的加快數據庫的查詢, 但也不是什麼都需要建索引的,索引建的過多,反而會引起一定的不良作用,導致性能降低。比如說:

  • 假設我們一個表的字段很多,但是實際行記錄很少。如果我們爲每個列都生成一個索引,這很容易就導致該表的索引文件遠大於行記錄數據本身,這就造成了一定的資源浪費,在佔據大量磁盤資源的基礎上,還因爲行數據量太小,索引根本起不來作用。
  • 建立的索引越多,說明需要維護的索引樹越多。每當新增,刪除一個行記錄,或是更新單行或多行的列數據,都需要對涉及的索引樹進行維護。而維護的過程也是需要有性能消耗的,在涉及索引過多的情況下,每次的數據庫寫操作都需要耗費大量的時間, 這就大大降低了數據庫寫的性能

所以我們要注意索引原則中的三大不應該:

  • 索引不是越多越好。索引太多,維護索引需要時間,同時索引也需要佔用磁盤資源
  • 頻繁更新的數據,不宜建索引。 數據頻繁更新,觸發索引頻頻維護,降低寫速度
  • 數據量小的表沒必要建立索引。數據量過小,建索引等於多此一舉,還增加了操作複雜度

索引失效的場景

  • 模糊搜索,左模糊或全模糊都會導致索引失效,比如'%key''%key%'。但是右模糊是可以利用索引的,比如'key%'
  • 隱式類型轉換,比如select * from name = xxx , name是字符串類型,但是沒有加引號,所以是由MySQL隱式轉換的,所以會讓索引失效
  • 當語句中帶有or的時候,比如select * from table where name=‘snailmann’ or age=20
  • 不符合聯合索引的最左前綴匹配, (A,B,C)的聯合索引,你只where了C或B或只有B,C

不同類型字段隱式轉換導致索引失效

問題描述
假設,我們要指向一個查詢語句select * from xxx where xxx = xxx,查詢條件是建立了索引的字段,字段類型爲varchar, 但我們的SQL條件實際傳入的是int類型,那麼猜測一下,這條查詢語句會走索引嗎?

測試數據
student表定義
在這裏插入圖片描述
student表的索引定義

在這裏插入圖片描述

測試

我們的student表的tel字段是一個組合索引,類型是varchar。所以我們就拿tel電話字段來測試一下

  • (1) 執行SQLexplain select * from student where tel = 123451;
    我們發現,因爲123451沒有引號,傳入數值類型。所以explain語句的分析中,type類型是ALL,說明走的是全表查詢,並沒有走索引。
    在這裏插入圖片描述
  • (2) 執行SQLexplain select * from student where tel = '123451';
    當我們給參數加上了引號,代表字符類型後,type類型變成了ref,走的是普通索引,並沒有全部查詢
    在這裏插入圖片描述

結論

  • 再寫SQL語句的時候,要儘量的避免出現類型不一致的情況,因爲MySQL的隱式類型轉換,很可能會導致查詢沒有走索引,從而導致查詢性能低下。

參考資料


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章