索引的本質是排序

2021-08-19 13:11:53 字數 3113 閱讀 5258

索引是經常用到的技術,但有些程式設計師對索引的原理了解不深,發現資料查詢效能有問題立刻就想起建索引,但效果常常也不盡人意。那麼到底什麼時候該用索引以及該怎麼用?我們來分析索引清理背後的技術原理就知道了。

索引技術的初衷是為了快速從乙個大資料集中找出某個字段等於確定值(比如按身份證號找出某個人)的記錄。乙個規模(行數)為n的資料集,用遍歷查詢則需要比較n次,而如果資料是按該字段值(在索引中稱為鍵值)有序的,那麼就可以建立二叉樹用二分法查詢,只要比較logn(以2為底)次,比如10億行資料只要比較30次(10億約是2^30),這顯然能大大提高效能。有時可能還會有鍵值有重複的情況(按出生日期找人)或按鍵值區間的查詢需求(按出生日期區間找人),比較次數就會比logn大一些,但基本仍是這個數量級的。

索引的本質就是排序。

當然,我們一般不會把原始資料集排序,而是把每條記錄的鍵值和這條記錄在資料集中的位置,按鍵值次序做成乙個規模較小的資料集,這也就是索引表了。如果還有其它欄位也要用於鍵值查詢,則可以再建立別的索引。原始資料集只有乙份,索引可以有多個,如果每個索引都把原始資料集排序,則會使資料集被複製很多遍,占用空間過大。

另外,資料庫在建立索引時還要考慮資料會插入刪除,簡單排序的索引會導致插入刪除的成本非常高,這時一般會使用b樹以方便快速更新。b樹相當於把二叉樹擴充套件成n叉樹,本質上仍然是鍵值有序。(索引如何建立的話題內容不少,我們將另行撰文討論,這裡只研討索引使用)

還有一種引申出來的方法是hash索引,計算記錄鍵值的某種hash值,雜湊到1…k的自然數範圍。這樣查詢時連二分比較也不必做,直接用hash值定位了。hash方法只用來做鍵值的精確查詢,不能用來實現區間查詢,因為hash函式並不單調,已經失去原來鍵值的大小資訊了,不過這在許多場景下也夠用(按身份證號找人)。hash索引本質上也是排序,只是用了鍵值的hash值來排序。我們下面的討論還是以普通鍵值排序為例,結論也適用於hash索引。

從原理上看,顯然索引不會提高全量資料遍歷的運算效能。有些程式設計師不明就裡時為了提高分組彙總效能也建索引,就是濫用了。

理解了上述原理後,我們就能知道什麼時候索引會有效,以及書寫語法時的注意事項。

1. 只針對鍵值本身提條件的,很有效。

如:身份證號等於某值的、出生日期在某個區間內的,這些都很有效。

2. 針對鍵值的函式提條件的,大部分無效,小部分取決於資料庫優化

如:出生日期是星期幾的,索引鍵是出生日期。索引就沒法用,因為星期幾對索引無序,這時要把索引直接建在鍵值函式上,大部分資料庫都支援這種索引。

再如:年齡在某個區間的,索引鍵是出生日期。索引不能直接用,但年齡和出生日期之間是個單調函式,如果資料庫優化做得好是可能利用的。但大概率是不行的。

書寫查詢條件時要盡量寫成針對原始索引鍵值本身,不要使用函式或表示式。

3. 一般性條件中包含鍵值條件的,鍵值條件作為乙個最外層的and條件時有效

如:出生日期在某天且姓名中有某字的。資料庫會用索引找出出生日期在某天的、然後再在其中遍歷查詢出姓名中有某字的。現代商用資料庫都能夠智慧型地分析條件表示式而找到可以使用索引提速的部分。

再如:出生日期在某天或姓名中有某字的。這時候索引就沒法用了,後半部條件反正也只能遍歷,那就直接遍歷了,索引就忽略了。

書寫多個組合查詢條件時就要注意盡量把索引鍵有關的條件放在最外層和其它條件and起來,索引鍵不能用於縮小查詢範圍時不會提高效能。

如果我們為資料集查詢條件中涉及的多個欄位都建立索引,是否會進一步提高效能?

從上面的原理分析後結論比較悲催,大部分場景是只能用上乙個。

比如在字段a和b上都建有索引,查詢條件是 a=1 and b=2。先用索引a過濾出來的a=1的記錄,對b並沒有序,這時b=2的條件只能硬遍歷;反過來也一樣,先用b=2過濾的結果集對a無序,也只能遍歷了。商用資料庫一般會預估成本,選擇a和b中的過濾後結果集較小的那個索引來用。

不過,如果是a=1 or b=2反而有可能用上,優化能力較好的資料庫會分別用索引過濾出a=1和b=2的記錄,再做個並集。

還可以建立多欄位索引,如果建立a,b雙字段索引,那麼用a=1過濾後的結果集就對b有序,就可以繼續用該索引過濾b=2的條件。資料庫優化較好時會知道a=1 and b=2和b=2 and a=1是一回事,條件書寫次序可以不必刻意和索引次序一致,只要注意上一小節所說的情況3就行:盡量把索引涉及條件放在最外層。

但是,a,b雙字段索引對單獨的b=2這個條件並無效,因為對a,b有序未必對b有序。b=2這種條件普只能再遍歷了,這是許多程式設計師容易犯的錯誤。完整些說,a,b,c這樣的多字段索引,對於a=?,a=? and b=?,a=? and b=? and c=?這類條件都有效,但對於b=?,c=?,b=? and c=?這種條件是無效的,還需要重新建立關於b或c的索引。

出於這個考慮,建立一次a,b,c多欄位索引會對a,a/b,a/b/c條件都有效,那我們是否應當盡量把索引欄位搞得盡量多?從索引原理上似乎是這樣,但這樣會導致索引表也大一圈,增加io成本,所以也不一定,需要適當的權衡。

如果我們按上述原則正確地建立和使用了索引,是否就一定能提高效能呢?

還是不一定!

索引的初衷是用鍵值取數, 大多數情況是從乙個巨大的資料集中會取出很少的記錄出來。這類場景下,如果按上述原則建立和使用索引,確實是能顯著地提高效能。但有時候條件遍歷取出的記錄非常多,這就很難說是不是能提高效能了,甚至可能反而更差。

原因是這樣的:

我們前述說過,建索引時一般不會直接把原始資料集排序,而是另建乙個索引表。按索引表的次序取出的資料,對於原始資料集而言並不是連續存放的,資料庫優化做得不好時甚至可能是亂序的。硬碟取出大量不連續存放的資料時會同時取出很多無關資料,其耗時不能簡單地按取出資料量來計算,這時候使用索引取數的效能提公升就不會象希望的那樣明顯。如果亂序時還強行使用索引則還可能導致重複取,對於機械硬碟再有大量的磁頭跳動時間,結果集很大時就極有可能還不如硬遍歷的效能好。不過一般商用資料庫會預估成本後選擇合適的執行計畫,發現有可能是這些情況就不再使用索引了,所以看到的表現一般最差也就是和遍歷一樣了,但如果預估不准,執行計畫搞錯了就可能出現還不如遍歷性能好的現象。

資料庫中資料一般是按插入次序存放的,如果這個次序和索引鍵序基本一致,那麼會保證取出資料在物理上存放時是相對連續的,這時候再使用索引過濾,即使取出資料量較大也經常能觀察到比較明顯的效能提公升。

索引的本質是排序

索引是經常用到的技術,但有些程式設計師對索引的原理了解不深,發現資料查詢效能有問題立刻就想起建索引,但效果常常也不盡人意。那麼到底什麼時候該用索引以及該怎麼用?我們來分析索引清理背後的技術原理就知道了。索引技術的初衷是為了快速從乙個大資料集中找出某個字段等於確定值 比如按身份證號找出某個人 的記錄。...

索引的本質

在我學習了索引如何使用後,我開始有一些問題,而最根本的原因就是我不知道本質上索引是什麼,所以本文就是針對索引的本質來講下他的原理的。但是只以btree為例,其他資料結構或則演算法的索引原理會在其他文章講解。1.什麼是索引?首先,當我看見有一種索引的建立語句是如下的時候,create index in...

mysql的索引本質

1索引的定義 索引是幫助mysql高效獲取資料的排好序的資料結構 2 索引的本質 就是拍好序的資料結構 3.為啥b tree這種資料結構做索引的效率比其他的資料結構 紅黑樹,二叉樹,平衡二叉樹,hash表 效率高 具體的比較可以參考我之前的文章 主要是有以下幾個優點 b 樹的優點 1 b 樹的層級更...