理解索引(上)

最近有個需求,要修改現有存儲結構,涉及查詢條件和查詢效率的考量,看了幾篇索引和HBase相關的文章,回憶了相關知識,結合項目需求,說說本身的理解和總結。html

部份內容摘錄了幾個博友的文章,最後會給出文章連接,感謝他們的精彩分析。node

會從如下幾個方面介紹:mysql

  • 爲何須要索引
  • 索引的類別
  • MySQL索引演化
  • MySQL索引優化
  • HBase介紹
  • HBase存儲結構
  • HBase索引介紹
  • 業務需求及設計

準備分3篇文章介紹,這篇主要介紹前3小節,理解咱們經常說的MySQL索引,第2篇重點介紹索引分析方法和常見索引優化,第3篇介紹下業務中使用的HBase。算法

爲何須要索引

整體來講,索引是爲了提升查詢速度的,當數據量比較大時,從頭至尾依次檢索是沒法接受的。另外,存儲的數據會包含多個屬性來描述業務實體,屬性能夠連續或分開存儲,分別對應到MySQL和HBase。sql

以MySQL爲例,學生這個實體有學號、姓名、性別、所屬班級等屬性,通常還有個主鍵ID,如今有個需求:查詢學號爲002的學生姓名。數組

學生實體

數據是存儲在磁盤上的,操做系統讀取磁盤的最小單位是塊,若是沒有索引,會加載全部的數據到內存,依次進行檢索,加載的總數據會不少,磁盤IO多。微信

若是有了索引,會以學號爲key建立索引,MySQL採用B+樹結構存儲,一方面加載的數據只有學號和主鍵ID,另外一方便採用了多叉平衡樹,定位到指定學號會很快,根據關聯的ID能夠快速定位到對應行的數據,因此檢索的速度會很快,由於加載的總數據不多,磁盤IO少。性能

可見,索引能夠大大減小檢索數據的範圍、減小磁盤IO,使查詢速度很快,由於磁盤IO是很慢的,是由它的硬件結構決定的。優化

下面,再詳細介紹下磁盤存儲結構和數據定位過程,加深對索引的理解。ui

硬盤存儲結構

磁盤內部結構以下:

  • 不少個盤片被串在一個主軸上,主軸帶着他們快速的旋轉;
  • 每一個盤片都有不少一圈一圈的磁道,每一個磁道又分爲一個一個的扇區;
  • 多個盤片上的同一位置的磁道組成了一個柱面;
  • 每一個盤片上都有能夠讀寫數據的磁頭;

訪問數據時,能夠說:把1柱面,1磁頭,1扇區的數據取出來?

磁盤會把1磁頭挪到1柱面,就是對應的磁道, 這叫「尋道時間」,而後再旋轉磁盤,讓磁頭指向1扇區,開始讀取數據,這叫「旋轉時間」,轉速快的硬盤能更快的旋轉到特定扇區,因此性能會更好。

結構比較複雜,但操做系統給封裝了,提供了一個叫作邏輯塊的方式,你看到磁盤就是有一個個「塊」組成的,編號爲1, 2, 3, ..n,把指定的塊轉化成柱面,磁頭,扇區, 按照上面說的方法尋道,旋轉,讀取數據。

爲了方便咱們操做,操做系統又進一步抽象,抽象成了文件,由文件系統去保存和讀取數據,咱們只須要與文件打交道,文件使用inode結構,經過它能夠輕鬆的找到這個文件所使用的全部磁盤塊。

inode

扇區是對硬盤而言,塊是對文件系統而言,塊是文件存取的最小單位,通常一個塊由連續的幾個扇區組成。

一個表的數據塊以鏈表的方式串聯在一塊兒,數據以行爲單位一行一行的存放在磁盤的塊中。

表在磁盤的存儲

數據定位過程

以MySQL的B+樹爲例,簡單說下幾種常見場景下,數據的定位過程。

第一種場景:索引精確查找

select * from user_info where id = 23 ;
複製代碼

肯定定位條件, 找到根節點Page No, 根節點讀到內存, 逐層向下查找, 讀取葉子節點Page,經過 二分查找找到記錄或未命中。

索引精確查找

第二種場景:索引範圍查找

select * from user_info where id >= 18 and id < 22 ;
複製代碼

讀取根節點至內存, 肯定索引定位條件id=18, 找到知足條件第一個葉節點, 順序掃描全部結果, 直到終止條件知足id >=22。

索引範圍查找

第三種場景:全表掃描

select * from user_info where name = 'abc' ;
複製代碼

直接讀取葉節點頭結點, 順序掃描, 返回符合條件記錄, 到最終節點結束

全表掃描

第四中場景:二級索引查找

create table table_x(int id primary key, varchar(64) name , key sec_index(name) ) ;
select * from table_x where name = 'd' ;
複製代碼

經過二級索引查出對應主鍵,拿主鍵回表查主鍵索引獲得數據, 二級索引可篩選掉大量無效記錄,提升效率

二級索引查找

索引的類別

上面介紹了索引的優勢和數據的定位過程,對其有了總體瞭解,另外,索引有不一樣種類和不一樣的實現方式,這節重點梳理下這些概念。

聚簇索引與非聚簇索引

簡單來講,彙集索引是一種索引組織形式,索引的鍵值邏輯順序決定了表數據行的物理存儲順序,而非彙集索引則就是普通索引了,僅僅只是對數據列建立相應的索引,不影響整個表的物理存儲順序。

聚簇索引的優勢有:

  • 範圍查詢效率更高;
  • 特別適合有一小部分熱點數據頻繁讀寫的場景;
  • 經過主鍵訪問數據時快速可達;

InnoDB引擎是彙集索引組織表,它的彙集索引選擇規則是這樣的:

  • 首先選擇顯式定義的主鍵索引作爲彙集索引;
  • 若是沒有,則選擇第一個不容許NULL的惟一索引;
  • 仍是沒有的話,就採用InnoDB引擎內置的ROWID做爲彙集索引;
主鍵索引和輔助索引

主鍵索引,簡稱主鍵,由一個或多個列組成,用於惟一性標識數據表中的某一條記錄。

InnoDB數據表的主鍵設計一般遵循幾個原則:

  • 採用一個沒有業務用途的自增屬性列做爲主鍵;
  • 主鍵字段值老是不更新,只有新增或者刪除兩種操做;
  • 不選擇會動態更新的類型,好比當前時間戳等;

輔助索引,常規所指的索引,也叫二級索引,又分爲惟一索引和非惟一索引。

InnoDB引擎中,主鍵索引會被選中做爲彙集索引,而惟一索引和普通輔助索引間除了惟一性約束外,在存儲上沒本質區別。

MySQL索引演化

本小節主要介紹索引是如何根據需求一步步演變最終成爲B+樹結構,基本思路是減小訪問數據的總量,相應的減小磁盤IO。

密集索引(Dense Index )

根據減小無效數據訪問的原則,咱們將鍵的值拿過來存放到獨立的塊中。而且爲每個鍵值添加一個指針, 指向原來的數據塊。

密集索引

當進行定位操做時,再也不進行表掃描。而是進行索引掃描(Index Scan),依次讀出全部的索引塊,進行鍵值的匹配,這樣帶來的問題是須要不少空間來存儲Dense索引。另外索引的定位效率也比較低,能夠經過排序和查找算法來減小IO的訪問。

假設一個塊中能存儲100行數據,10,000,000行的數據須要100,000個塊的存儲空間。假設鍵值列(+指針)佔用一行數據1/10的空間。那麼大約須要10,000個塊來存儲Dense索引。所以咱們用大約1/10的額外存儲空間換來了大約全表掃描10倍的定位效率。

折半塊查找

須要對Dense進行索引,每一個索引塊內是有序的,另外,須要一個數組按順序存儲索引塊地址,這樣總體就有序了,數組也要存儲到磁盤上,放在單獨的塊鏈中。

折半查找的時間複雜度是O(log2(N)),在上面的列子中,dense索引總共有10,000個塊。假設1個塊能存儲2000個指針,須要5個塊來存儲這個數組。經過折半塊查找,咱們最多隻須要讀取5(數組塊)+ 14(索引塊log 2(10000))+1(數據塊)=20個塊。

折半塊查找

從圖中能夠看到,相對於密集索引,編號是有序的。

稀疏索引(Sparse Index)

介紹基於塊的折半查找時發現,讀出每一個塊後只須要和第一行的鍵值匹配,就能夠決定下一個塊的位置(方向)。 所以有效數據是每一個塊的第一行數據,將每個塊的第一行的數據單獨拿出來,和索引數組的地址放到一塊兒。這樣就能夠直接在這個數組上進行折半查找了,這個數組就進化成了Sparse Index了。

稀疏索引

須要10個塊來存儲10000個Dense Index塊的地址和首行鍵值,經過Sparse索引,僅須要讀取10(Sparse塊)+1(Dense塊)+1(數據塊)=12個塊。

多層稀疏索引

由於Sparse Index自己是有序的,因此能夠爲Sparse Index再建sparse Index。經過這個方法,一層一層的創建 Sparse Indexes,直到最上層的Sparse Index只佔用一個塊爲止。

多層稀疏索引

這個例子中,Sparse Index只有10個塊,只須要再創建一個Sparse Index.經過兩層Sparse Index和一層Dense Index查找時,只需讀取1+1+1+1=4個塊。

若是數據自己是基於某個Key來排序的,那麼能夠直接在數據上創建sparse索引,而不須要創建一個dense索引層(能夠認爲數據就是dense索引層),這就是說的彙集索引。

B+樹

因爲鍵值是有序的,所以能夠進行範圍查找。只須要將數據塊、Dense Index塊分別以雙向鏈表的方式進行鏈接, 就能夠實現高效的範圍查找。以下圖所示:

範圍索引

這就是咱們常說的B+ Tree,倒過來看下:

B+樹

最後總結下它的特色:

  • 每次進行定位操做時,都從根開始查找;
  • 每層索引只須要讀出一個塊;
  • 最底層的Dense Index或數據稱做葉子(leaf);
  • 每次查找都必需要搜索到葉子節點,才能定位到數據;
  • 索引的層數稱做索引樹的高度(height);
  • 索引的IO性能和索引樹的高度密切相關,索引樹越高,磁盤IO越多;
  • 進行範圍查找時,效率很高;

參考文章:

  1. MYSQL-B+TREE索引原理
  2. 我是一塊硬盤
  3. FAQ系列 | MySQL索引之彙集索引
  4. 由淺入深理解索引的實現

歡迎掃描下方二維碼,關注個人我的微信公衆號,查看更多文章 ~

情情說
相關文章
相關標籤/搜索