1. 程式人生 > >通俗易懂講索引(二)

通俗易懂講索引(二)

原創文章轉載請註明出處!

1.2 稀疏索引

由於稠密索引中索引項和記錄都是一一對應的,這導致索引項的數量過多。那能不能不一一對應,要是一個索引項能對應100條記錄那麼索引項的數量將降低100倍。答案當然是能,而這正是稀疏索引的思路即一個索引項對應n條記錄。

這裡寫圖片描述

那應該怎麼建立索引呢?仍然使用前面商品列表的例子。如果商品記錄表中有5億條記錄,並且記錄都是按照商品編號從小到大排序的(這可是線性索引的基本要求),我們可以將這些記錄進行分組,若每組100條記錄,可以獲得500萬條記錄。分組完成後,跟前面建立一樣,我們抽取記錄和它的儲存地址組成索引項,只是這次我們只抽取每組的第一條記錄。這樣我們就建立了一個稀疏索引,索引項只有500萬條,佔用空間約50M,這可比稠密索引的5G降低了很多啊,記憶體表示毫無壓力。而且由於索引項變少,插入或刪除時索引的維護成本也降低了。那怎麼搜尋呢?

比如我們要查詢編號為yyy的商品,怎麼查?我們拿這個編號和索引項的編號進行比較,如果發現yyy大於第i個索引項,但是小於第i+1個索引項,說明yyy這個編號在第i組中。我們通過第i個索引項拿到其儲存地址,然後遍歷該組中的100項記錄,若編號存在則一定能找到該商品記錄。

上面說了這麼多的稀疏索引的好處,那稀疏索引就沒有缺點麼?當然是有的,那就是其搜尋速度要比稠密索引慢,理論上來說最壞的情況下其查詢次數約為klogn, k為每組記錄的數量。那前面5億條資料的例子,查詢次數約為2225.3,稠密索引則約為28.8,前者是後者的約77倍,但是兩者的時間複雜度都是O(logn),所以有時計算漸進複雜度略去的常數影響也不小。從下圖中可有看出隨著k的增加查詢所需次數也隨之增加。

這裡寫圖片描述

除了查詢效率上的問題外,稠密索引在資料量進一步增大,如500億條時,每組100條記錄,其索引項的數量仍可能會達5億,儲存空間要約5G。原來在稠密索引中的儲存問題就再次出現在了稀疏索引中。有人可能說我分組變大一點不就行了麼,然而每組資料量過大時會佔用很多塊仍會反覆讀取硬碟,而且分組變大也會導致查詢次數增加,查詢效率仍然不高。因此稀疏索引在資料量不是特別大的情況下是可以使用的,但是當資料量超過一定量時其弊端也就顯現出來了。那這麼大的資料量下就沒有辦法了麼?當然有!接下來我們就說一下可以解決這一問題的多級索引。

2 多級索引

從前面分析可知,在資料量大到一定程度的情況下,稠密索引和稀疏索引索引量大無法放到記憶體的問題,此時索引只能放在硬碟中,查詢時反覆讀取硬碟導致查詢效率變低。如果稠密索引分佈在10萬個塊中,用二分法查詢需要讀取硬碟log(100000)約130次。由於機械硬碟讀寫存在機械性的操作,尋道、旋轉延遲和資料傳輸等都會導致其資料讀取速度相對記憶體要低很多,一般情況下一次讀取要3ms到15ms。因此,一次查詢平均需要1170ms,這意味著1秒一次查詢都無法完成。所以,大資料量的情況下提升查詢速度的關鍵是減少硬碟的讀取次數。那應該怎麼處理呢?

假如我們已經建立了一個稠密索引,它一共佔用10萬個塊。然後,我們在此基礎上再建立一個稀疏索引,每個索引項指向一個塊,我們可以得到10萬條索引項,若仍按照前面說的索引項佔1kB,那這個稀疏索引需要大約需要100M。如果記憶體夠用,可以把這個稀疏索引都放到記憶體中,那麼查詢時只需要讀取一個塊,所需時間平均約9ms。一秒鐘可以查詢100多次。這可比一秒查一次快很多了。但是,有人說了,我記憶體沒法預留100M的空間放索引,100M也得放到硬碟上,那怎麼辦?那也有辦法,假如這100M的索引佔用1000個塊(注意:文中提到的索引所佔用的塊數並未根據塊的一般大小和索引大小進行計算,只是一個假設),我們在剛才建立的稀疏索引的基礎上再建立一個稀疏索引,同樣該索引的索引項分別指向那1000個塊,這樣這個稀疏索引一共有1000個索引項,佔用空間不到1M。此時把它放到記憶體肯定沒有問題了,進行一次查詢,需要讀取塊兩次,平均耗時約18ms,1秒內可以查詢約55次,還是挺快的。

這裡寫圖片描述

上面討論中先在資料上建立了一個稠密索引,又在稠密索引上建立了一個稀疏索引,然後又在這個稀疏索引上建立了一個稀疏索引,這個整體是一個三層或三級的索引,如上圖所示,像這樣二級或二級以上的索引叫做多級索引,如圖所示。其實多級索引也有不少種,像B+樹索引,這些我們將在以後詳細介紹。

3 聚集索引和非聚集索引

前面我們建立稠密索引的時候,把商品的編號和這個記錄的儲存地址拿出來並按編號排序後建立的。現在可能有兩種情況,一種是商品記錄清單本身就是按照商品編號排序的,也就是索引順序和商品清單順序一樣,我們把這樣的索引叫做聚集索引。另一種情況是商品記錄本身並未按照商品編號排序,但是我們建立的索引是排過序的,也就是說索引順序和商品記錄的順序是不一樣的,我們把這樣的索引叫做非聚集索引。

4 倒排索引(Inverted Index)

前面我們為用編號查詢商品建立了不同的索引。但是如果現在我有編號從1到100的100篇文章每篇文章大約有5000字,我們想查詢這100篇文章中包含 ”火箭“ 這個詞語的有哪幾篇,該怎麼辦?最簡單的就是挨著文章搜一遍,這種方法可行,但是效率好像太差了些。如果我有1萬篇文章甚至更多那不得等崩潰了。倒排索引就是針對這種情況出現的。

我們先把文章內容進行分割,然後提取出每篇文章的詞語,然後把所有文章的詞語彙總成一個表:

詞語 文章編號
火箭 1,3,5
衛星 1,2,7

加入要搜尋”火箭“一詞,立刻就能找到,是1,3,5三個編號的文章包含這個詞語。但是光有編號還不行,得直到它具體的存地址。那我們就再根據編號建立另一個表:

文章編號 儲存地址
1 xxx
2 yyy
3 aaa
4 bbb
5 ddd

這個表是不是很熟悉,對了,它就是一個稠密索引。有了這個表,我們就能根據文章編號一下找到儲存文章的地方然後取得文章了。這個過程是不是很熟悉,這其實是很多搜尋引擎工作的基本原理,當然實際上要比這個複雜多了。倒排索引這個翻譯其實不太準確。Inverted的意思是我先從詞彙查詢到編號,然後再通過編號查詢具體內容,而不是從編號直接查詢到內容。