本文節選自《這就是搜尋引擎:核心技術詳解》第三章
3.4建立索引
正如前述章節所述,索引結構如果建立好了,可以增加搜尋的速度,那麼給定乙個文件集合,索引是如何建立起來的呢?建立索引的方式有很多種,本節敘述比較實用的三種建立索引的方法。
3.4.1兩遍文件遍曆法(2-pass in-memory inversion)
顧名思義,此種方法需要對文件集合進行兩遍掃瞄,圖3-11是這種方法的示意圖。值得注意的一點是:此種方法完全是在記憶體裡完成索引的建立過程的,而另外兩種方法則是通過記憶體和磁碟相互配合來完成索引建立任務。
圖3-11 兩遍文件遍曆法
第一遍文件遍歷
在第一遍掃瞄文件集合時,該方法並沒有立即開始建立索引,而是收集一些全域性的統計資訊。比如文件集合包含的文件個數n,文件集合內所包含的不同單詞個數m,每個單詞在多少個文件中出現過的資訊df。將所有單詞對應的df值全部相加,就可以知道建立最終索引所需記憶體大小是多少,因為乙個單詞對應的df值如果是10,說明有10個文件包含這個單詞,那麼這個單詞對應的倒排列表應該包含10項內容,每一項記載某個文件的文件id和單詞在該文件對應的出現次數tf。
在獲得了上述三類資訊後,就可以知道最終索引的大小,於是在記憶體中分配足夠大的空間,用來儲存倒排索引內容。如圖3-11所示,在記憶體中可以開闢連續儲存區域,因為第一遍掃瞄已經獲得了每個單詞的df資訊,所以將連續儲存區劃分成不同大小的「片段」,詞典內某個單詞根據自己對應的df資訊,可以通過指標,指向屬於自己的記憶體「片段」的起始位置和終結位置,將來在第二遍掃瞄時,這個單詞對應的倒排列表資訊會被填充進入這個片段中。
綜上所述,第一遍掃瞄的主要目的是獲得一些統計資訊,並根據統計資訊分配記憶體等資源,同時建立好了單詞相對應倒排列表在記憶體中的位置資訊,即主要做些資源準備工作。
第二遍文件遍歷
在第二遍掃瞄的時候,開始真正建立每個單詞的倒排列表資訊,即對於某個單詞來說,獲得包含這個單詞的每個文件的文件id,以及這個單詞在文件中的出現次數tf,這樣就可以不斷填充第一遍掃瞄所分配的記憶體空間。當第二遍掃瞄結束的時候,分配的記憶體空間正好被填充滿,而每個單詞用指標所指向的記憶體區域「片段」,其起始位置和結束位置之間的資料就是這個單詞對應的倒排列表。
經過兩遍掃瞄完成索引建立後,即可將記憶體的倒排列表和詞典資訊寫入磁碟,這樣就完成了建立索引的過程。從上述流程可以看出,索引的構建完全是在記憶體中完成的,這就要求記憶體的大小一定要足夠大,否則如果文件集合太大時,記憶體未必能夠滿足需求。
從另外乙個角度看,在建立索引的過程中,從磁碟讀取文件並解析文件基本是最消耗時間的乙個步驟,而兩遍掃瞄法因為要對文件集合進行兩遍遍歷,所以從速度上不佔優勢,在實際中採用這種方法的系統並不常見。而下面介紹的兩種方法都是對文件集合進行一遍掃瞄,所以在速度方面明顯佔優。
3.4.2排序法(sort-basedinversion)
兩遍遍曆法在建立索引過程中,對記憶體的消耗要求較高,不同的文件集合包含文件數量大小不同,其所需記憶體大小是不確定的。當文件集合非常大時,可能因記憶體不夠,導致無法建立索引。排序法對此做出了改進,該方法在建立索引的過程中,始終在記憶體中分配固定大小的記憶體,用來存放詞典資訊和索引的中間結果,當分配的記憶體被消耗光的時候,把中間結果寫入磁碟,清空記憶體裡中間結果所佔記憶體,以用作下一輪存放索引中間結果的儲存區。這種方法由於只需要固定大小的記憶體,所以可以對任意大小的文件集合建立索引(參考圖3-12)。
圖3-12 排序法
中間結果記憶體排序
圖3-12是排序法在記憶體中建立索引中間結果的示意圖。讀入文件後,給文件進行編號,賦予唯一的文件id,並對文件內容解析。對於文件中出現的單詞,通過查詞典將單詞轉換為對應的單詞id,如果詞典中沒有這個單詞,說明是第一次碰到,則賦予單詞以唯一的單詞id並插入詞典中。在完成了由單詞對映為單詞id過程之後,可以對該文件內每個單詞建立乙個(單詞id,文件id,單詞頻率)三元組,這個三元組就是單詞對應文件的倒排列表項,將這個三元組追加進入中間結果儲存區末尾。如果文件內的所有單詞都經過如此處理,形成三元組序列的形式,則該文件被處理完成,開始依次序處理下一文件,過程與此類似。
隨著新的文件不斷被處理完成,儲存三元組集合的中間結果所占用的記憶體會越來越多,詞典裡包含的新單詞也越來越多,當分配的記憶體定額被佔滿時,該方法對三元組中間結果進行排序。排序的原則是:主鍵是單詞id,即首先要按照單詞id由小到大排序。次鍵是文件id,即在相同單詞id的情況下,按照文件id由小到大排序。通過以上方式,三元組變成有序形式。為了騰出記憶體空間,將排好序的三元組寫入磁碟臨時檔案中,這樣就空出記憶體來進行後續文件的處理。這裡需要注意的是:在建立索引過程中,詞典是一直儲存在記憶體中的,每次清空記憶體只是將中間結果寫入磁碟。隨著處理文件的加大,詞典占用的記憶體會逐漸增加,由於分配記憶體是固定大小,而詞典占用記憶體會越來越大,也就是說,越往後,可用來儲存三元組的空間是越來越少的。
之所以要對中間結果進行排序,主要是為了方便後續的處理。因為每一輪處理都會在磁碟產生乙個對應的中間結果檔案,當所有文件處理完成後,在磁碟會有多個中間結果檔案,為了產生最終的索引,需要對這些中間結果檔案合併。圖3-13是如何對中間結果進行合併的示意圖。
圖3-13 中間檔案合併
合併中間結果
如圖3-13所示,在合併中間結果的過程中,系統為每個中間結果檔案在記憶體中開闢乙個資料緩衝區,用來存放檔案的部分資料。因為在形成中間結果檔案前,已經按照單詞id和文件id進行了排序,所以進入緩衝區的資料已經是有序的。合併過程中,將不同緩衝區中包含的同乙個單詞id的三元組進行合併,如果某個單詞id的所有三元組全部合併完成,說明這個單詞的倒排列表已經構建完成,則將其寫入最終索引中,同時將各個緩衝區中對應這個單詞id的三元組內容清空,這樣緩衝區就可以繼續從中間結果檔案中讀入後續的三元組來進行下乙個單詞的三元組合併。當所有中間結果檔案都依次被讀入緩衝區,在合併完成後,就形成了最終的索引檔案。
3.4.3歸併法(merge-basedinversion)
「排序法」分配固定大小記憶體來建立索引,所以無論要建索引的文件集合有多大,都可以通過這種方法完成。但是如上所述,在分配的記憶體定額被消耗光時,
「排序法」只是將中間結果寫入磁碟,而詞典資訊一直在記憶體中進行維護,隨著處理文件越來越多,詞典裡包含的詞典項越來越多,所以占用記憶體越來越大,導致後期中間結果可用記憶體越來越少。「歸併法」對此做出了改進,即每次將記憶體中資料寫入磁碟時,包括詞典在內的所有中間結果資訊都被寫入磁碟,這樣記憶體所有內容都可以被清空,後續建立索引可以使用全部的定額記憶體。
圖3-14是「歸併法」的示意圖。其整體流程和排序法大致相同,也是分為兩個大的階段,首先在記憶體裡維護中間結果,當記憶體佔滿後,將記憶體資料寫入磁碟臨時檔案,第二階段對臨時檔案進行歸併形成最終索引。
圖3-14 歸併法
儘管整體流程看上去和排序法大致相同,但是在具體實現方式上有較大差異。
首先,「排序法」在記憶體中存放的是詞典資訊和三元組資料,在建立索引過程中,詞典和三元組資料並沒有直接的聯絡,詞典只是為了將單詞對映為單詞id。而「歸併法」則是在記憶體中建立乙個完整的記憶體索引結構,相當於對目前處理的文件子集單獨在記憶體建立起了一整套倒排索引,和最終的索引相比,其結構和形式是相同的,區別只是這個索引只是部分文件的索引而非全部文件的索引。
其次,在將中間結果寫入磁碟臨時檔案時,「歸併法」會將整個記憶體的倒排索引寫入臨時檔案,對於某個單詞的倒排列表,在寫入磁碟檔案時,將詞典項放在列表最前端,之後跟隨相應的倒排列表,這樣依次將單詞和對應的倒排列表寫入磁碟檔案,隨後徹底清空所佔記憶體。而「排序法」如上節所述,只是將三元組資料排序後寫入磁碟臨時檔案,詞典作為乙個對映表一直儲存在記憶體中。
在最後的臨時檔案合併為最終索引過程中,兩者也有差異。「排序法」因為儲存的是有序三元組資訊,所以在合併時,是對同一單詞的三元組依次進行合併;而「歸併法」的臨時檔案則是每個單詞對應的部分倒排列表,所以在合併時針對每個單詞的倒排列表進行合併,形成這個單詞的最終倒排列表。另外,「歸併法」在最後的合併過程中形成最終的詞典資訊。
搜尋引擎索引之如何更新索引
本文節選自 這就是搜尋引擎 核心技術詳解 第三章 動態索引通過在記憶體中維護臨時索引,可以實現對動態文件和實時搜尋的支援。但是伺服器記憶體總是有限的,隨著新加入系統的文件越來越多,臨時索引消耗的記憶體也會隨之增加。當最初分配的記憶體將被使用完時,要考慮將臨時索引的內容更新到磁碟索引中,以釋放記憶體空...
搜尋引擎索引之索引基礎
本文節選自 這就是搜尋引擎 核心技術詳解 第三章 本節通過引入簡單例項,介紹與搜尋引擎索引有關的一些基礎概念,了解這些基礎概念對於後續深入了解索引的工作機制非常重要。3.1.1單詞 文件矩陣 單詞 文件矩陣是表達兩者之間所具有的一種包含關係的概念模型,圖3 1展示了其含義。圖3 1的每列代表乙個文件...
搜尋引擎索引之索引基礎
本文節選自 這就是搜尋引擎 核心技術詳解 第三章 本節通過引入簡單例項,介紹與搜尋引擎索引有關的一些基礎概念,了解這些基礎概念對於後續深入了解索引的工作機制非常重要。3.1.1單詞 文件矩陣 單詞 文件矩陣是表達兩者之間所具有的一種包含關係的概念模型,圖3 1展示了其含義。圖3 1的每列代表乙個文件...