1. 程式人生 > >mysql聚簇索引和非聚簇索引以及二級索引

mysql聚簇索引和非聚簇索引以及二級索引

聚簇索引:  ORANCLE 中的索引組織表;   

如何區分聚簇索引和非聚簇索引?這裡有一個比喻,來自網路: 

舉例來說明一下聚集索引和非聚集索引的區別: 

其實,我們的漢語字典的正文字身就是一個聚集索引。比如,我們要查“安”字,就會很自然地翻開字典的前幾頁,因為“安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字母“a”開頭並以“z”結尾的,那麼“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查“張”字,那您也會將您的字典翻到最後部分,因為“張”的拼音是“zhang”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。 

我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為“聚集索引”。 

如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之後的檢字表中“張”的頁碼是672頁,檢字表中“張”的上面是“馳”字,但頁碼卻是63頁,“張”的下面是“弩”字,頁面是390頁。很顯然,這些字並不是真正的分別位於“張”字的上下方,現在您看到的連續的“馳、張、弩”三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的對映。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。 

我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”。 


  聚簇:資料行和資料緊密的儲存在一起     葉子頁 vs 節點頁     葉子頁存資料,節點頁存索引。

innodb的聚簇索引實際上是在同一個結構中儲存了btree索引和資料行。

INNODB通過主鍵列來索引資料。沒有定義,INNODB會選擇一個唯一的非空索引列代替。如果沒有這樣的索引,會選擇第一個非空的唯一索引代替,如果沒有非空唯一索引,Innodb會隱式定義一個6位元組的rowid主鍵來作為聚集索引。innodb只聚集在同一個頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠。(物理位置)

聚簇索引:

  ORANCLE 中的索引組織表;

他的資料實際上存放的索引的葉子頁中。    聚簇:資料行和資料緊密的儲存在一起。                         葉子頁 vs 節點頁     葉子頁存資料,節點頁存索引。

innodb的聚簇索引實際上是在同一個結構中儲存了btree索引和資料行。

INNODB通過主鍵列來索引資料。沒有定義,INNODB會選擇一個唯一的非空索引列代替。如果沒有這樣的索引,InnoDB會隱式定義一個主鍵。

聚集索引的缺點:

  A:聚簇資料最大限度地提高了IO密集型應用的效能,但如果資料全部放在記憶體中,則訪問的順序就沒有那麼重要了,聚集索引也沒有什麼優勢了

  B:插入速度嚴重依賴於插入順序,按照主鍵的順序插入是載入資料到innodb表中速度最快的方式,但如果不是按照主鍵順序載入資料,那麼在載入完成後最好使用optimize table命令重新組織一下表 。                                     optimize table account;

  C:更新聚集索引列的代價很高,因為會強制innodb將每個被更新的行移動到新的位置

  D:基於聚集索引的表在插入新行,或者主鍵被更新導致需要移動行的時候,可能面臨頁分裂的問題,當行的主鍵值要求必須將這一行插入到某個已滿的頁中時,儲存引擎會將該頁分裂成兩個頁面來容納該行,這就是一次頁分裂操作,頁分裂會導致表佔用更多的磁碟空間

  E:聚集索引可能導致全表掃描變慢,尤其是行比較稀疏,或者由於頁分裂導致資料儲存不連續的時候

  F:二級索引可能比想象的更大,因為在二級索引的葉子節點包含了引用行的主鍵列。

  G:二級索引訪問需要兩次索引查詢,而不是一次

因為二級索引葉子節點中儲存的不是指向行的物理位置的指標,而是行的主鍵值。這意味著通過二級索引查詢行,儲存引擎需要找到二級索引的葉子節點獲得對應的主鍵值,然後根據這個主鍵值去聚集索引中查詢對應的行,這裡做了重複的工作,兩次btree查詢而不是一次,對於innodb,自適應雜湊索引能減少這樣的重複工作。

innodb和myisam物理儲存的資料分佈對比:

  myisam:

  是按照資料插入的順序儲存在磁碟上的,myisam中的主鍵索引和二級索引在結構上並沒有什麼不同,主鍵索引就是一個名為primary的唯一非空索引。MyISAM的非聚餐索引:資料和索引是分開存放的 

這裡寫圖片描述

  innodb:

  因為innodb支援聚集索引,所以使用非常不同的方式儲存同樣的資料,innodb聚集索引包含了整個表的資料,而不是隻有索引,因為在Innodb中,聚集索引就是表,所以不像myisam那樣需要獨立的行儲存。聚集索引的每一個葉子節點都包含了主鍵值,事務ID,用於事務和MVCC的回滾指標以及所有剩餘列的值,如果主鍵是一個列字首索引,innodb也會包含完整的主鍵列和剩下的列的值。

  還有一點和myisam不同的是,innodb的二級索引和聚集索引很不同,innodb二級索引的葉子節點中儲存的不是行指標,而是主鍵值,並以此作為指向行的指標,這樣的策略減少了當出現行移動或者資料頁的分裂時二級索引的維護工作,使用主鍵值當做指標會讓二級索引佔用更多的空間,換來的好處是,innodb在移動行時無須更新二級索引中的這個指標。

InnoDB的聚簇索引: 資料和主鍵索引是存放在一起的,其他索引葉子結點存放的主鍵id。 

 這裡寫圖片描述

InnoDB的的二級索引的葉子節點存放的是KEY欄位加主鍵值。因此,通過二級索引查詢首先查到是主鍵值,然後InnoDB再根據查到的主鍵值通過主鍵索引找到相應的資料塊。而MyISAM的二級索引葉子節點存放的還是列值與行號的組合,葉子節點中儲存的是資料的實體地址。所以可以看出MYISAM的主鍵索引和二級索引沒有任何區別,主鍵索引僅僅只是一個叫做PRIMARY的唯一、非空的索引,且MYISAM引擎中可以不設主鍵。

InnoDb 使用自增主鍵和非自增主鍵亂序值做主鍵區別

eg :用UUID做主鍵,因為新航的主鍵值不一定比之前插入的大,所以Innodb無法簡單的總是把新行插入到索引最後,而是需要為新行尋找合適的位置,並且分配空間,這樣會增加很多額外的工作,並導致資料分佈不夠優化。這將導致 大量的隨機I/O。因為寫入是亂序的,IoonDB不得不頻繁的做頁分裂操作,頁分裂會導致移動大量的資料,一次插入最好啊需要修改三個頁。 並且頁會變的稀疏並且被不規則的填充,所以最終資料會有碎片。

順序主鍵也可能會造成       ①主鍵上屆熱點 ②AUTON_INCREMENT鎖機制

可以修改 innodb_autoinc_lock_mode 引數

網上關於二級索引的解釋:

二級索引也是有兩個欄位的有序檔案:

第一個欄位是索引欄位,有相同的資料型別,並且是資料檔案中的非排序欄位,

第二個欄位可以是一個塊指標也可以是記錄指標。二級索引(也稱為非聚簇索引)用於在二級鍵上搜索檔案,二級索引的搜尋鍵指定了一個順序,這個順序與檔案的排序順序不同。例如,對於圖3.16所示的EMPLOYEE薪水冊檔案,可能用僱員識別符號(EMP.ID)作為構建主索引的主鍵,而僱員的姓和名(EMP.LNAME和EMP.FNAME)可能用於構建二級索引。因此,使用者產生的搜尋操作可以通過僱員識別符號(EMP.ID)或者僱員的名字(EMP.FNAME和EMP.LNAME)來訪問記錄。


網上關於二級索引的解釋:

二級索引也是有兩個欄位的有序檔案:

第一個欄位是索引欄位,有相同的資料型別,並且是資料檔案中的非排序欄位,

第二個欄位可以是一個塊指標也可以是記錄指標。二級索引(也稱為非聚簇索引)用於在二級鍵上搜索檔案,二級索引的搜尋鍵指定了一個順序,這個順序與檔案的排序順序不同。例如,對於圖3.16所示的EMPLOYEE薪水冊檔案,可能用僱員識別符號(EMP.ID)作為構建主索引的主鍵,而僱員的姓和名(EMP.LNAME和EMP.FNAME)可能用於構建二級索引。因此,使用者產生的搜尋操作可以通過僱員識別符號(EMP.ID)或者僱員的名字(EMP.FNAME和EMP.LNAME)來訪問記錄。

選擇索引

  這裡我們要引入兩個比較難理解但很重要的概念:聚簇索引和非聚簇索引。這是索引的兩種型別。在聚簇索引中,索引樹的葉級頁包含實際的資料:記錄的索引順序與物理順序相同。在非聚簇索引中,葉級頁指向表中的記錄:記錄的物理順序與邏輯順序沒有必然的聯絡。

  聚簇索引非常象目錄表,目錄表的順序與實際的頁碼順序是一致的。非聚簇索引則更象書的標準索引表,索引表中的順序通常與實際的頁碼順序是不一致的。一本書也許有多個索引。例如,它也許同時有主題索引和作者索引。同樣,一個表可以有多個非聚簇索引。通常情況下,你使用的是聚簇索引,但是你應該對兩種型別索引的優缺點都有所理解。

  從建立了聚簇索引的表中取出資料要比建立了非聚簇索引的錶快。當你需要取出一定範圍內的資料時,用聚簇索引也比用非聚簇索引好。例如,假設你用一個表來記錄訪問者在你網點上的活動。如果你想取出在一定時間段內的登入資訊,你應該對這個表的DATETIME(日期時間)型欄位建立聚簇索引。假如你不僅想根據日期,而且想根據使用者名稱從你的網點活動日誌中取資料。在這種情況下,同時建立一個聚簇索引和非聚簇索引是有效的。你可以對日期時間欄位建立聚簇索引,對使用者名稱欄位建立非聚簇索引。如果你發現你需要更多的索引方式,你可以增加更多的非聚簇索引。

  但非聚簇索引需要大量的硬碟空間和記憶體。另外,雖然非聚簇索引可以提高從表中取資料的速度,它也會降低向表中插入和更新資料的速度。每當你改變了一個建立了非聚簇索引的表中的資料時,必須同時更新索引。因此你對一個表建立非聚簇索引時要慎重考慮。如果你預計一個表需要頻繁地更新資料,那麼不要對它建立太多非聚簇索引。另外,如果硬碟和記憶體空間有限,也應該限制使用非聚簇索引的數量。