當提到MySQL數據庫的時候,咱們的腦海裏會想起幾個關鍵字:索引、事務、數據庫鎖等等,索引是MySQL的靈魂,是平時進行查詢時的利器,也是面試中的重中之重。程序員
可能你瞭解索引的底層是b+樹,會加快查詢,也會在表中創建索引,但這是遠遠不夠的,這裏列舉幾個索引常見的面試題:面試
一、索引爲何要用b+樹這種數據結構?算法
二、彙集索引和非彙集索引的區別?數據庫
三、索引何時會失效,最左匹配原則是什麼?segmentfault
當遇到這些問題的時候,可能會發現本身對索引仍是隻知其一;不知其二,今天咱們一塊兒學習MySQL的索引。緩存
首先來看在MySQL數據庫中,一條查詢語句是如何執行的,索引出如今哪一個環節,起到了什麼做用。服務器
當執行SQL語句時,應用程序會鏈接到相應的數據庫服務器,而後服務器對SQL進行處理。數據結構
接着數據庫服務器會先去查詢是否有該SQL語句的緩存,key是查詢的語句,value是查詢的結果。若是你的查詢可以直接命中,就會直接從緩存中拿出value來返回客戶端。函數
注:查詢不會被解析、不會生成執行計劃、不會被執行。性能
若是沒有命中緩存,則開始第三步。
最後,數據庫服務器將查詢結果返回給客戶端。(若是查詢能夠緩存,MySQL也會將結果放到查詢緩存中)
這就是一條查詢語句的執行流程,能夠看到索引出如今優化SQL的流程步驟中,接下來了解索引究竟是什麼?
先簡單地瞭解一下索引的基本概念。
索引是幫助數據庫高效獲取數據的數據結構。
彙集索引表記錄的排列順序和索引的排列順序一致,因此查詢效率快,由於只要找到第一個索引值記錄,其他的連續性的記錄在物理表中也會連續存放,一塊兒就能夠查詢到。
缺點:新增比較慢,由於爲了保證表中記錄的物理順序和索引順序一致,在記錄插入的時候,會對數據頁從新排序。
索引的邏輯順序與磁盤上行的物理存儲順序不一樣,非彙集索引在葉子節點存儲的是主鍵和索引列,當咱們使用非彙集索引查詢數據時,須要拿到葉子上的主鍵再去表中查到想要查找的數據。這個過程就是咱們所說的回表。
好比漢語字典,想要查「阿」字,只須要翻到字典前幾頁,a開頭的位置,接着「啊」「愛」都會出來。也就是說,字典的正文部分自己就是一個目錄,不須要再去查其餘目錄來找到須要找的內容。咱們把這種正文內容自己就是一種按照必定規則排列的目錄稱爲==彙集索引==。
若是遇到不認識的字,只能根據「偏旁部首」進行查找,而後根據這個字後的頁碼直接翻到某頁來找到要找的字。但結合部首目錄和檢字表而查到的字的排序並非真正的正文的排序方法。
好比要查「玉」字,咱們能夠看到在查部首以後的檢字表中「玉」的頁碼是587頁,而後是珏,是251頁。很顯然,在字典中這兩個字並無挨着,如今看到的連續的「玉、珏、瑩」三字實際上就是他們在非彙集索引中的排序,是字典正文中的字在非彙集索引中的映射。咱們能夠經過這種方式來找到所須要的字,但它須要兩個過程,先找到目錄中的結果,而後再翻到結果所對應的頁碼。咱們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱爲==非彙集索引==。
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
ALTER TABLE `table_name` ADD UNIQUE (`column`)
ALTER TABLE `table_name` ADD INDEX index_name (`column` )
ALTER TABLE `table_name` ADD FULLTEXT (`column`)
ALTER TABLE `table_name` ADD INDEX index_name (`column1`,`column2`,`column3`)
瞭解了索引的基本概念後,可能最好奇的就是索引的底層是怎麼實現的呢?爲何索引能夠如此高效地進行數據的查找?如何設計數據結構能夠知足咱們的要求?
下文經過通常程序員的思惟來想一下若是是咱們來設計索引,要如何設計來達到索引的效果。
可能直接想到的就是用哈希表來實現快速查找,就像咱們平時用的hashmap同樣,value = get(key) O(1)時間複雜度一步到位,確實,哈希索引是一種方式。
哈希索引就是採用必定的哈希算法,只需一次哈希算法便可馬上定位到相應的位置,速度很是快。本質上就是把鍵值換算成新的哈希值,根據這個哈希值來定位。
在MySQL經常使用的InnoDB引擎中,仍是使用B+樹索引比較多。InnoDB是自適應哈希索引的(hash索引的建立由==InnoDB存儲引擎自動優化建立==,咱們干預不了)。
假設要查詢某個區間的數據,咱們只須要拿到區間的起始值,而後在樹中進行查找。
如數據爲:
當查找到起點節點10後,再順着鏈表進行遍歷,直到鏈表中的節點數據大於區間的終止值爲止。全部遍歷到的數據,就是符合區間值的全部數據。
利用二叉查找樹,區間查詢的功能已經實現了。可是,爲了節省內存,咱們只能把樹存儲在硬盤中。
那麼,每一個節點的讀取或者訪問,都對應一次硬盤IO操做。每次查詢數據時磁盤IO操做的次數,也叫作==IO漸進複雜度==,也就是==樹的高度==。
因此,咱們要減小磁盤IO操做的次數,也就是要==下降樹的高度==。
結構優化過程以下圖所示:
這裏將二叉樹變爲了M叉樹,下降了樹的高度,那麼這個M應該選擇多少才合適呢?
問題:對於相同個數的數據構建m叉樹索引,m叉樹中的m越大,那樹的高度就越小,那m叉樹中的m是否是越大越好呢?到底多大才合適呢?
無論是內存中的數據仍是磁盤中的數據,操做系統都是按頁(一頁的大小一般是4kb,這個值能夠經過getconfig(PAGE_SIZE)
命令查看)來讀取的,一次只會讀取一頁的數據。
若是要讀取的數據量超過了一頁的大小,就會觸發屢次IO操做。因此在選擇m大小的時候,要儘可能讓每一個節點的大小等於一個頁的大小。
通常實際應用中,出度d(樹的分叉數)是很是大的數字,一般超過100;==樹的高度(h)很是小,一般不超過3==。
順着解決問題的思路知道了咱們想要的數據結構是什麼。目前索引經常使用的數據結構是B+樹,先介紹一下什麼是B樹(也就是B-樹)。
以下圖所示:
瞭解了B樹,再來看一下B+樹,也是MySQL索引大部分狀況所使用的數據結構。
這些基本特色是爲了知足如下的特性。
由於MongoDB不是傳統的關係型數據庫,而是以Json格式做爲存儲的NoSQL非關係型數據庫,目的就是高性能、高可用、易擴展。擺脫了關係模型,因此範圍查詢和遍歷查詢的需求就沒那麼強烈了。
MyISAM的索引文件(.MYI)和數據文件(.MYD)文件是分離的,索引文件僅保存記錄所在頁的指針(物理位置),經過這些指針來讀取頁,進而讀取被索引的行。
樹中的葉子節點保存的是對應行的物理位置。經過該值,==存儲引擎能順利地進行回表查詢,獲得一行完整記錄==。
同時,每一個葉子也保存了指向下一個葉子的指針,從而方便葉子節點的範圍遍歷。
在MyISAM中,主鍵索引和輔助索引在結構上沒有任何區別,==只是主鍵索引要求key是惟一的,而輔助索引的key能夠重複==。
Innodb的主鍵索引和輔助索引以前提到過,再回顧一次。
InnoDB主鍵索引中既存儲了主健值,又存儲了行數據。
對於輔助索引,InnoDB採用的方式是在葉子節點中保存主鍵值,經過這個主鍵值來回表查詢到一條完整記錄,所以按輔助索引檢索其實進行了二次查詢,效率是沒有主鍵索引高的。
在上一節中瞭解了索引的多種數據結構,以及B樹和B+樹的對比等,你們應該對索引的底層實現有了初步的瞭解。這一節從應用層的角度出發,看一下如何建索引更能知足咱們的需求,以及MySQL索引何時會失效的問題。
先來思考一個小問題。
問題:當查詢條件爲2個及2個以上時,是建立多個單列索引仍是建立一個聯合索引好呢?它們之間的區別是什麼?哪一個效率高呢?
先來創建一些單列索引進行測試:
這裏創建了一張表,裏面創建了三個單列索引userId,mobile,billMonth。
而後進行多列查詢。
explain select * from `t_mobilesms_11` where userid = '1' and mobile = '13504679876' and billMonth = '1998-03'
咱們發現查詢時只用到了userid這一個單列索引,這是爲何呢?由於這取決於MySQL優化器的優化策略。
當多條件聯合查詢時,優化器會評估哪一個條件的索引效率高,它會選擇最佳的索引去使用。也就是說,此處三個索引列均可能被用到,只不過優化器判斷只須要使用userid這一個索引就能完成本次查詢,故最終explain展現的key爲userid。
多個單列索引在多條件查詢時優化器會選擇最優索引策略,可能只用一個索引,也可能將多個索引都用上。
可是多個單列索引底層會創建多個B+索引樹,比較佔用空間,也會浪費搜索效率
因此多條件聯合查詢時最好建聯合索引。
那聯合索引就能夠三個條件都用到了嗎?會出現索引失效的問題嗎?
該部分參考並引用文章:
建立user表,而後創建 name, age, pos, phone 四個字段的聯合索引
全值匹配(索引最佳)。
索引生效,這是最佳的查詢。
那麼時候會失效呢?
最左匹配原則:最左優先,以最左邊的爲起點任何連續的索引都能匹配上,如不連續,則匹配不上。
如:創建索引爲(a,b)的聯合索引,那麼只查 where b = 2 則不生效。換句話說:若是創建的索引是(a,b,c),也只有(a),(a,b),(a,b,c)三種查詢能夠生效。
這裏跳過了最左的name字段進行查詢,發現索引失效了。
遇到範圍查詢(>、<、between、like)就會中止匹配。
好比:a= 1 and b = 2 and c>3 and d =4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,由於c字段是一個範圍查詢,它以後的字段會中止匹配。
如計算、函數、(手動或自動)類型轉換等操做,會致使索引失效而進行全表掃描。
explain select * from user where left(name,3) = 'zhangsan' and age =20
這裏對name字段進行了left函數操做,致使索引失效。
explain select * from user where age != 20;
explain select * from user where age <> 20;
索引失效
explain select * from user where name like ‘%zhangsan’;
索引生效
explain select * from user where name like ‘zhangsan%’;
explain select * from user where name = 2000;
explain select * from user where name = ‘2000’ or age = 20 or pos =‘cxy’;
正常(索引參與了排序),沒有違反最左匹配原則。
explain select * from user where name = 'zhangsan' and age = 20 order by age,pos;
違反最左前綴法則,致使額外的文件排序(會下降性能)。
explain select name,age from user where name = 'zhangsan' order by pos;
正常(索引參與了排序)。
explain select name,age from user where name = 'zhangsan' group by age;
違反最左前綴法則,致使產生臨時表(會下降性能)。
explain select name,age from user where name = 'zhangsan' group by pos,age;
但願你們可以多去使用索引進行SQL優化,有問題歡迎指出。
來源:宜信技術學院做者:楊亨