數據庫索引底層原理與優化策略

博客內容

1.索引數據結構紅黑樹, Hash, B+樹詳解
2.索引是怎樣支撐千萬級表的快速查找
3.如何基於索引B+樹精準建立高性能索引
4.聯合索引底層數據結構又是怎樣的
5.MySQL索引優化最佳實踐

索引底層原理

什麼是索引?

索引是幫助MySQL高效獲取數據的排好序的數據結構

索引數據結構有哪些

  • 二叉樹
  • 紅黑樹
  • Hash表
  • B-Tree

在這裏插入圖片描述

select * from t where t.col2 = 89;

當沒有索引時,需要在磁盤中6次IO
當創建了索引時,如右側二叉樹,只需要2次IO

右側的二叉樹的每個節點中存儲的時每個記錄的地址

什麼是記錄?

簡單的說,就是插入的每一行數據元素

這麼看來,二叉樹讀取元素的時間複雜度是O(log n),爲什麼索引內部實現是B+樹,他有什麼缺點

比如說,當創建的主鍵是col1,二叉樹就會成爲這樣的

在這裏插入圖片描述
如果是有序的話,那麼他就和沒有索的時候向磁盤讀取的次數是一樣的
所以,沒有采用這種數據結構

紅黑樹呢,他是可以自選的,不會使其單邊增長,他的使用情況時怎樣的

紅黑樹也叫不完全平衡二叉樹,如果放入的數據多了,那麼樹的高度會很高,查詢的成本就會隨樹的高度增加而增加
如果進行一次千萬級別的查詢 那麼就要向磁盤讀取23次左右,差不多就10秒左右了

那麼Hash表呢

只需要進行一次hash(key),就可以找到在磁盤上的內存地址只需要一次讀取磁盤操作,就可以得到想要的記錄,這是非常快的

既然Hash表查詢操作那麼快,爲什麼MySQL在99%的情況下使用的是B+樹

這個很好理解,如果我們查找的是一個範圍,Hash表就無能爲力了,因爲哈希表的特點就是可以快速的精確查詢,但是不支持範圍查詢

如果是B-Tree呢?

B-Tree:

  • 葉節點具有相同的深度
  • 葉結點的指針爲空
  • 節點中的數據索引從左到右遞增排列
  • 一個節點是16K
    在這裏插入圖片描述
    可以發現同樣的元素,B樹的表示要比完全平衡二叉樹要“矮”,原因在於B樹中的一個節點可以存儲多個元素。
    B樹其實就已經是一個不錯的數據結構,用來做索引效果還是不錯的。

那爲啥沒用B樹,而用了B+樹?

如果查詢的是一個範圍,也是相當麻煩的,需要拿出後面的每個節點,沒有解決查詢範圍的查詢

那麼來看看B+樹 :

  • 非葉子節點不存儲data,只存儲suoyin,可以放更多的索引
  • 葉子節點不存儲指針
  • 順序訪問指針,提高區間訪問的性能

在這裏插入圖片描述
可以看出開,所有的數據在葉子節點上有一份完整的數據,葉子節點上有一份非葉子節點的冗餘.

我們看一下上面的數據結構,最開始的Hash不支持範圍查詢,二叉樹樹高很高,只有B樹跟B+有的一比。

B樹一個節點可以存儲多個元素,相對於完全平衡二叉樹整體的樹高降低了,磁盤IO效率提高了。

而B+樹是B樹的升級版,只是把非葉子節點冗餘一下,這麼做的好處是爲了提高範圍查找的效率

提高了的原因也無非是會有指針指向下一個節點的葉子節點。

小結:到這裏可以總結出來,Mysql選用B+樹這種數據結構作爲索引,可以提高查詢索引時的磁盤IO效率,並且可以提高範圍查詢的效率,並且B+樹裏的元素也是有序的。

那麼,一個B+樹的節點中到底存多少個元素最合適你有了解過麼?

大概三行B+樹就可以存儲2000多萬個索引元素,一個節點16k,非葉子節點大概可存儲1170個索引,葉子節點大概16個索引
一般2~4層,如果數據更多的話,那就分庫分表

數據庫的存儲引擎瞭解嗎

存儲引擎:

  • 非聚集索引:MyISAM
  • 聚集索引 : Innodb

哦? 說說MyISAM

MyISAM索引文件和數據文件是分離的(非聚集)

創建一個表時,會創建三個文件:

  • XXX.firm : 存儲的是標的數據定義相關的信息,表結構
  • XXX.MYD : 存儲的是表中所有的數據行
  • XXX.MYI : 存儲的是表的索引,底層是B+樹

在這裏插入圖片描述

select * from t where col1 = 49;

那麼就通過三次IO就取到地址0x90,在MYD中查找記錄

說說InnoDB

InnoDB索引實現(聚集):

  • 表數據文件 本身就是按B+樹組織的一個索引結構文件
  • 聚集索引-------葉節點包含了完整的數據記錄

創建一個表時,會創建兩個文件:

  • XXX.firm : 存儲的是標的數據定義相關的信息,表結構
  • XXX.idb : 按B+樹組織的一個索引結構文件,數據在索引的葉子節點中

對比兩張B+樹的葉子節點,你就能明白聚集與非聚集的含義

在這裏插入圖片描述

那你說說什麼是聚集索引?

主鍵索引和數據是聚集在葉子節點上,搜索效率是高於非聚集索引的

爲什麼InnoDB表必須有主鍵,並且推薦使用整型的自增型的自增主鍵?

  • 如果沒有創建主鍵索引,MySQL內部會選取一個可標識的列作爲主鍵,如果找不到,他會自動生成一個默認的隱藏列,InnoDB的數據元素必須依靠主鍵索引創建
  • 如果使用字符串(UUID)比較,先得轉換成ASCII比較,比較慢,使用整型會快很多,如果是自增型,可以直接往後插,效率較快,舉個例子,如果一個葉子節點已經滿了,想再往進插入的時候,會影響整個表結構,表會分裂

在這裏插入圖片描述

在這裏插入圖片描述

爲什麼非主鍵索引結構葉子節點存儲的是主鍵值?

一致性和節省存儲空間

優化

聯合索引的底層存儲結構是什麼樣子

在這裏插入圖片描述
滿足最左前綴匹配原則(a,b,c)先匹配a,如果a相等,匹配b,以此類推。

其實聯合索引的查找就跟查字典是一樣的,先根據第一個字母查,然後再根據第二個字母查,或者只根據第一個字母查,但是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左前綴原理。

詳細說說最左匹配原則

比如聯合索引索引的例子(a,b,c).下面的索引都可以用到:

select * from t where a=1//(a)
select * from t where a=1 and b=2//(a,b)
select * from t where a=1 and b=2 and c=3// (a,b,c)

再比如說:

select * from t where a=1 and c=3// 只會用到索引a
select * from t where b=1 and c=2// 沒有用到索引,會查詢到索引,輔助索引查主鍵索引

如果是這樣

select * from table where b=2 and a=1select * from table where b=2 and a=1 and c=3

mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。但我們還是最好按照索引順序來查詢,這樣查詢優化器就不用重新編譯了。

聽說過前綴索引嗎?

  • 就是列的前綴作爲索引,比如 like 'XXX%',但是像like '%xx%不會用到前綴索引
  • MySQL 前綴索引能有效減小索引文件的大小,提高索引的速度
  • 但是前綴索引也有它的壞處:MySQL 不能在 ORDER BYGROUP BY 中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)。

簡單說說你知道的索引優化策略

  • 最左前綴匹配原則

  • 一定要建主鍵索引

  • where,on,group by,order by 中出現的列使用索引

  • 儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0

  • 對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵

  • 索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但這個進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’)

  • 較長的字符串使用前綴索引

  • 儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可

  • 不要過多創建索引, 權衡索引個數與DML之間關係DML也就是插入、刪除數據操作。這裏需要權衡一個問題,建立索引的目的是爲了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因爲我們修改的表數據,索引也需要進行調整重建

  • 對於like查詢,"%"不要放在前面

SELECT * FROM t WHERE uname LIKE'後盾%' -- 走索引 
SELECT * FROM t WHERE uname LIKE "%後盾%" -- 不走索引·
					      -- 查詢where條件數據類型不匹配也無法使用索引 
  • 字符串與數字比較不使用索引
CREATE TABLE a (a char(10)); 
EXPLAIN SELECT * FROM a WHERE a="1" – 走索引 
EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引 
  • 正則表達式不使用索引,這很好理解,類似前綴索引,所以爲什麼在SQL中很難看到regexp關鍵字的原因
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章