在日常工作中,遇到查詢資料比較慢的情況,一般是資料量很大,且沒用到索引,索引就像書的目錄,如果沒有目錄,需要一頁一頁的查詢,效率很慢。有了目錄,可以快速的查詢資料。
索引常見的三種模型
- hash 表
- 排序陣列
- 二叉查詢樹
hash 表是一種以鍵 - 值儲存資料的結構,通過 key 直接直接找到對應的 vale。hash 表只適用等值查詢場景,對範圍查詢就失效了。
排序陣列支援等值查詢和範圍查詢,在有序陣列中,使用二分查詢,查詢的時間複雜度是 O(logn)。從查詢效率來說,有序陣列確實是一個很好的選擇。但是需要新增或者刪除資料時,為了保證陣列的有序性,往中間插入的資料,需要移動陣列後面的陣列,而記憶體的分配是很耗時的過程。
二叉樹查詢樹也叫二叉搜尋樹,它的特定是一個結點上左子樹上所有的值都小於右子樹上所有的值,可以將索引的值有序的儲存在二叉樹上,如下圖所示。
查詢的速度就是樹的高度,節點每次的訪問都對應這磁碟的 IO 操作,同樣的資料,為了加快查詢速度,需要降低樹的高度,而降低樹的高度,需要將二叉樹轉成 N 叉樹。這裡的 N 和mysql 查詢的頁的大小有關。
B+樹結構
b+樹的查詢過程
如圖所示,B+ 樹是一個 N 叉樹,每個節點有索引和指標。如果查詢資料項28。
- 首先會把磁碟塊1載入到記憶體,此時發生一次IO,在記憶體中使用二分查詢確定28在17和35之間
- 找到磁碟1中的P2指標,通過磁碟1的P2指標指向的磁碟3載入到記憶體,發生第二次IO
- 28在26和30之間,找到磁碟3的P2指標指向磁碟8,把磁碟8載入到記憶體中,發生第三次IO
- 在記憶體中做二分查詢找到28,總共三次IO
真實情況是,三層的 b+ 樹可以表示上百萬的資料,如果百萬的資料只需要三次IO,效能將會很大的提升,沒有索引,查詢每條資料都需要發生一次IO,查詢的效率很低。
通過分析,我們可以知道IO次數取決於b+樹的高度,當資料一定時,每個磁碟的數量越大,樹的高度就越小,磁碟的大小也就是一個資料頁的大小,是固定的,如果資料項佔的空間越小,資料項的數量越多,樹的高度就越低,所以在選擇索引欄位的時候要儘量小,比如 int 4個位元組要比 bigint 佔8個位元組少佔一半。
B+樹和B樹的區別
- b 樹節點儲存資料,b+樹的節點不儲存資料,只是存索引,資料都儲存在葉子節點。
- b+樹葉子節點用連結串列串聯起來,而b樹沒有。
建立索引的幾個原則
- 最左匹配原則,mysql 會一直向右匹配知道遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 ,如果建立(a,b,c,d)順序的索引,d是用不到索引的。如果建立(a,b,d,c)的索引都可以用到,a、b、d的順序可以任意調整。
- = 和 in 可以亂序,比如 a = 1 and b = 2 and c = 3 建立 (a,b,c)索引可以任意順序,mysql 查詢優化器會優化查詢索引
- 儘量選擇區分度高的列作為索引,區分度指的欄位的不重複性比例,比例越大,掃描的記錄就越少,唯一鍵的區分度是1,而一些狀態,性別區分度在資料量大的面前區分度就是0
- 索引不能參與計算,保持列的乾淨,不能在索引列上新增函式,或者運算之類。因為b+樹儲存的是資料表的資料,而經過運算的資料和b+樹上的資料不能做比較,導致索引失效
- 儘量的擴充套件索引,不要新建索引。比如表中原來有a的索引,現在要新增b的索引,把原來的索引擴充套件成(a,b)的索引即可。因為沒建一個索引,就需要建立一個b+樹。
參考
如果覺得文章對你有幫助的話,請點個推薦吧!