索引
使用索引,會提前存儲被索引字段對應行的磁盤文件地址指針,做一次IO就能查找到數據。
二叉樹
遞增的索引不適合用二叉樹來維護,因爲遞增的id會導致二叉樹退化成爲鏈表,這樣建立索引的效果和不建立索引的差別不大。因此MySQL不使用二叉樹做索引。
紅黑樹
單邊增長不平衡時,紅黑樹會自動平衡。
JDK1.8之後,HashMap使用的就是紅黑樹。
在某些場景下,紅黑樹也會存在問題:樹的高度會一直增長,數據量大的時候,還是會有很多次IO。
B樹
B樹是對紅黑樹的改造,讓一個節點能夠存儲更多的元素。
爲什麼不把所有元素都加載進一個節點,放進內存中?
- 佔用內存過大;一次磁盤IO有大小限制,加載不過來,會很慢
- 沒有必要把一個節點搞得太大。
使用show global status like'Innodb_page_size'
查看MySQL默認設置一個葉節點(頁)的值:16Kb
B+樹
MySQL底層使用的是B+樹,是對B樹的改造。
把非葉子節點的data數據都挪到了葉子結點上去存儲,這樣,在16kb大小固定的情況下,一個葉子節點在橫向上能存放更多的索引。
例如,查找30
這個元素對應的data
的過程:
第一次,將15 56 77
load 進內存:
第二次,將15 20 49
load 進內存
第三次,將20 30
load 進內存,進而取出30
對應的data
因此,如果添加了合適的索引,只需要進行2-3
次磁盤IO,就可以找到數據。
如果不添加索引的話,需要一行一行去找,十分緩慢。
關於存儲引擎
存儲引擎是表級別的,不同的表可以有不同的存儲引擎
主鍵自帶主鍵索引。
MyISAM引擎的查詢過程:
- 先去索引文件中查找指針
- 再去指針的位置磁盤中把data查詢出來
數據和索引是分開存儲的,它的葉子結點只存儲了數據在磁盤對應文件的地址。
MySQL數據文件存儲在哪裏?都有哪些文件?如圖:
InnoDB引擎的查詢過程:
InnoDB的主鍵索引是聚集索引。(什麼是稀疏索引?稀疏索引就是非聚集索引)
.ibd
文件同時存儲索引和數據,以B+樹的結構組織起來,在葉子節點包含了一條記錄(行)的所有數據。
常見問題
爲什麼InnoDB表必須有主鍵,並且推薦使用整型
的自增
主鍵?
MySQL InnoDB引擎設計數據默認使用B+樹來組織。
如果沒設置主鍵,MySQL會自動找一個能夠作爲主鍵的字段,作爲索引去維護。
如果MySQL找不到合適的主鍵,MySQL會自己維護一個隱藏列,用這個隱藏列做索引。
爲什麼推薦使用自增的主鍵?
自增的主鍵有利於維護B+樹(每次都向尾部追加),而如果不自增的話,可能會插入到B+數中間位置的某個節點中,而每個節點的大小是有限制的。超出限制後,爲了保持樹的平衡,會造成葉節點分裂,導致插入性能下降。
用UUID作爲主鍵怎麼樣?
在B+數查找的過程需要進行多次比較大小。UUID是作爲字符串存儲的,而用整型數字較大小效率更高,並且整型的索引在存儲的時候佔用的磁盤空間更小。
用Hash作爲索引怎麼樣?
用Hash的話,select * from t where col1 > 6
這樣的範圍查詢無法加快查詢速度
而如果使用B+樹,葉子結點的(雙向)指針提高了範圍查詢的速度。
分庫分表怎麼辦?是不是UUID更方便?
分庫分表也可以使用整型。
參考雪花算法:Twitter 開源的分佈式 id 生成算法。其核心思想就是:使用一個 64 bit 的 long 型的數字作爲全局唯一 id。在分佈式系統中的應用十分廣泛,且ID 引入了時間戳,基本上保持自增的。這 64 個 bit 中,其中 1 個 bit 是不用的,然後用其中的 41 bit 作爲毫秒數,用 10 bit 作爲工作機器 id,12 bit 作爲序列號。
聯合索引是什麼結構?
幾個字段一起存儲在節點彙總。排序方式:按照字段逐個進行排序,維護B+樹從左到右依次遞增。
使用聯合索引時,注意最左前綴原則:如果a b c
三列加了索引,查找時如果根據a b
查找,能走索引。如果根據b c
查找,無法走索引。(不能跳過某一個索引,直接用後面的索引)