淺談SQL優化入門:三、利用索引


0、寫在前面的話

關於索引的內容原本是想寫的,大概收集了下資料,發現並無想象中的簡單,又不想總結了,糾結了一下,決定就大概寫點淺顯的,好吧,就是懶,先挖個淺坑,之後再挖深一點。最基本的使用很簡單,直接就寫在這裏吧。

索引是衆所周知的能夠提升查詢的速度,且針對的是具體的字段,使用方式爲:
(MySQL中,通常創建主鍵,數據庫會自動創建其彙集索引;而其餘字段的索引,若不具體指明則創建非彙集索引)
(也能夠經過關鍵字 CLUSTERED 或 NONCLUSTERED 指定彙集索引或非彙集索引,由於本篇不涉及具體應用案例,故不展開)
CREATE [CLUSTERED | NONCLUSTERED] INDEX <索引名> ON <表名(關係名)>; 

e.g.
CREATE INDEX yearIndex ON movie(year);

而撤銷索引:
DROP INDEX <索引名> ON <表名>;

e.g.
DROP INDEX yearIndex ON movie;



一、索引

1.1 索引的概念

咱們在數據庫查詢過程當中,即便知足給定條件的記錄不多,也須要把整個表關係掃描一遍,當關系很是大時,其開銷是很大的。

例如,在電影表中查詢迪斯尼公司2000年製做的電影:
SELECT *
FROM movie
WHERE studioName='Disney' AND year=2000;

假設電影表中有10000條記錄,其中2000年製做的電影有200個,這之中製做公司爲迪斯尼的只有10個。若是不採起措施,要實現該查詢,咱們就要把10000個記錄都挨個進行檢查是否知足條件。若是有某種方法能讓咱們只取出年份爲2000年的200條記錄,而後再去找製做公司爲迪斯尼的,顯然效率要高不少。

確實有這樣的方法,就叫作 索引,它是一種 爲表中的給定字段提供存取路徑的數據結構

因此什麼是索引?上面這個解釋徹底不明白,咱們仍是形象一點說明,網友 elysee 在它的博客《 數據庫優化實踐【索引篇】》中講了一個故事,很是形象,但我總理解起來有些不妥,就按照本身的意思修改了一下,故事以下:

「圖書館」的圖片搜索結果

好久之前,在一個古城的的大圖書館中珍藏有成千上萬本書籍,但書架上的書沒有按任何順序擺放,所以每當有人詢問某本書時,圖書管理員只有挨個尋找,每一次都要花費大量的時間。 更糟的是圖書館的圖書愈來愈多,圖書管理員的工做變得異常痛苦。

有一天,圖書館來了一個聰明的小夥子,他看到圖書管理員的痛苦工做後,想出了一個辦法,他說:「你把全部書架,按照英文字母分紅26個部分,每本書根據書名的字母順序,放到相應的書架上去。好比《阿凡提》就放在A書架中,若是有《阿凡達》,也放在A書架,且根據書名字母順序《阿凡達》(afd)放在《阿凡提》(aft)前面」,這樣一來,若是有人指定了書籍的名字, 那麼圖書管理員很快就能夠找到它的位置了。

(圖書按照書名的字母順序放在對應的字母書架上,就像建立彙集索引,即表中的全部行會在文件系統上根據書名進行物理排序,當查詢表中任一行時,數據庫首先使用匯集索引找到對應的數據頁,就像首先找到對應的字母書架同樣;而後按順序就能夠找到目標行,就像找到書架上的書同樣)

因而圖書管理員開始分類整理放置,爲此他花了整整一週時間,最後,他發現找書的效率確實大大提升了。

(在一個表上只能建立一個彙集索引,就像書只能按一種規則擺放同樣)

但問題並未徹底解決,有不少人想看某個做者的全部書,圖書管理員無奈又只有掃描全部圖書的做者,進行挨個尋找,時間又變得太長了,所以他向那個聰明的小夥子求助。

(這就好像你給book表增長了索引bookName,但除此以外沒有創建其它索引,當使用bookAuthor進行檢索時,數據庫引擎又只要進行全表掃描,逐個尋找)

聰明的小夥告訴圖書管理員,那就建立一個目錄文檔好了,文檔中將圖書按照做者分類,將書籍從新排列,並把做者的圖書和圖書對應的書架位置一塊兒記錄下來就行了,不用去動圖書真正的位置。這樣,一旦有人指定做者,那麼根據做者分類的目錄文檔,就能夠找到該做者的書了。

因而圖書管理員又花費時間趕忙整理了一個目錄文檔,果真有效,而後他又 開始了新的思考,讀者可能還會根據圖書的其它屬性來找書,如書的類型,是小說?詩歌?仍是其餘什麼,因而他用這個辦法爲書的種類建立了目錄,如今能夠根據書名、做者和種類迅速找到圖書了,圖書管理員的工做變得輕鬆了。

故事到這裏就結束了。其中上面提到了個概念,叫 彙集索引,索引順序和物理順序相同,只能有一個,也就是這裏的書名;相對的,還有 非彙集索引,能夠有多個,索引順序和物理順序沒有關係,也就是後來的做者目錄,種類目錄。

1.2 索引的存儲

要理解這二者的區別和含義,須要先來聊聊索引的存儲。首先,一條索引記錄包含:
  • 鍵值(即你定義索引時指定的全部字段的值)
  • 邏輯指針(指向數據頁或者另外一索引頁)

另外,索引不管是彙集索引仍是非彙集索引,都是數據庫另外開闢的空間,並非依附在原有數據上的。因此當建立索引時,數據庫系統會分配一個索引頁,每當你往表中插入一行數據,數據庫系統也將插入一行索引記錄。此時的索引頁是 根節點,若是滿了,則會進行分裂,將原來指向數據頁的邏輯指針,更換爲指向子索引頁的邏輯指針(以下圖)。



1.3 索引的類型

1.3.1 彙集索引

所謂 彙集索引,就是肯定表中數據的物理順序,如上面圖書的按書名排列,又或者手機電話簿的聯繫人按姓氏排列等。它 規定了數據在表中的物理存儲順序 ,所以一個表只能包含一個彙集索引。也就是,索引存儲順序和數據行的存儲順序是一致的。



如上圖,咱們在名字字段上創建彙集索引,當須要在根據此字段查找特定的記錄時,數據庫系統會根據特定的系統表查找的此索引的根,而後根據指針查找下一個,直到找到。例如咱們要查詢「green」,因爲它介於[bennet,karsen],據此咱們找到了索引頁1007,在該頁中「green」介於[greane, hunter]間,據此咱們找到葉結點1133(也即數據結點),並最終在此頁中找以了目標數據行。

1.3.2 非彙集索引

咱們說 彙集索引存儲記錄是物理上連續存在,而非彙集索引是邏輯上的連續,因此在物理上它就沒有所謂的順序可言,也和物理存儲順序無關。



如上圖,能夠看到,非彙集索引的索引記錄結構發生了必定的變化,它包括:
  • 索引字段值(鍵值)
  • 數據頁的頁指針,以及指針偏移量(相對彙集索引,新增的部分)
  • 下一個索引頁的指針

由於彙集索引是有順序的,因此咱們最終只須要指向索引頁,按順序就能找到,也正是如此, 彙集索引最終索引頁存儲的是頁指針,而不是行指針

而非彙集索引,由於無序,那麼意味着非彙集索引要爲每個數據行存儲一條索引記錄,才能進行準確查詢。這裏就有了「數據頁的頁指針,和指針偏移量」,相似於數據行的座標,存儲在索引記錄中。也就是說,非彙集索引存儲的是鍵值和其對應的數據座標,又爲了索引分頁,因此也包含第三個部分,用來存儲下一個索引頁指針。

(就像以前那個故事,若是書籍已經按名字排序,你要找《朝花夕拾》,你知道在Z書架便可;若是書籍仍是亂糟糟沒有排序,而你手裏有個做者分類的目錄文檔,那你要找魯迅的《朝花夕拾》,意味着文檔上就必須記錄書籍具體的位置了)



二、漢語字典的例子

若是你還不明白關於「彙集索引和非彙集索引的區別」,這裏還有一個形象的例子:


1)彙集索引
漢語字典,其正文自己就是一個彙集索引。

好比,咱們要查「安」字,就會很天然地翻開字典的前幾頁,由於「安」的拼音是「an」,而按照拼音排序漢字的字典是以英文字母「a」開頭並以「z」結尾的,那麼「安」字就天然地排在字典的前部。若是您翻完了全部以「a」開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字。

一樣的,若是查「張」字,那您也會將您的字典翻到最後部分,由於「張」的拼音是「zhang」。也就是說,字典的正文部分自己就是一個目錄,你不須要再去查其餘目錄來找到您須要找的內容。正文內容自己就是一種按照必定規則排列的目錄稱爲「彙集索引」。

2)非彙集索引
若是您認識某個字,您能夠快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛纔的方法找到您要查的字,而須要去根據「偏旁部首」查到您要找的字,而後根據這個字後的頁碼直接翻到某頁來找到您要找的字。

但您結合「部首目錄」和「檢字表」而查到的字的排序並非真正的正文的排序方法,好比您查「張」字,咱們能夠看到在查部首以後的檢字表中「張」的頁碼是672頁,檢字表中「張」的上面是「馳」字,但頁碼倒是63頁,「張」的下面是「弩」字,頁面是390頁。很顯然,這些字並非真正的分別位於「張」字的上下方,如今您看到的連續的「馳、張、弩」三字實際上就是他們在非彙集索引中的排序,是字典正文中的字在非彙集索引中的映射。咱們能夠經過這種方式來找到您所須要的字,但它須要兩個過程,先找到目錄中的結果,而後再翻到您所須要的頁碼。



三、簡單總結

  • 索引能夠提升查詢效率
  • 彙集索引一個表只能有一個,而非彙集索引一個表能夠存在多個
  • 彙集索引存儲記錄是物理上連續存在,而非彙集索引是邏輯上的連續,物理存儲並不連續

另外,衆所周知的是,索引確實能夠提升查詢速度,但會影響插入、刪除和更新的效率,正是由於數據變化的同時,索引也必須進行更新維護,消耗性能。因此不能盲目地建立索引,而是合理地建立和使用。

另外的另外,索引這部分的坑仍是很深的,好比說若是是按照學分查找60-90之間的學生,那麼在學分上建立有順序的彙集索引是不是最優選擇?又或者,如何正確使用匯集索引和非彙集索引?等等相似的問題,都涉及到索引的細節和深度理解。

如今本身只能大概理解一些表面意思,實際應用上,由於精力和工做應用暫時不涉及,也就再也不繼續展開了。

淺談SQL優化入門,目前也就這3篇博文了,主要也是由於工做上一個SQL問題引起的,便以此展開對涉及知識點進行梳理,筆記於此。

總體來講此次體驗有幾個點,關於SQL優化的總結:
  • 儘可能使用顯性鏈接
  • 多利用EXPLAIN查看SQL執行順序
  • 使用小的結果集驅動大的結果集
  • 合理使用索引


啊,編程路真是漫漫長!



四、參考連接

相關文章
相關標籤/搜索