本文節選自《這就是搜尋引擎:核心技術詳解》第三章
動態索引通過在記憶體中維護臨時索引,可以實現對動態文檔和即時搜尋的支援。但是伺服器記憶體總是有限的,隨著新加入系統的文檔越來越多,臨時索引消耗的記憶體也會隨之增加。當最初分配的記憶體將被使用完時,要考慮將臨時索引的內容更新到磁碟索引中,以釋放記憶體空間來容納後續的新進文檔,此時要考慮合理有效索引更新策略。
常用的索引更新策略有四種:完全重建策略,再合并策略,就地更新策略以及混合策略。
3.6.1完全重建策略(CompleteRe-Build)
完全重建策略是一個相當直觀的方法,當新增文檔達到一定數量,將新增文檔和原先的老文檔進行合并,然後利用前述章節提到的建立索引的方式,對所有文檔重建立立索引。新索引建立完成後,老的索引被遺棄釋放,之後對使用者查詢的響應完全由新的索引負責。圖3-16是這種策略的說明。
圖3-16完全重建策略
因為重建索引需要較長時間,在進行索引重建的過程中,記憶體中仍然需要維護老的索引,來對使用者的查詢做出響應,只有當新索引完全建立完成後,才能釋放舊的索引,將使用者查詢響應切換到新索引上。
這種重建策略比較適合小文檔集合,因為完全重建索引的代價較高,但是目前主流商業搜尋引擎一般是採用此種方式來維護索引的更新,這與互連網本身的特性有關。
3.6.2再合并策略(Re-Merge)
有新增文檔進入搜尋系統時,搜尋系統在記憶體維護臨時倒排索引來記錄其資訊,當新增文檔達到一定數量,或者指定大小的記憶體被消耗完,則把臨時索引和老文檔的倒排索引進行合并,以產生新的索引。圖3-17是這種策略的一種圖示。
圖3-17 再合并策略
在實際的搜尋系統中,“再合并策略”按照以下步驟進行索引內容的更新:
當新文檔進入系統,解析文檔,之後更新記憶體中維護的臨時索引,文檔中出現的每個單詞,在其倒排列表末尾追加倒排清單項目,這個臨時索引可稱之為“增量索引”;
一旦“增量索引”將指定的記憶體消耗光,此時需要進行一次索引合并,即將“增量索引”和老的倒排索引內容進行合并。圖3-18是合并過程,這裡需要注意的是:倒排檔案裡的倒排列表存放順序,已經按照索引單詞字典順序由低到高進行了排序,增量索引在遍曆詞典的時候也按照字典序由低到高排列,這樣對老的倒排檔案只需進行一遍掃描,並可順序讀取,減少了檔案操作中比較耗時的磁碟尋道時間,可以有效地增加合并效率。
圖3-18 合并增量索引與倒排索引
在合并過程中,需要依次遍曆“增量索引”和老索引單詞詞典中包含的單詞及其對應的倒排列表,可以用兩個指標分別指向兩套索引中目前需要合并的單詞(參考圖3-18中箭頭所指),按照如下方式進行倒排列表的合并:
考慮“增量索引”的單詞指標指向的單詞,如果這個單詞在詞典序中小於老索引的單詞指標指向的單詞,說明這個單詞在老索引中沒有出現過,則直接將這個單詞對應的倒排列表寫入新索引的倒排檔案中,同時“增量索引”單詞指標後移指向下一個單詞。
如果兩個“單詞指標”指向的單詞相同,說明這個單詞在“增量索引”和老索引中同時出現,則將老索引中這個單詞對應的倒排列表寫入新索引的倒排列表,然後把“增量索引”中這個單詞對應的倒排列表追加到其後,這樣就完成了這個單詞所有倒排列表的合并。圖3-18中箭頭所指單詞是“搜尋”,說明此時合并到了該單詞,因為在老的索引系統和增量索引中都包含這個單詞,所以首先將老索引對應的倒排列表追加到新索引倒排檔案末尾,之後將增量索引中“搜尋”這個單詞對應的倒排列表追加在其後,這樣就完成了這個單詞索引項目的合并。兩個索引的單詞指標都移動到下一個單詞繼續進行合并。
如果某個單詞只在老索引中出現過,即發現老索引的“單詞指標”指向的單詞,其詞典序小於“增量索引”單詞指標指向的單詞,則直接將老索引中對應的倒排列表寫入新索引倒排檔案中。老索引的單詞指標後移指向下一個單詞,繼續進行合并。
當兩個索引的所有單詞都遍曆完成後,新索引建成,此時可以遺棄釋放老索引,使用新索引來響應使用者查詢請求。
同樣的,在按照這個策略進行索引合并的過程中,為了能夠響應使用者查詢,在合并索引期間,需要使用老索引響應使用者查詢請求。
“再合并策略”是效率非常高的一種索引更新策略,主要原因在於:在對老的倒排索引進行遍曆時,因為已經按照索引單詞的詞典序由低到高排好順序,所以可以順序讀取檔案內容,減少了磁碟尋道時間,這是其高效的根本原因。但是這種方法也有其缺點,因為要產生新的倒排索引檔案,所以對於老索引中的很多單詞來說,儘管其倒排列表並未發生任何變化,但是也需要將其從老索引中讀取出來並寫入新索引中,這種對磁碟輸入輸出的消耗是沒有太大必要且非常耗時的。
3.6.3原地更新策略(In-Place)
原地更新策略的基本出發點,可以認為是試圖改進“再合并策略”的缺點。就是說,在索引更新過程中,如果老索引的倒排列表沒有變化,可以不需要讀取這些資訊,而只對那些倒排列表變化的單詞進行處理。甚至希望能更進一步:即使老索引的倒排列表發生變化,是否可以只在其末尾進行追加操作,而不需要讀取原先的倒排列表並重寫到磁碟另外一個位置? 如果能夠達到這個目標,明顯可以大量減少磁碟讀寫操作,提升系統執行效率。
為了達到上述目標,原地更新策略在索引合并時,並不產生新的索引檔案,而是直接在原先老的索引檔案裡進行追加操作(參考圖3-19),將增量索引裡單詞的倒排清單項目追加到老索引相應位置的末尾,這樣就可達到上述目標,即只更新增量索引裡出現的單詞相關資訊,其它單詞相關資訊不做變動。
圖3-19 原地更新策略
但是這裡存在一個問題:對於倒排檔案中的兩個相鄰單詞,為了在查詢時加快讀取速度,其倒排列表一般是順序序列儲存的,這導致沒有空餘位置用來追加新資訊。為了能夠支援追加操作,“原地更新策略”在初始建立的索引中,會在每個單詞的倒排列表末尾預留出一定的磁碟空間,這樣,在進行索引合并時,可以將增量索引追加到預留空間中。
圖3-20是這種合并策略的一個說明。在圖中,老索引中每個單詞的倒排列表末尾都預留出空餘磁碟空間,以作為資訊追加時的儲存地區。在對“新增索引”進行合并時,按照詞典序,依次遍曆“新增索引”中包含的單詞,並對新增倒排列表的大小和老索引中相應預留空間大小進行比較,如果預留空間足夠大,則將新增列表追加到老索引即可,如果預留空間不足以容納新增倒排列表,那麼此時需要在磁碟中找到一塊完整的連續儲存區,這個儲存區足以容納這個單詞的倒排列表,之後將老索引中的倒排列表讀出並寫入新的磁碟位置,並將“增量索引”對應的倒排列表追加到其之後,這樣就完成了一次倒排列表的“遷移”工作。
圖3-20原地更新策略索引合并
在圖3-20所示的例子中,可以看出,單詞“技術”和“引擎”在老索引中的預留空間足夠大,所以對“增量索引”只需做追加寫入即可,但是對於單詞“搜尋”來說,其預留空間不足以容納新增倒排列表,所以這個單詞的倒排列表需要遷移到磁碟另外一個連續儲存區中。
“原地更新策略”的出發點很好,但是實驗資料證明其索引更新效率比“再合并策略”要低,主要是出於以下兩個原因:
在這種方法中,對倒排列表進行“遷移”是比較常見的操作,為了能夠進行快速遷移,需要找到足夠大的磁碟連續儲存區,所以這個策略需要對磁碟可用空間進行維護和管理,而這種維護和尋找成本非常高,這成為該方法效率的一個瓶頸。
對於倒排檔案中的相鄰索引單詞,其倒排列表順序一般是按照相鄰單詞的詞典序儲存的,但是由於“原地更新策略”對單詞的倒排列表做資料移轉,某些單詞及其對應倒排列表會從老索引中移出,這樣就破壞了這種單詞連續性,導致在進行索引合并時不能順序進行讀取,必須維護一個單詞到其倒排檔案相應位置的映射表,而這樣做,一方面降低了磁碟讀取速度,另外一方面需要大量的記憶體來儲存這種映射資訊。
3.6.4混合策略(Hybrid)
混合策略的出發點是能夠結合不同索引更新策略的長處,將不同的索引更新策略混合,以形成更高效的方法。
混合策略一般會將單詞根據其不同性質進行分類,不同類別的單詞,對其索引採取不同的索引更新策略。常見的做法是根據單詞的倒排列表長度進行區分,因為有些單詞經常在不同文檔中出現,所以其對應的倒排列表較長,而有些單詞很少見,則其倒排列表就較短。根據這一性質將單詞劃分為“長倒排列表單詞”和“短倒排列表單詞”。“長倒排列表單詞”採取“原地更新策略”,而“短倒排列表單詞”則採取“再合并策略”。
之所以這樣做,是由於“原地更新策略”更適合“長倒排列表單詞”,因為這種策略能夠節省磁碟讀寫次數,而“長倒排列表單詞”的讀寫開銷明顯要比“短倒排列表單詞”大很多,所以如果採用“原地更新策略”,效果體現得比較顯著。而大量“短倒排列表單詞”讀寫開銷相對而言不算太大,所以利用“再合并策略”來處理,則其順序讀寫優勢也能被充分利用。