Are You OK?主鍵、聚集索引、輔助索引


每張表都一定存在主鍵嗎?

關於這個問題,各位小伙伴們不妨先自己想一想,再往下尋找答案。


首先公布結論:對於 InnoDB 存儲引擎來說,每張表都一定有個主鍵(Primary Key)!

讓人非常遺憾的是,網絡上至今仍然有非常多的文章是這樣的描述:“一張表中必須有聚集索引,但不一定需要主鍵”。前半句是正確的,后半句是大錯特錯!

對於 InnoDB 存儲引擎來說,表采用的存儲方式稱為索引組織表(index organizedtable),也即表都是根據主鍵的順序來進行組織存放的。如果主鍵都沒有,表怎么存?

那下面這段沒定義主鍵的建表語句是正確的嗎?

CREATE TABLE test(
	a INT NOT NULL,
	b INT NULL,
	c INT NOT NULL,
	d INT NOT NULL,
	UNIQUE KEY(b),
	UNIQUE KEY(d),
	UNIQUE KEY(c)
);

當然是沒有任何問題的。

因為 不顯示定義主鍵 != 沒有主鍵

如果在創建表時沒有顯式地定義主鍵,InnoDB 存儲引擎會按如下方式選擇或創建主鍵:

  • 首先判斷表中是否有非空的唯一索引(Unique NOT NULL),如果有,則該列即為主鍵
  • 如果不符合上述條件,InnoDB 存儲引擎自動創建一個 6 字節大小的指針 _rowid 作為主鍵

如果表中有多個非空唯一索引時怎么辦呢? InnoDB 存儲引擎將選擇建表時第一個定義的非空唯一索引為主鍵。需要注意的是!主鍵的選擇根據的是非空唯一索引定義的順序,而不是建表時列的順序。

比如上面那段代碼,有 a、b、c、d 四個列,b、c、d 三列上都有唯一索引。不過 b 列不是非空的,所以不可能成為主鍵了。而 d 列首先被定義為非空的唯一索引,所以 InnoDB 存儲引擎將其視為主鍵。

B+ 樹索引總覽

InnoDB 存儲引擎支持以下幾種常見的索引:

  • B+ 樹索引
  • 全文索引
  • 哈希索引

所謂哈希索引也就是得益於哈希算法的快速查找特性,不過哈希索引的致命缺點就是無法范圍查詢。並且 InnoDB 中哈希索引是自適應的,也就是說 InnoDB 存儲引擎會根據表的使用情況自動為表生成哈希索引,不能人為干預是否在一張表中生成哈希索引

全文索引本文先暫且不做贅述。

再來看 B+ 樹索引,B+ 樹索引的本質就是 B+ 樹在數據庫中的實現,它是目前關系型數據庫系統中查找最為常用的索引。

關於 B+ 樹的數據結構我就不詳細說了,B 代表平衡(Balance),而不是二叉(Binary),B+ 樹是從最早的平衡二叉樹演化而來的,但是 B+ 樹不是一個二叉樹。

簡單介紹下:B+ 樹是為磁盤或其他直接存取輔助設備設計的一種平衡查找樹。在 B+ 樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉子節點上,各葉子節點之間通過雙向鏈表進行連接。

也就是說,B+ 樹的葉子節點存儲真正的記錄,而非葉子節點的存在是為了更快速的找到對應記錄所在的葉子節點。如下圖是一個高度為 2 的 B+ 樹:

另外,需要注意的是,B+ 樹索引並不能找到一個給定鍵值的具體“行”!B+ 樹索引能找到的只是被查找數據行所在的“頁”。然后數據庫通過把頁讀入到內存,再在內存中進行查找,最后得到要查找的數據

肯定有些小伙伴會懵逼了,“頁” 又是什么東西?

這就得說到 InnoDB 存儲引擎的邏輯存儲結構。

InnoDB 存儲引擎中,所有數據都被邏輯地存放在一個空間中,稱之為 表空間(tablespace),也就是說我們常說的表,可以看作是 InnoDB 存儲引擎邏輯結構的最高層。表空間又由 段(segment)區(extent)頁(page) 組成(頁有時也稱為塊 block)。如下圖:

頁是 InnoDB 磁盤管理的最小單位,在 InnoDB 存儲引擎中,默認每個頁的大小為 16KB。而頁里面存放的東西就是一行一行的記錄。


我們接下來要說的 聚集索引(clustered inex)和輔助索引(secondary index)其實都是一種 B+ 樹索引。也就是說不管是聚集索引還是輔助索引,其內部都是 B+樹,即高度平衡的,葉子節點存放着所有的數據。(需要注意的是,索引是存儲引擎負責實現的,因此不是所有的存儲引擎都支持聚簇索引)

聚集索引與輔助索引不同之處就是,葉子節點存放的是否是一整行的信息。下文我們會詳細解釋。

主鍵和聚集索引的關系

先來看聚集索引,上面我們說過,InnoDB 存儲引擎表是索引組織表結構,即表中數據都是按照主鍵順序進行存放的。而聚集索引就是按照每張表的主鍵構造一棵 B+ 樹,同時葉子節點中存放的即為表中一行一行的數據,所以聚集索引的葉子節點也被稱為數據節點。

聚簇索引的數據分布

也就是說,聚集索引能夠在 B+ 樹索引的葉子節點上直接找到數據。並且由於定義了數據的邏輯順序,查詢優化器能夠快速發現到底是哪一段范圍的數據頁需要掃描。比如用戶需要查詢一張用戶表,查詢最后注冊的 10 位用戶,由於 B+ 樹索引的葉子節點是基於雙向鏈表的,所以用戶可以快速找到最后一個數據頁,並取出 10 條記錄。這也就是為什么大部分情況下查詢優化器傾向於采用聚集索引了。

可以這么說:在聚集索引中,索引即數據,數據即索引


另外,由於數據頁只能按照一棵 B+ 樹進行查找排序,或者說無法同時把數據行存放在兩個不同的地方,所以每張表只能擁有一個聚集索引

講了這么多,好像還沒講到主鍵和聚集索引有啥區別。一張表只能有一個主鍵,並且也只能有一個聚集索引,聚集索引還是按照主鍵來構建的,那這種種跡象不都表明主鍵就是聚集索引?

事實上,主鍵和索引就不是一個層次的東西!

主鍵是一種約束,這個約束用來強制表的實體完整性,一個表中只能有一個主鍵約束,並且主鍵約束中的列值必須是非空且唯一的。

而聚集索引它作為一種索引,其目的不是為了約束啥,而是為了對數據行進行排序以提高查詢的效率,換句話說它決定的是數據庫的物理存儲結構。

⭐ 形象點說,一個沒加聚集索引的表,它的數據是一行一行 無序 地存放在磁盤存儲器上的。而如果給表添加了聚集索引,那么表在磁盤上的存儲結構就由一行一行排列的結構轉變成了 樹狀結構,也就是 B+ 樹結構,換句話說,就是整個表就變成了一個索引,也就是上面提到的 “索引即數據,數據即索引”。

而至於 “主鍵就是索引” 這種觀點的由來,是因為:InnoDB 存儲引擎中,每張表都一定存在主鍵(顯示或隱式),而聚集索引依賴於主鍵的建立,所以如果沒有強制指定使用非聚集索引,InnoDB 在創建主鍵的同時會建立一個唯一的聚集索引(也有些文章稱之為 主鍵索引)。

所以,不要說 “主鍵就是聚集索引”,應該這樣說:“聚集索引一般都是加在主鍵上的”。

聚集索引和輔助索引的關系

輔助索引(Secondary Index)也稱為 非聚集索引、二級索引。其和聚集索引的最大區別就在於,輔助索引的葉子節點並不包含行記錄的全部數據。

簡單來說,一行記錄我們可以用 “主鍵 + 其他數據” 這樣的組合來標識,聚集索引中的葉子節點存儲的就是這一整個組合,而非聚集索引中的葉子節點只存儲了這個組合中的主鍵,那其他數據我怎么獲得呢?

非聚集索引的葉子節點說還包含了一個 書簽(bookmark),該書簽用來告訴 InnoDB 存儲引擎哪里可以找到與索引相對應的行數據。

那各位不妨想一想,行數據存儲在哪里呢?

沒錯,上文說過,聚集索引中的葉子節點中存放的就是表中一行一行的數據,所以 InnoDB 存儲引擎的輔助索引中的書簽其實就是相應行數據的聚集索引鍵

也就是說,輔助索引的葉子節點包含的是:每行數據的主鍵 + 該行數據對應的聚集索引鍵

當通過輔助索引來尋找數據時,InnoDB 存儲引擎會先遍歷輔助索引並通過葉子節點獲得某個主鍵對應的聚集索引鍵,然后再通過聚集索引來找到一個完整的行記錄。

舉個例子,如果在一棵高度為 3 的輔助索引樹中查找數據,那需要對這棵輔助索引樹遍歷 3 次找到指定聚集索引鍵,如果聚集索引樹的高度同樣為 3,那么還需要對聚集索引樹進行 3 次查找,最終找到一個完整的行數據所在的頁,因此一共需要 6 次邏輯 IO 訪問以得到最終的一個數據頁。

另外,很顯然的是,輔助索引的存在並不影響數據在聚集索引中的組織,因此每張表上可以有多個輔助索引

🎉 關注公眾號 | 飛天小牛肉,即時獲取更新

  • 博主東南大學碩士在讀,攜程 Java 后台開發暑期實習生,利用課余時間運營一個公眾號『 飛天小牛肉 』,2020/12/29 日開通,專注分享計算機基礎(數據結構 + 算法 + 計算機網絡 + 數據庫 + 操作系統 + Linux)、Java 技術棧等相關原創技術好文。本公眾號的目的就是讓大家可以快速掌握重點知識,有的放矢。關注公眾號第一時間獲取文章更新,成長的路上我們一起進步

  • 並推薦個人維護的開源教程類項目: CS-Wiki(Gitee 推薦項目,現已累計 1.8k+ star), 致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習 ~ 😊

  • 如果各位小伙伴春招秋招沒有拿得出手的項目的話,可以參考我寫的一個項目「開源社區系統 Echo」Gitee 官方推薦項目,目前已累計 900+ star,基於 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ... 並提供詳細的開發文檔和配套教程。公眾號后台回復 Echo 可以獲取配套教程,目前尚在更新中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM