正確的理解MySQL的索引機制以及內部實現(一)
如果覺得對你有幫助,能否點個贊或關個注,以示鼓勵筆者呢?!博客目錄 | 先點這裏
- 第一部分 傾向於MySQL數據庫索引的日常生活,主要體現MySQL索引的應用
正確的理解MySQL的索引機制以及內部實現(一) - 第二部分 更傾向於講解MySQL B+樹索引的實現原理
正確的理解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
的最終數據,效率提升可想而知!
其他問題
索引是越建越多,越好嗎?
數據庫索引是越多越好嗎?非也非也 。雖然很多情況下, 建立索引能夠很好的加快數據庫的查詢, 但也不是什麼都需要建索引的,索引建的過多,反而會引起一定的不良作用,導致性能降低。比如說:
- 假設我們一個表的字段很多,但是實際行記錄很少。如果我們爲每個列都生成一個索引,這很容易就導致該表的索引文件遠大於行記錄數據本身,這就造成了一定的資源浪費,在佔據大量磁盤資源的基礎上,還因爲行數據量太小,索引根本起不來作用。
- 建立的索引越多,說明需要維護的索引樹越多。每當新增,刪除一個行記錄,或是更新單行或多行的列數據,都需要對涉及的索引樹進行維護。而維護的過程也是需要有性能消耗的,在涉及索引過多的情況下,每次的數據庫寫操作都需要耗費大量的時間, 這就大大降低了數據庫寫的性能
所以我們要注意索引原則中的三大不應該
:
- 索引不是越多越好。索引太多,維護索引需要時間,同時索引也需要佔用磁盤資源
- 頻繁更新的數據,不宜建索引。 數據頻繁更新,觸發索引頻頻維護,降低寫速度
- 數據量小的表沒必要建立索引。數據量過小,建索引等於多此一舉,還增加了操作複雜度
索引失效的場景
模糊搜索
,左模糊或全模糊都會導致索引失效,比如'%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) 執行SQL
explain select * from student where tel = 123451;
我們發現,因爲123451沒有引號,傳入數值類型。所以explain語句的分析中,type類型是ALL
,說明走的是全表查詢,並沒有走索引。
- (2) 執行SQL
explain select * from student where tel = '123451';
當我們給參數加上了引號,代表字符類型後,type類型變成了ref
,走的是普通索引,並沒有全部查詢
結論
- 再寫SQL語句的時候,要儘量的避免出現類型不一致的情況,因爲MySQL的隱式類型轉換,很可能會導致查詢沒有走索引,從而導致查詢性能低下。
參考資料
- 《MySQL技術內幕》
- 深入理解MySQL索引原理和實現——爲什麼索引可以加速查詢?
- 數據庫索引的作用和優點缺點以及索引的11中用法 - @作者:菜鳥程序猿
- mysql--------四種索引類型 - @作者:切切歆語
- Mysql聯合索引最左匹配原則 - @princekin
- 通俗易懂 索引、單列索引、複合索引、主鍵、唯一索引、聚簇索引、非聚簇索引、唯一聚簇索引 的區別與聯繫 - @知乎
- 如果覺得對你有幫助,能否點個贊或關個注,以示鼓勵筆者呢?!