innodb
大家都知道,mysql索引由存儲引擎層實現,常見的是innodb。
索引最主要得作用是提升訪問效率(類似於書籍目錄,比如新華字典,有字母索引,也有筆畫索引),索引的定義:
索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息
所以,索引是數據結構。
索引注意點
- 創建索引要大量耗時(如果是大數據的情況,要慎重)
- primary,唯一且不能爲空。 fulltext index,一般不用
- 不要使用隨機無序的字段做索引(寫入會產生大量磁盤碎片,分裂問題和單鏈表一樣,導致大量操作)
- 和業務無關的自增值當主鍵。
- 不是所有存儲引擎都支持索引,也不是所有存儲引擎都是btree實現(但是主流是)
- innodb 索引就是數據,數據就是索引(數據在葉子節點上)
- 不要在離散度小的的數據列上建立索引
- 聯合索引有最左匹配原則
- 覆蓋索引,select key from table,key全部在索引中,避免了回表
- 回表(二級索引查詢聚集索引(主鍵索引))
- 聯合主鍵索引,看業務場景,一般不推薦用
- 隨機無序的值,不適合主鍵索引
b+tree演進過程
分析其他算法
二分查找法,效率提升,但是數組不好維護
鏈表,有侷限性,查找鏈路過長
因此產生了二分查找樹,binrary search tree (BST),並且天然有序。
二分查找樹依舊擺脫不了樹深度問題(二叉樹問題),因此,平衡二叉樹出現。
一個節點只能存一個數據的話,會造成大量的頁浪費。
因此BTREE產生,一個樹節點的大小設計爲16kb=>16384 bytes。一個節點存儲了 16384 / len(key) 個索引。
分裂、合併保持平衡和平衡二叉樹保持一致。
B+Tree非葉子節點只保存索引,並且分爲聚集索引和非聚集索引。非聚集索引的葉子節點保存的是聚集索引的地址。聚集索引的葉子節點保存的是實際存儲的值。
b+tree特性
- 關鍵字數量和度的數量是 N:N,樹的深度更小
- 只有葉子節點纔有數據(io次數很穩定,就是樹的深度,並且使中間節點能存更多,能更矮更胖【樹形狀】)
- 葉子節點有雙向指針(遍歷數據更快,只需要從葉子節點最左邊開始即可)
一些其他question:
- 要是一行數據大於16k咋辦,一個page放不下怎麼辦,分裂成多個節點嗎?
https://gper.club/answers/7e7e7f7ff1g59gc0g6e 一步步來分析:
1)InnoDB Page默認大小爲16KB = 16384 bytes,葉子節點最大爲 16384 字節。
2)如果一行數據的大小超過了page的可用空間,怎麼辦?比如一行數據包含了一個varchar字段,varchar最大長度65536 bytes(實際是65532bytes),如果字段達到這個長度,顯然一個page存不下這行數據
3)這個時候會發生行溢出( overflow pages),數據存儲在所謂的溢出頁(off-page)中
4)不同的行格式(row format)對於行溢出的處理不同 參考官網 https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html 這裏有一篇中文的行格式的文章:https://www.cnblogs.com/25-lH/p/12739837.html 頁溢出確實會影響性能,儘量縮減字段長度。
- 爲什麼二級索引不存主鍵所在的葉子節點的地址呢?
聚集索引的樹可能會發生變化,存地址的話,不可靠,或者增加了維護性
- 沒有主鍵索引的時候怎麼辦?
_row_id,會自動創建一個索引