聚簇索引和非聚簇索引:Mysql爲何建議使用自增id作主鍵

通俗點講
1、‘頁’和操做系統的關係
①爲何要有內存管理?
咱們知道,一個進程完成他的功能,須要訪問磁盤加載數據到內存而後等待進入cpu運算,由於數據量大小遠遠大於內存大小。所以提出虛擬內存概念。虛擬內存就是將程序用到的數據進行劃分,暫時用不到的放到磁盤裏,用到的放到內存裏,操做系統中老是運行着不止一個進程,各個進程有優先級順序,因此存在進程調度問題,進程的每次調度都會致使內存和磁盤數據置換,段式內存管理頁式內存管理都是基於虛擬內存概念的具體內存管理解決方案。html

②什麼是頁式內存管理?
虛擬內存位於程序和物理內存之間,程序只能看見虛擬內存,不再能直接訪問物理內存。每一個程序都有本身獨立的進程地址空間,這樣就作到了進程隔離。這裏的進程地址空間是指虛擬地址。顧名思義,既然是虛擬地址,也就是虛的,不是現實存在的地址空間。既然咱們在程序和物理地址空間之間增長了虛擬地址,那麼就要解決怎麼從虛擬地址映射到物理地址,由於程序最終確定是運行在物理內存中的,主要有分段和分頁兩種技術。node

分頁機制就是把內存地址空間分爲若干個很小的固定大小的頁,每一頁的大小由內存決定,就像Linux中ext文件系統將磁盤分紅若干個Block同樣,這樣作是分別是爲了提升內存和磁盤的利用率。mysql

③頁的大小爲何是4K?
CPU位數準確地說應該是CPU一次可以並行處理的數據寬度,通常就是指數據總線寬度。(後續補充:參考http://blog.sina.com.cn/s/blo...
④mysql索引和頁的關係
B-tree,B是balance,通常用於數據庫的索引。使用B-tree結構能夠顯著減小定位記錄時所經歷的中間過程,從而加快存取速度。而B+tree是B-tree的一個變種,MySQL就廣泛使用B+tree實現其索引結構。  
  通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程當中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,因此評價一個數據結構做爲索引的優劣最重要的指標就是在查找過程當中磁盤I/O操做次數的漸進複雜度。換句話說,索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數。
  爲了達到這個目的,磁盤按需讀取,要求每次都會預讀的長度通常爲頁的整數倍。並且數據庫系統將一個節點的大小設爲等於一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入。每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。並把B-tree中的m值設的很是大,就會讓樹的高度下降,有利於一次徹底載入。web

2、聚簇索引和非聚簇索引
①mysql索引
B+Tree結構均可以用在MyISAM和InnoDB上。mysql中,不一樣的存儲引擎對索引的實現方式不一樣,大體說下MyISAM和InnoDB兩種存儲引擎。算法

MyISAM的是非聚簇索引,B+Tree的葉子節點上的data,並非數據自己,而是數據存放的地址。主索引和輔助索引沒啥區別,只是主索引中的key必定得是惟一的。這裏的索引都是非聚簇索引。非聚簇索引的兩棵B+樹看上去沒什麼不一樣,節點的結構徹底一致只是存儲的內容不一樣而已,主鍵索引B+樹的節點存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵。表數據存儲在獨立的地方,
這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據,對於表數據來講,這兩個鍵沒有任何差異。因爲索引樹是獨立的,經過輔助鍵檢索無需訪問主鍵的索引樹。InnoDB的數據文件自己就是索引文件,B+Tree的葉子節點上的data就是數據自己,key爲主鍵,這是聚簇索引。聚簇索引,葉子節點上的data是主鍵(因此聚簇索引的key,不能過長)。sql

InnoDB使用的是聚簇索引(有且只能有必須有一個)+非聚簇索引(其餘索引,無關緊要),將主鍵(默認是聚簇索引)組織到一棵B+樹中,而行數據就儲存在葉子節點上,若使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索算法便可查找到對應的葉節點,以後得到行數據。若對Name列進行條件搜索,則須要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操做,最終到達葉子節點便可獲取整行數據。數據庫

B+Tree示意圖緩存

彙集索引和非彙集索引原理圖數據結構

②聚簇索引(數據存儲的順序和聚簇索引存儲的順序一致,如新華字典中經過拼音查找)
聚簇索引的數據的物理存放順序與索引順序是一致的,即:只要索引是相鄰的,那麼對應的數據必定也是相鄰地存放在磁盤上的。聚簇索引要比非聚簇索引查詢效率高不少。
彙集索引這種主+輔索引的好處是,當發生數據行移動或者頁分裂時,輔助索引樹不須要更新,由於輔助索引樹存儲的是主索引的主鍵關鍵字,而不是數據具體的物理地址。
③非聚簇索引(數據存儲的順序和聚簇索引存儲的順序一致,如新華字典中經過筆畫查找,)
非彙集索引,相似於圖書的附錄,那個專業術語出如今哪一個章節,這些專業術語是有順序的,可是出現的位置是沒有順序的。每一個表只能有一個聚簇索引,由於一個表中的記錄只能以一種物理順序存放。可是,一個表能夠有不止一個非聚簇索引。性能

3、Page結構
Page是整個InnoDB存儲的最基本構件,也是InnoDB磁盤管理的最小單位,與數據庫相關的全部內容都存儲在這種Page結構裏。Page分爲幾種類型,常見的頁類型有數據頁(B-tree Node)Undo頁(Undo Log Page)系統頁(System Page) 事務數據頁(Transaction System Page)等。單個Page的大小是16K(編譯宏UNIV_PAGE_SIZE控制),每一個Page使用一個32位的int值來惟一標識,這也正好對應InnoDB最大64TB的存儲容量(16Kib * 2^32 = 64Tib)。


  • 聚簇索引:將數據存儲與索引放到了一塊,找到索引也就找到了數據
  • 非聚簇索引:將數據存儲於索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam經過key_buffer把索引先緩存到內存中,當須要訪問數據時(經過索引訪問數據),在內存中直接搜索索引,而後經過索引找到磁盤相應數據,這也就是爲何索引不在key buffer命中時,速度慢的緣由

澄清一個概念:innodb中,在聚簇索引之上建立的索引稱之爲輔助索引,輔助索引訪問數據老是須要二次查找,非聚簇索引都是輔助索引,像複合索引、前綴索引、惟一索引,輔助索引葉子節點存儲的再也不是行的物理位置,而是主鍵值

什麼時候使用聚簇索引與非聚簇索引

cluster.png

聚簇索引具備惟一性

因爲聚簇索引是將數據跟索引結構放到一塊,所以一個表僅有一個聚簇索引

一個誤區:把主鍵自動設爲聚簇索引

聚簇索引默認是主鍵,若是表中沒有定義主鍵,InnoDB 會選擇一個惟一的非空索引代替。若是沒有這樣的索引,InnoDB 會隱式定義一個主鍵來做爲聚簇索引。InnoDB 只彙集在同一個頁面中的記錄。包含相鄰健值的頁面可能相距甚遠。若是你已經設置了主鍵爲聚簇索引,必須先刪除主鍵,而後添加咱們想要的聚簇索引,最後恢復設置主鍵便可

此時其餘索引只能被定義爲非聚簇索引。這個是最大的誤區。有的主鍵仍是無心義的自動增量字段,那樣的話Clustered index對效率的幫助,徹底被浪費了。

剛纔說到了,聚簇索引性能最好並且具備惟一性,因此很是珍貴,必須慎重設置。通常要根據這個表最經常使用的SQL查詢方式來進行選擇,某個字段做爲聚簇索引,或組合聚簇索引,這個要看實際狀況。

記住咱們的最終目的就是在相同結果集狀況下,儘量減小邏輯IO

結合圖再仔細點看

image

image

  1. InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行數據就儲存在葉子節點上,若使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索算法便可查找到對應的葉節點,以後得到行數據
  2. 對Name列進行條件搜索,則須要兩個步驟第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操做,最終到達葉子節點便可獲取整行數據。(重點在於經過其餘鍵須要創建輔助索引

MyISM使用的是非聚簇索引,非聚簇索引的兩棵B+樹看上去沒什麼不一樣,節點的結構徹底一致只是存儲的內容不一樣而已,主鍵索引B+樹的節點存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵。表數據存儲在獨立的地方,這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據,對於表數據來講,這兩個鍵沒有任何差異。因爲索引樹是獨立的,經過輔助鍵檢索無需訪問主鍵的索引樹

聚簇索引的優點

看上去聚簇索引的效率明顯要低於非聚簇索引,由於每次使用輔助索引檢索都要通過兩次B+樹查找,這不是畫蛇添足嗎?聚簇索引的優點在哪?

  1. 因爲行數據和葉子節點存儲在一塊兒,同一頁中會有多條行數據,訪問同一數據頁不一樣行記錄時,已經把頁加載到了Buffer中,再次訪問的時候,會在內存中完成訪問,沒必要訪問磁盤。這樣主鍵和行數據是一塊兒被載入內存的,找到葉子節點就能夠馬上將行數據返回了,若是按照主鍵Id來組織數據,得到數據更快
  2. 輔助索引使用主鍵做爲"指針"而不是使用地址值做爲指針的好處是,減小了當出現行移動或者數據頁分裂時輔助索引的維護工做使用主鍵值看成指針會讓輔助索引佔用更多的空間,換來的好處是InnoDB在移動行時無須更新輔助索引中的這個"指針"也就是說行的位置(實現中經過16K的Page來定位)會隨着數據庫裏數據的修改而發生變化(前面的B+樹節點分裂以及Page的分裂),使用聚簇索引就能夠保證無論這個主鍵B+樹的節點如何變化,輔助索引樹都不受影響
  3. 聚簇索引適合用在排序的場合,非聚簇索引不適合
  4. 取出必定範圍數據的時候,使用用聚簇索引
  5. 二級索引須要兩次索引查找,而不是一次才能取到數據,由於存儲引擎第一次須要經過二級索引找到索引的葉子節點,從而找到數據的主鍵,而後在聚簇索引中用主鍵再次查找索引,再找到數據
  6. 能夠把相關數據保存在一塊兒。例如實現電子郵箱時,能夠根據用戶 ID 來彙集數據,這樣只須要從磁盤讀取少數的數據頁就能獲取某個用戶的所有郵件。若是沒有使用聚簇索引,則每封郵件均可能致使一次磁盤 I/O。

聚簇索引的劣勢

  1. 維護索引很昂貴,特別是插入新行或者主鍵被更新導至要分頁(page split)的時候。建議在大量插入新行後,選在負載較低的時間段,經過OPTIMIZE TABLE優化表,由於必須被移動的行數據可能形成碎片。使用獨享表空間能夠弱化碎片
  2. 表由於使用UUId(隨機ID)做爲主鍵,使數據存儲稀疏,這就會出現聚簇索引有可能有比全表掃面更慢,

image

因此建議使用int的auto_increment做爲主鍵

image

主鍵的值是順序的,因此 InnoDB 把每一條記錄都存儲在上一條記錄的後面。當達到頁的最大填充因子時(InnoDB 默認的最大填充因子是頁大小的 15/16,留出部分空間用於之後修改),下一條記錄就會寫入新的頁中。一旦數據按照這種順序的方式加載,主鍵頁就會近似於被順序的記錄填滿(二級索引頁多是不同的)

  1. 若是主鍵比較大的話,那輔助索引將會變的更大,由於輔助索引的葉子存儲的是主鍵值;過長的主鍵值,會致使非葉子節點佔用佔用更多的物理空間

爲何主鍵一般建議使用自增id

聚簇索引的數據的物理存放順序與索引順序是一致的,即:只要索引是相鄰的,那麼對應的數據必定也是相鄰地存放在磁盤上的。聚簇索引的順序和磁盤中數據的存儲順序是一致的,若是主鍵不是自增id,那麼能夠想 象,它會幹些什麼,不斷地調整數據的物理地址、分頁,固然也有其餘一些措施來減小這些操做,但卻沒法完全避免。但,若是是自增的,那就簡單了,它只須要一 頁一頁地寫,索引結構相對緊湊,磁盤碎片少,效率也高。

由於MyISAM的主索引並不是聚簇索引,那麼他的數據的物理地址必然是凌亂的,拿到這些物理地址,按照合適的算法進行I/O讀取,因而開始不停的尋道不停的旋轉聚簇索引則只需一次I/O。(強烈的對比)

不過,若是涉及到大數據量的排序、全表掃描、count之類的操做的話,仍是MyISAM佔優點些,由於索引所佔空間小,這些操做是須要在內存中完成的

mysql中聚簇索引的設定

聚簇索引默認是主鍵,若是表中沒有定義主鍵,InnoDB 會選擇一個惟一的非空索引代替。若是沒有這樣的索引,InnoDB 會隱式定義一個主鍵來做爲聚簇索引。InnoDB 只彙集在同一個頁面中的記錄。包含相鄰健值的頁面可能相距甚遠。

相關文章
相關標籤/搜索