常常在開發中碰到同事說,數據查詢好慢,第一個反應就是給表加個索引。從而引起想去探索下咱們常說的索引到底是什麼?難道只須要加個索引就能解決數據庫查詢問題嗎?mysql
帶着這個問題咱們開始探究MySQL中的索引到底是什麼,它能幫助咱們作些什麼。sql
在現有程序業務中,數據庫做爲存儲的重要一環,不可或缺,而對於數據庫的操做無外乎是增刪改查,但隨着數據量的增長,數據庫的性能就成爲最重要的一環,數據查詢不能慢,數據查詢一慢,用戶體驗就會差。數據庫
如何在保證數據存儲中的增刪改查效率呢?就成了一個必不可少的設計。編程
在Mysql這樣的數據存儲中老是少不了一個東西--->索引,索引就相似於咱們看書的目錄,使用書籍的目錄能夠幫助快速的定位到知識點的頁數,而索引也是一樣的目的,快速檢索到數據。數據結構
那就能夠總結出索引的目的:提升數據的檢索速度。函數
既然索引有提升檢索的速度,那就給數據庫的查詢操做都加上索引,讓他們飛快的運行,這事還真不能急,爲啥?數據庫的索引種類有好多種,萬一索引用的不對,引起的不是加快數據庫的運行,而是衆多的慢查詢會將整個數據庫拖垮。性能
用於提升讀寫效率的數據結構種類這麼多,那咱們來了解下數據庫中常見的索引類型都有哪些。優化
索引類型 | 哈希索引 | 二叉樹 | 跳錶 | B+Tree |
---|
哈希索引簡單來講就是Key-Value模型,咱們只要經過給定的Key就能夠查找到對應的Value,十分快速方便。spa
不過你要明白哈希索引是經過哈希函數對Key進行計算,換算成數據存儲位置,隨着數據量的增長,不可避免會出現不一樣的Key通過哈希函數計算後出現相同的數據存儲位置。設計
這種狀況怎麼解決呢?業界通用的方式是當出現位置同樣的數據結果,會在該結果後面連接一個鏈表,將相同的數據放入到鏈表中。
更進一步當相同位置中的數據愈來愈多,查詢數據時會將鏈表中的數據遍歷,速度也是慢,這時候能夠採用將鏈表進行樹化,二叉樹的查找速度仍是很快的。
⬇️圖是數據舉例說明:
哈希索引只適合用來查找等值的數據,而不是適合範圍索引,排序等操做。常見的哈希索引是在Redis中。
數據結構中存在樹數據結構,雖然樹的結構多種多樣,可是經常使用的數據結構是二叉樹,二叉樹是擁有兩個分叉的樹,分別爲左子節點與右子節點。以此類推,動物中有八爪魚,一樣的也存在八叉樹,你能夠想象八叉樹是什麼樣子。
二叉樹的特色是,左節點的值<父節點<右節點。若是要查找到一個值就能夠按照子節點的順序進行查找.
隨着數據量的增大,二叉樹的高度也會主鍵遞增,數據庫存儲的數據並非都放到內存中,而是要放到磁盤上,磁盤的訪問速度是比內存慢幾十倍。
如今假如一個樹高30,每次搜索樹一次就須要訪問一次硬盤,一次訪問磁盤速度假設是10ms,樹高30至少須要訪問磁盤30次才能獲取到數據,30*10=300ms。
若是數據更多,樹高到100,獲取一次數據成本就很高了。
爲了解決這個問題,可使用N叉樹的方式來下降樹的高度,減小訪問磁盤的次數,這樣就能提升效率。
跳錶是創建在多層級鏈表上的數據結構,經過一層層的鏈表查詢就提升了檢索數據的效率。
Mysql中索引的實現是創建在數據庫引擎上的,而在Mysql中有多個數據庫引擎,經常使用的數據庫引擎是InnoDB.
InnoDB引擎索引實現是使用B+Tree索引模型,其實還有一種BTree模型,B+Tree是創建在BTree基礎上發展的。
B+Tree能夠認爲是BTree的改進版本:
注意子節點與葉子節點是不一樣的概念。把沒有子節點的節點叫作葉子節點
數據庫中每個索引都能對應到一顆B+樹,一個表是能夠存在多個B+樹。
不論是B+Tree仍是BTree都是利用多叉樹(該樹有多少叉是根據頁的大小進行計算好的,索引會涉及到新增刪除,一樣的就會涉及到頁的分裂與合併),保證不把全部的索引數據放入到內存上,下降磁盤的訪問次數加快數據訪問。
Mysql中經常使用Innodb引擎,組織數據庫索引的方式就是B+Tree。
B+Tree是索引組織表,那在B+Tree有多少種索引的類型呢?
從不一樣的方向劃分能夠劃分爲不一樣的類型。
主要爲普通索引,主鍵索引,惟一索引,前綴索引,全文索引,哈希索引。
普通索引就是咱們經常使用的索引建立-> 建立單個索引,相關語句以下
alter table table_name add index index_name(column);
drop index index_name on table_name;
ALTER TABLE table_name DROP INDEX index_name
複製代碼
主鍵索引是在普通索引的基礎上增長兩種約束條件分別爲惟一和不能爲空。主鍵索引在Innodb中用來維護索引組織的性質,因此,在使用Innodb引擎時,建議你的表都設置主鍵。
建立主鍵能夠在建立的表的時候指定 primary key('id'),也能夠建立聯合主鍵primary key ('id','name').
建立主鍵的相關SQL
# 當表裏面沒有主鍵索引時,增長主鍵索引
ALTER TABLE table_name ADD PRIMARY KEY ( `id` )
# 刪除主鍵索引
ALTER TABLE table_name DROP INDEX name_index
複製代碼
惟一索引時在普通索引的基礎上增長惟一的約束,在插入相關數據時,會檢查該索引數據是否已經存在數據庫中。
使用下面的建立語句建立:
# 建立惟一索引
ALTER TABLE table_name ADD UNIQUE (`column`)
# 刪除索引
drop index index_name on table_name;
複製代碼
字符串在編程中常常遇到的,好比經常使用的郵箱,一些業務場景中須要對某些字符串的前綴進行匹配。
這就涉及到一個問題,不能使用索引的話,就只能進行全表掃描。數據量一大,該方式就會成爲性能的瓶頸。
數據庫中的前綴索引就是解決字符串前綴匹配的問題。
# 建立前綴索引
alter table table_name add index index_name(columns(6));
# 刪除索引
drop index index_name on table_name;
# 怎麼計算前綴索引設計幾個字符 使用下列語句進行估算
select count(distinct 列名)/count(*)as a,COUNT(DISTINCT left(列名,100)) as b, COUNT(DISTINCT left(列名,110)) as c from 表名
複製代碼
前綴索引有一個缺點就是沒法使用覆蓋索引的優化,必須回表查詢。
全文索引是用來解決Mysql中文本匹配慢的問題,常使用like模糊搜索%內容%,無法用到前面列舉的索引,這時候就能夠嘗試使用全文索引來解決該問題。
相關SQL文件看下👇
create fulltext index table_name
on index_name(column,column);
alter table table_name
add fulltext index index_name(column,column);
複製代碼
注意一點的是全文索引是有本身的匹配語法,使用match和against關鍵字來進行匹配👇。
select * from table_name where match(column,column) against('xxx xxx');
複製代碼
從個數區分就是該索引郵幾個列組成。當有多個列構成就是聯合索引
單個索引的介紹不用多說,這裏主要說下聯合索引。
咱們知道B+Tree樹這種形式的索引結構是可使用最左前綴,來定位記錄。十分恰當的聯合索引就須要使該規則才能發揮出強大的做用。
順便引出的問題就是咱們在建立聯合索引的時候應該怎麼安排索引內的字段順序?
噹噹前表是新創建的,尚未其餘索引能夠根據業務需求進行直接建立;若是表中已經存在其餘索引,那能夠經過調整順序幫助減小索引的建立。
每次建立一個新的索引,就會增長一部分的索引存儲空間,隨着數據量的增長,索引的存儲也會暴漲,因此在建立索引時,都須要考一個空間佔用的原則。
當有一個大字段和小字段組合成聯合索引時,大字段索引放在聯合索引的前面。
好比如今須要根據郵件和年齡查詢數據,但還有根據age以及email單獨查詢的需求。
通常第一個反應就是建立三個索引,age,name,(name,age)/(age,name)。
而通常email的長度是大於age的,在有最左前綴的原則下,聯合索引第一個字段單獨查詢是可使用索引。則這裏選擇建立的索引就是age,name,(name,age)。
看一些數據庫資料老是聚簇索引,非聚簇索引,這兩種方式跟主鍵索引,普通索引又有什麼區別?
其實就是一類內容,只是根據分類的方式不一樣,叫的名字不一樣而已。聚簇索引與非聚簇索引是指在磁盤上對數據的組織結構不一樣。
聚簇索引可認爲是磁盤將實際數據按照定物理地址進行順序存放,而且與索引的順序是一致的。那麼當索引是相鄰的,對應的數據必定也是按照相鄰的順序存放。
磁盤對於順序讀取速度比磁盤隨機讀取的速度要快不少;正由於聚簇索引是按照物理順序進行存儲,那一個表只能有一個聚簇索引,該索引在Mysql中是主鍵索引,固然主鍵索引也是能夠包含多個列的。
其餘類型的索引都是被稱爲非聚簇索引。
使用非聚簇索引查詢數據,目的是先查到對應的聚簇索引也就是主鍵索引。經過主鍵索引從而查詢到對應的數據。在這個過程當中還涉及到兩個技術,分別爲回表以及索引下推。
回表很見到就是經過其餘索引查到對應的主鍵值,再使用主鍵值去表裏面再檢索一遍數據。
那有沒有不用回表?有的,在查詢的數據的時候,查詢的索引中已經包含要的字段,就不須要再使回表使用主鍵查詢數據,這句話可能有點蒙,看下SQL你就明白了。
# 設置userid爲普通索引
# 不使用覆蓋索引,須要回表查詢
select * from user where userid=1
# 使用了覆蓋索引,這是由於userid的索引列上葉子節點就是存儲的主鍵id,不須要再回表
select ID from user where userid=1;
# 使用聯合索引也是能夠作到的(userid,name)作一個聯合索引,索引列上有對應的值,則不須要再回表查詢
select name from user where userid =1
複製代碼
在Mysql5.6版本之前,查詢到的數據每條都須要從新回表查詢一次,而在5.6版本增長索引下推技術後,能夠直接在索引列中過濾掉不須要數據,減小回表的次數。
注意該技術須要使用範圍是聯合索引(age,sex)
select * from user where 10<age and age>20 and sex=1;
複製代碼
首先使用索引查詢到年齡知足第一條大於10小於20的人,同時索引列能夠直接進行判斷該用戶性別是否是知足1(男性),是就繼續,不是該條記錄過濾掉。
而在5.6之前知足大於10小於20後,根據讀取到主鍵數據再進行對比。回表的次數天然比使用索引下推技術版本多。
本章從一個小問題引起了對索引的探索,包含對現有的數據庫中經常使用的幾個索引技術進行介紹,明白爲何Mysql會選擇B+Tree做爲索引的檢索方式,並經過此方式梳理了Mysql中現有的索引技術。
但願這邊索引文章能幫助到你對索引的理解。