索引是對資料庫表中乙個或多個列的值進行排序的資料結構,以協助快速查詢、更新資料庫表中資料。在mysql中,索引是在儲存引擎層實現的,所以並沒有統一的索引標準,即不同儲存引擎的索引的工作方式並不一樣。而即使多個儲存引擎支援同一種型別的索引,其底層的實現也可能不同。索引加速了資料訪問,因為儲存引擎不會再去掃瞄整張表得到需要的資料;相反,它從根節點開始,根節點儲存了子節點的指標,儲存引擎會根據指標快速尋找資料。
索引在 mysql 資料庫中分三類:
我們在工作開發中最常接觸到的是innodb儲存引擎中的b+樹索引。在innodb中,每一張表其實就是多個b+樹,即乙個主鍵索引樹和多個非主鍵索引樹。 執行查詢的效率:使用主鍵索引 > 使用非主鍵索引 > 不使用索引。如果不使用索引進行查詢,則從主鍵索引b+樹的葉子節點進行遍歷。
要了解 b+樹索引,就不得不提二叉查詢樹、b樹這兩種資料結構。b+樹就是由它們演化來的。
對一張表建立二叉查詢樹索引的示意圖如下:
為了讓二叉查詢樹支援按照區間來查詢資料,我們可以對它進行這樣的改造:樹中的節點並不儲存資料本身,而是只是作為索引。除此之外,我們把每個葉子節點串在一條鍊錶上,鍊錶中的資料是從小到大有序的。經過改造之後的二叉樹如下圖所示。
如果我們用樹這種資料結構作為索引的資料結構,那我們每查詢一次資料就需要從磁碟中讀取乙個節點,也就是乙個磁碟塊。二叉查詢樹每個節點只儲存乙個鍵值和資料的。說明每個磁碟塊僅僅儲存乙個鍵值和資料!但是,我們要為幾千萬、上億的資料構建索引,如果將索引儲存在記憶體中,儘管記憶體訪問的速度非常快,查詢的效率非常高,但是,占用的記憶體會非常多。比如,我們給1億個資料構建二叉查詢樹索引,那索引中會包含大約1億個節點,每個節點假設占用16個位元組,那就需要大約1gb的記憶體空間。給一張表建立索引,我們需要1gb的記憶體空間。如果我們要給10張表建立索引,那對記憶體的需求是無法滿足的。如何解決這個索引占用太多記憶體的問題呢?
可以借助時間換空間的思路,把索引儲存在硬碟中,而非記憶體中。我們都知道,硬碟是乙個非常慢速的儲存裝置。通常記憶體的訪問速度是納秒級別的,而磁碟訪問的速度是毫秒級別的。讀取同樣大小的資料,從磁碟中讀取花費的時間,是從記憶體中讀取所花費時間的上萬倍,甚至幾十萬倍。這種將索引儲存在硬碟中的方案,儘管減少了記憶體消耗,但是在資料查詢的過程中,需要讀取磁碟中的索引,因此資料查詢效率就相應降低很多。
二叉查詢樹經過改造之後,支援區間查詢的功能就實現了。不過,為了節省記憶體,如果把樹儲存在硬碟中,那麼每個節點的讀取(或者訪問),都對應一次磁碟io操作。樹的高度就等於每次查詢資料時磁碟io操作的次數。我們前面講到,比起記憶體讀寫操作,磁碟io操作非常耗時,所以我們優化的重點就是儘量減少磁碟io操作,也就是,盡量降低樹的高度。那如何降低樹的高度呢?
如果我們把索引構建成m叉樹,高度是不是比二叉樹要小呢?如圖所示,給16個資料構建二叉樹索引,樹的高度是4,查詢乙個資料,就需要4個磁碟io操作(如果根節點儲存在記憶體中,其他節點儲存在磁碟中),如果對16 個資料構建五叉樹索引,那高度只有2,查詢乙個資料,對應只需要2次磁碟操作。如果m叉樹中的m是100,那對一億個資料構建索引,樹的高度也只是3,最多只要3次磁碟io就能獲取到資料。磁碟io變少了,查詢資料的效率也就提高了。
從上圖可以看出,b樹相對於二叉查詢樹,每個節點儲存了更多的鍵值(key)和資料(data),並且每個節點擁有更多的子節點,子節點的個數一般稱為階,上述圖中的b樹是3階b樹,高度也會很低。基於這個特性,b樹查詢資料讀取磁碟的次數將會很少,資料的查詢效率也會比二叉查詢樹高很多。
b樹為什麼要設計成多路?為了進一步降低樹的高度,路數越多樹的高度越低,如果設計成無限多路就退化成有序陣列了。
了解了b樹後再來了解下它的變形版:b+樹,它比b樹的查詢效能更高。b+樹由b樹改良而來,屬於改良版的多路平衡查詢樹。下圖就是一棵b+樹。
b+樹是由b樹改進而來的,所以b樹能解決的問題,b+樹都能解決,那麼b+樹能解決哪些b樹所不能解決的問題呢?
掃庫、掃表能力更強:如果我們要對錶進行全表掃瞄,只需要遍歷葉子節點就可以 了,不需要遍歷整棵b+樹
b+樹的磁碟讀寫能力相對於b樹來說更強:根節點和非葉子節點不儲存資料區, 所以乙個節點可以儲存更多的關鍵字,一次磁碟載入(io操作)能獲取到相對更多的關鍵字
效率穩定:b+樹永遠是在葉子節點拿到資料,所以io次數是穩定的,而b樹運氣好的話根節點就能拿到資料,運氣不好就要到葉子節點才能拿到資料,所花費的時間會有差異。
使用場景決定設計,b+樹經常用作資料庫的索引,資料庫中的select操作並不是只返回一條資料而是多條。如果用b樹的話,可能需要跨層訪問,而b+樹由於所有資料都在葉子結點,不用跨層,同時鍊錶有序只要找到首尾,就可以定位到所有符合條件的資料。這就是b+樹比b樹更優的地方。
hash更快,為什麼資料庫還用b+樹作索引?與業務場景有關,如果只選乙個資料,hash確實更快,但是select經常要選擇多條,這時由於b+樹索引有序並且又有鍊錶相連,它的查詢效率比hash更快。而且資料庫的索引是儲存在磁碟上的,資料量大的情況下無法一次性裝入記憶體,b+樹的多路設計可以允許索引資料分批載入到記憶體,樹的高度也很低,提高查詢效率。
每乙個索引在innodb裡面對應一棵b+樹。
假設,我們有乙個主鍵列為id的表,表中有字段k,並且在k上建立普通索引。建立這張表並插入一些資料。
create
table test(
id int
primary
key,
k int
notnull
, name varchar(16
),index
(k))
engine
=innodb
;insert
into test (id,k)
values
(100,1
),(200,2
),(300,3
),(500,5
),(600,6
);
這張表的兩棵索引b+樹的示例示意圖如下:
也就是說,基於非主鍵索引的查詢需要多掃瞄一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。
b+樹為了維護索引有序性,在插入新值的時候需要做必要的維護。以上面這個圖為例,如果插入新的行id值為700,則只需要在r5的記錄後面插入乙個新記錄。如果新插入的id值為400,就相對麻煩了,需要邏輯上挪動後面的資料,空出位置。
當然有**就有合併。當相鄰兩個頁由於刪除了資料,利用率很低之後,會將資料頁做合併。合併的過程,可以認為是**過程的逆過程。
看到這裡,終於理解了為什麼建表規範裡面要求建表語句裡一定要有自增主鍵,就是為了避免頁**。自增主鍵的插入資料模式,正符合了前面提到的遞增插入的場景。每次插入一條新記錄,都是追加操作,都不涉及到挪動其他記錄,也不會觸發葉子節點的**。如果使用帶業務邏輯的字段做主鍵(例如身份證號),則往往不容易保證有序插入,這樣寫資料成本相對較高。
InnoDB 儲存引擎
innodb是事務型資料庫的首選引擎,支援事務安全表 acid 支援行鎖定和外來鍵。mysql 5.5.5 之後,innodb作為預設儲存引擎。innodb的主要特性有一下幾項。a.innodb給mysql提供了具有提交 回滾和崩潰恢復能力的事務安全 acid相容 儲存引擎。innodb鎖定在行級並...
InnoDB 儲存引擎
設計上採用了類似於oracle資料庫的架構 接下來 詳細介紹 innodb 儲存引擎的 體系架構 及其不同於其他儲存引擎的特性 一 概述 innodb 儲存引擎 是第乙個完整支援acid事物的 mysql 儲存引擎 特點 1 行鎖設計 2 支援mvcc 3 提供一致性非鎖定讀 4 最有效地利用 以及...
InnoDB儲存引擎
內外存交換的基本單位 mysql將資料從外存讀入記憶體不是以記錄為單位,這樣消耗太大,是以頁為單位,每個頁裡填充記錄。每頁大小為16kb。記錄堆 行記錄儲存區,分為有效記錄和已刪除記錄兩種 自由空間鍊錶 已刪除記錄組成的鍊錶 未分配空間 頁尾 儲存頁面校驗資訊 頁內的資料是按照主鍵的順序有序儲存的。...