在淘寶搜索系統中中,搜索結果頁的緩存(Cache)是對搜索“效率”貢獻最大的設計。由於緩存中的搜索結果頁都是前人查詢的結果,因此用戶的查詢請求如果在緩存中命中(和前人的查詢相同),則查詢系統直接把緩存中存放的搜索結果頁返回給用戶。
用戶在使用淘寶搜索引擎進行檢索時,查詢詞可能千差萬別。但是如果從大量用戶的查詢統計上看,總會有一些詞匯經常被查詢,有些詞匯卻很少被查詢。
(1)前20%的查詢詞的查詢次數約占了總查詢次數的80%。
(2)查詢具有穩定性,查過的詞很可能在不久的將來還會被查詢。
搜索結果緩存的實現方法和操作系統中提到的LRU算法基本一致,我們大致回顧一下LRU緩存置換算法。
熟悉網頁庫設計的讀者都應該知道,對搜索結果頁的緩存庫必須能夠支持隨機訪問,這一點很重要。如何支持這種隨機訪問其內部原理和數據庫設計很相似,這里不再展開,有興趣的讀者可以參考B+樹等這類能夠支持隨機訪問的索引方式。
有了搜索結果頁緩存的設計,淘寶搜索引擎查詢層就能夠大大降低重復的計算量,提高同時響應用戶檢索請求的能力。具有搜索結果頁緩存功能支持的查詢系統如圖1所示。
增加了緩存功能后,淘寶查詢系統可以較少執行實際的查詢計算,而采用重用緩存中保存的歷史相同的查詢結果網頁的方法來大大提高查詢效率。目前的技術能夠達到在緩存中命中99%的查詢,因此用戶實際的查詢絕大多數情況都是取自緩存的搜索結果頁,這就是搜索引擎為什么能夠如此快速地返回查詢結果的一個重要原因。
也許是由於搜索結果頁緩存的出色設計,在“效率”和“效果”之間的競爭上,“效率”占據了優勢。因此近年來,淘寶查詢系統的研究方向主要在“效果”上,而“效果”的追求還需要推測用戶的查詢意圖。如果能正確地推測出用戶的查詢意圖,那么對效果的改善可以說是非常有利的。接下來我們就詳細講解淘寶搜索引擎的緩存機制和原理。
緩存(Cache)是目前所有搜索引擎都會采用的技術。所謂緩存,就是在高速內存硬件設備內開辟一塊數據存儲區,用來容納常見的用戶查詢及搜索結果(或者索引數據及搜索的中間結果),同時采取一定的管理策略來維護存儲區內的數據。當搜索引擎接收到用戶查詢請求時,首先在緩存系統里查找,如果能夠找到則直接返回搜索結果,否則采取正常的搜索流程來返回搜索結果。
為何搜索引擎要引入緩存機制?一則使用緩存系統能夠加快用戶查詢響應的速度;另外還可以有效地減少搜索引擎后台計算量,節省計算資源。
對於一個正常的搜索流程,比如用戶輸入查詢請求“夏季 連衣裙”,淘寶搜索引擎需要分別將存儲在磁盤上的兩個單詞的倒排索引讀入內存,之后進行解壓縮,然后求兩個單詞對應倒排列表的交集,找到所有包含兩個單詞的文檔集合,根據排序算法來對每個文檔的相關性進行打分,按照相關度輸出相關度最高的搜索結果。
以上這個流程涉及了磁盤讀/寫、內存運算等一系列操作,相對比較耗費時間和計算資源。如果將本次搜索結果存儲在緩存中,下次遇到相同的查詢請求,則可以直 接將搜索結果返回,不需要經過上述的復雜流程進行計算。緩存一般用最快的內存設備進行存儲,所以響應速度非常快,同時也省略了相當多的磁盤讀取和計算步 驟,有效地節省了計算資源。
以上搜索加速行為能夠成立,其實隱含了一個假設,即:相同的用戶查詢會反復出現。只有這個假設成立,才能夠利用以上措施來加快搜索速度,但是問題是這個假設成立嗎?
這涉及用戶查詢分布本身具有的特點。我們先看下用戶搜索請求行為有哪些特點。目前有很多研究集中在分析用戶搜索行為,通過對搜索日志的分析,可以得出如下結論。
1、至少63.5%的淘寶搜索引擎用戶只看搜索結果第1頁的內容;大約11.7%的淘寶搜索引擎用戶會翻看搜索結果第2頁內容;至少79%的淘寶搜索引擎用戶只查看搜索結果前3頁的內容。
2、用戶發出的查詢請求分布符合逆Power-Law規則,即少數查詢占了查詢總數的相當比例,而大多數查詢出現次數非常少。在十億規模的搜索日志記錄中,63.7%的用戶查詢只出現過一次,而熱門查詢占搜索請求總數的比例非常高,最熱門的25個用戶搜索請求占了用戶查詢請求總數的1.2%一1.5%;同時,用戶查詢有很大比例的重復性,大約有30%-40%的用戶查詢是重復查詢。
3、用戶查詢請求具備時間局部性,即大多數重復的用戶查詢會在較短的間隔時間被再次重復訪問。
通過上面的調查結論,可以看出在一定的時間間隔內,發送到淘寶搜索引擎的用戶查詢有相當比例的重復性,而緩存機制之所以能夠運用在淘寶搜索引擎里來加快系統響應速度,與這一點是密不可分的。
一、淘寶搜索引擎緩存系統架構
圖2是淘寶搜索引擎緩存系統架構示意圖,當淘寶搜索引擎接收到用戶查詢的時候,會首先在緩存系統查找,看緩存內是否包含用戶查詢的搜索結果,如果發現緩存已經存儲了相同查詢的搜索結果,則從緩存內讀出結果展現給用戶;如果緩存內沒有找到相同的用戶查詢,則將用戶查詢按照常規處理方式交由淘寶搜索引擎返回結果,並將這條用戶查詢的搜索結果及中間數據根據一定策略調入緩存中,這樣下次遇到同樣的查詢可以直接在緩存中讀取,以加快用戶響應速度並減少淘寶搜索引擎系統的計算負載。
淘寶緩存系統包含兩個部分,即緩存存儲區及緩存管理策略。緩存存儲區是高速內存中的一種數據結構,可以存放某個查詢對應的搜索結果,也可以存放搜索中間結果,比如一個查詢單詞的倒排列表。
緩存管理策略又包含兩個子系統,即緩存淘汰策略和緩存更新策略。
之所以需要緩存淘汰策略,是因為不論給緩存分配多大空間,當系統運行到一定程度,很可能緩存已經滿了,當有新的需要緩存的內容要進入緩存時,需要根據一定的策略,從緩存中剔除一部分優先級別較低的緩存內容,以騰出空間供后續內容放入緩存存儲區,如何選擇替換項目是緩存淘汰策略需要考慮的問題。
另外,使用緩存系統是有一定風險存在的,即緩存內容和索引內容不一致問題。如果淘寶搜索引擎索引的文檔集合是靜態文檔,這個問題是不存在的,因為既然文檔集合沒有發生任何變化,只要搜索引擎的排序算法不更改,那么針對固定的用戶查詢,其對應的搜索結果是固定不變的,所以緩存里面的內容永不過期。
但是在一般應用場景中,淘寶搜索引擎要處理的文檔集合是動態變化的,可能會面臨新加入的文檔,也可能會刪除舊的文檔或者舊的文檔內容發生了變化。當索引己經反映了這種變化,而緩存數據沒有隨着索引做出相應的變化,那么就會發生緩存內容和索引內容不一致的問題。緩存更新策略就是用來維持兩者一致性的。
對淘寶搜索引緩存系統來說,一個優秀的緩存系統,希望能夠在以下兒個方面表現出色。
1、最大化緩存命中率
所謂緩存的命中率,就是說一段時間內所有用戶發出的查詢中,有多大比例的查詢對應的搜索結果是從緩存中獲得的。這個比例越高,說明緩存管理策略越成功,就有效地節省了淘寶搜索引擎的計算成本。具體而言,不同的緩存淘汰策略就是采用不同算法來獲得盡可能高的命中率。
2、緩存內容與索引內容保持一致性
好的緩存管理策略應該避免出現緩存內容與索引內容不一致的狀況,因為這種不一致會影響用戶的搜索體驗,所以緩存系統需要有優秀的緩存更新策略來達到這個目的。
二、緩存對象
對於搜索引擎緩存,在存儲區內存放的數據對象並不是唯一的,可以是搜索結果,也可以是某個查詢詞匯對應的倒排列表,或者是一些搜索的中間結果。
最常見的緩存對象類型是用戶查詢請求所對應的搜索結果信息,比如寶貝的標題、寶貝URL等。圖3給出了將搜索結果作為緩存內容的示例,緩存里保存了“連衣裙" ,“運動鞋”等用戶查詢,以及其對應的搜索結果。如果此時有另外一個用戶愉入“連衣裙”作為查詢,則淘寶搜索引擎首先在緩存里面查找,發現己經存在這個用戶查詢項,則直接提取原先的搜索結果作為輸出返回給用戶。
另外一種比較常見的存儲對象類型是查詢詞匯對應的倒排列表(Posting List)。圖4是以單詞倒排列表作為緩存內容的一個示例圖,從圖中可以看出,以搜索結果作為緩存內容的情況下,用戶查詢即使包含多個單詞,也是作為一個整體存儲在緩存槽里的;而以單詞倒排列表作為緩存內容的方式,其存儲粒度相對會小些,是以用戶查詢的分詞結果存儲在緩存槽里的。比如“夏季 連衣裙”這個用戶查詢,在搜索結果作為緩存內容情形下占用一項緩存槽,而在緩存倒排列表方式下會占用兩個緩存槽,“夏季”和“連衣裙”各自占用一個存儲位置。
這兩種不同的緩存存儲內容各自有其優缺點,對於搜索結果型緩存來說,其用戶查詢響應速度非常快,因為只需要進行查找運算即可返回結果,但是其粒度比較粗,比如在如圖3所示的例子中,如果此時用戶輸入查詢“連衣裙 韓版”,則淘寶搜索引擎會發現緩存里面並不存在這個查詢,只能按照正常搜索流程,去調用索引數據並進行網頁排序等運算。但是倒排列表型緩存因為粒度較小,會發現“連衣裙”這個查詢詞匯已經在緩存中了,此時只需要從存儲在硬盤的倒排索引中讀取“韓版”這個詞匯的倒排列表數據,然后進行排序運算即可返回結果。由這個例子可以看出,倒排列表型緩存粒度小,所以命中率高,但是因為保存的只是倒排列表這種中間數據,所以仍然需要進行后續的計算才能返回最終結果,在用戶響應效率方面慢於搜索結果型緩存。而搜索結果型緩存粒度大,如果在緩存內命中用戶查詢,則很快給出最終結果,但是命中率要低於倒排列表型緩存。
另外,搜索結果型緩存因為征個搜索結果的大小是可以預估的(一般取前列的K個搜索結果),所以管理起來比較簡單,而倒排列表型緩存需要緩存某個單詞的倒排列表,而不同單詞的倒排列表大小差異很大,如果遇到一個非常大的倒排列表,可能會對目前的緩存空間造成較大影響,甚至被迫移出經常使用的用戶查詢緩存項,所以如何管理倒排列表型緩存存儲區相對而言比較復雜。
以上兩種緩存對象是比較常見的緩存類型,還有一種不太經常使用的方式,即保留兩個經常搭配出現單詞的倒排列表的交集,以這種中間結果形式作為緩存內容。因為用戶查詢有很大比例是由2個或者3個單詞組成的,對於多詞構成的用戶查詢,搜索引擎在從硬盤讀出每個詞匯的倒排列表后,需要進行文檔隊列的交集運算。而如果能夠事先將這些交集運算的計算結果緩存起來,則可以避免后續的交集運算,提高搜索系統返回結果的速度。但是這種詞匯組合的數據量非常大,都放置到內存中往往很困難,所以一般這種中間結果會存儲在磁盤上。這種類型的緩存不能單獨使用,但是可以作為多級緩存中的一個緩存級別存在,對其他類型的緩存起到補充作用。
三、緩存結構
搜索引擎緩存的結構設計可以有多種選擇,最常見的是單級緩存,也可以設計為二級甚至是三級緩存結構。
單級緩存是一種最常見也最簡單直接的緩存結構,緩存系統中只包含一個單一緩存,配以緩存管理策略構成了整個緩存系統。圖5左方和右方分別是搜索結果型和倒排列表型單級緩存示意圖。
盡管單級緩存只包含一級緩存,但是對於不同緩存對象類型來說,其內部處理流程有一定差異。搜索結果型緩存首先在緩存中查找是否包含用戶查詢,如果存在則直接將搜索結果返回,否則對用戶查詢進行處理,由搜索系統返回搜索結果並加入緩存中,之后將搜索結果返回給用戶。對於倒排列表型緩存,其處理步驟正好相反,查詢處理階段首先將用戶查詢分詞,之后在緩存中查找這些單詞對應的倒排列表,如果所有單詞的倒排列表都在緩存中,則由查詢處理模塊根據單詞倒排列表對搜索結果進行排序,並將搜索結果返回給用戶。如果發現某些單詞的倒排列表不在緩存中,會首先從磁盤讀入單詞對應的倒排列表,將其放入緩存,之后講行查詢處理步驟。
二級緩存結構由兩級緩存串聯構成,第1級緩存是搜索結果型緩存,第2級緩存是倒排列表型緩存,圖6是二級緩存示意圖。當系統接收到用戶查詢時,首先在一級緩存查找,如果找到相同查詢請求,則返回搜索結果;如果在一級緩存沒有找到完全相同的查詢,則轉向二級緩存查找構成查詢的各個單詞的倒排列表,如果某些單詞的倒排列表沒有在二級緩存中找到,則從磁盤讀取對應的倒排列表,進入二級緩存;之后,對所有單詞的倒排列表進行求交集運算並根據排序算法排序輸出最相關的搜索結果,將相應的用戶查詢和搜索結果放入一級緩存進行存儲,並返回最終結果給用戶。采用兩級緩存結構的出發點在於能夠融合搜索結果型緩存的用戶快速響應速度和倒排列表型緩存的命中率高這兩個優點。
四、緩存淘汰策略(Evict Policy)
緩存淘汰策略是任何緩存必須配備的管理策略。因為緩存的大小總是有限的,當緩存已滿的時候,如果有新的緩存項需要加入,那么必須從已有的緩存項中剔除相對最不重要的項目,而不同的緩存淘汰策略就是根據不同的算法來衡量項目的重要性,並剔除掉最不重要項目占用的內存空間。緩存淘汰策略方法眾多,從宏觀角度,可以將其分為動態策略和靜態動態混合策略。
4.1 動態策略
動態策略的緩存數據完全來自於在線用戶查詢請求,這種緩存策略的基本思路是:對緩存項保留一個權重值,這個權重值根據查詢命中情況動態調整,當緩存已滿的情況出現時,優先淘汰權重值最低的那個緩存項,通過這種方式來騰出空間。比較常見的動態策略包括:LRU策略、LandLord策略及SLRU等改進策略。
LRU策略:最近最少使用策略(Least Recently Used)
LRU淘汰策略是計算機領域使用非常廣泛的緩存替換算法,在操作系統內存管理和Web頁面緩存等領域也發揮着重要作用。LRU策略的基本思想是:當緩存已滿時,將在設定的時間范圍內使用次數最少的項目剔除出緩存,也就是將在設定時間段范圍內最少訪問的用戶查詢剔除掉。
在實際系統中,往往為每個緩存項設置一個計數器,將命中查詢的計數器清零,與此同時,其他查詢計數器加1。如果緩存己滿,則將計數器數值最大的項目剔除出緩存。
LandLord策略
LandLord策略是一種加權緩存策略(Weighted Cache)。其基本計算流程如下:當一個緩存項插入緩存的時候,會根據緩存項能夠獲得收益和緩存項所占內存大小的比率設定一個過期值 (Deadline),可以將這個比率理解為系統緩存這個項目的性價比。如果緩存已滿,需要剔除項目的時候,選擇過期值最小的項目進行淘汰,即淘汰性價比 最低的項目。同時,其他未被淘汰的項目對應的過期值都減去被淘汰項目的過期值,如果一個查詢請求在緩存中命中時,會相應地將其過期值根據一定策略調大。
SLRU策略:大小自適應LRU (Size-adjusted LRU)
SLRU策略是對LRU方法的改進。緩存被分為兩個部分:非保護區域和保護區域。每個區域的緩存項都按照最近使用頻度由高到低排序,頻率高端叫做MRU,低端的叫做LRU。如果某個查詢沒有在緩存中找到,那么將這個查詢放入非保護區域的MRU端;如果某個查詢在緩存中命中,則把這個查詢記錄放到保護區的MRU端;如果保護區已滿,則把記錄從保護區放入非保護區的MRU,這樣保護區的記錄最少要被訪問兩次。淘汰機制是將非保護區的LRU端緩存項淘汰。
4.2 混合策略
動態策略的緩存數據完全來自於在線用戶查詢請求,混合策略與此不同,其緩存數據一方面來自於在線用戶查詢,一方面來自於搜索日志等歷史數據。目前效果較好的混合策略包括SDC策略和AC策略。圖7是這種策略的示意圖。
SDC策略:靜態動態混合緩存策略(Static and Dynamic Caching)
SDC策略是一種混合緩存策略,SDC將緩存切割為兩個部分,一個靜態緩存與一個動態緩存。所謂靜態緩存,即緩存內容是事先根據搜索日志統計出的最高頻的那部分查詢請求,在一定時間范圍里是相對不變的;而動態緩存則可以配合使用LRU等其他緩存管理策略,根據用戶查詢請求不斷更換內容。通過同時使用靜態緩存和動態緩存,可以有效增加緩存請求命中率。SDC是目前效果最好的緩存策略之一。
AC策略:准入策略(Admission Control)
准入策略是類似於SDC策略的一種方法。該方法也將緩存分為兩個部分,分別存儲高頻出現的歷史用戶查詢和動態出現的用戶查詢及其對應的搜索結果。與SDC不同之處在於:SDC的靜態緩存所存儲的高頻用戶查詢是完全從過去的搜索日志統計得來的靜態內容,而AC策略則綜合了搜索日志的統計數據、查詢長度等多個判斷因素,以此來預測某個查詢是否會在未來被多次訪問,如果判斷是,則放入高頻用戶查詢緩存。
五、緩存更新策略(Refresh Policy)
如果搜索引擎的索引內容不發生變化,緩存的內容就總是和索引系統保持一致。但是淘寶搜索引擎索引經常更新,如果索引內容發生變化,而緩存內容不隨着索引變動,會導致緩存內容和索引內容的不一致,這種不一致對於用戶的搜索體驗會造成負面影響。緩存更新策略就是通過一定的技術手段盡可能保持緩存內容和索引內容的一致性。
目前很多搜索引擎使用簡單的更新策略,即在搜索引擎比較繁忙的時候不考慮緩存更新問題,而等到搜索引擎請求很少的時候,比如午夜等時間段,將緩存內的內容批量進行更新,使緩存內容保持和索引內容的一致。這種簡單策略適合索引更新不是非常頻繁的應用場景,對於索引更新頻繁的場景,需要相對復雜些的緩存更新策略。
根據緩存內容和索引內容聯系的密切程度,目前的緩存更新策略可以分為兩種:緩存——索引密切耦合策略和緩存——索引非耦合策略。
緩存——索引密切耦合策略在索引和緩存之間增加一種直接的變化通知機制,一旦索引內容發生變化則通知緩存系統,緩存系統根據一定的方法判斷哪些緩存的內容發生了改變,然后將改變的緩存內容進行更新,或者設定緩存項為過期,這樣就可以緊密跟蹤並反映索引變化內容。這種密切耦合策略在實際實現時是非常復雜的,因為頻繁的索引更新導致頻繁的緩存更新,對系統效率及緩存命中率都會有直接影響。圖8是一個緩存——索引密切耦合策略的示意圖。當有新的索引文檔進入淘寶搜索引擎時,系統會對文檔內容進行分析,抽取出文檔中得分較高的索引詞匯,並將這些詞匯及其得分傳遞給失效通知模塊,因為如果緩存中的查詢包含這些索引詞匯的話,很可能該文檔將會使得緩存內容失效,失效通知模塊會評估哪些緩存項需要進行內容更新,如果某項緩存項需要更新,則提取最新的緩存內容更新舊緩存項。
緩存——索引非耦合策略則使用相對簡單的策略,當索引變化時並不隨時通知緩存系統進行內容更新,而是給每個緩存項設定一個過期值(Time To Live),隨着時間流逝,項會逐步過期。通過這種方式可以將緩存項和索引的不一致盡可能減小。淘寶搜索引擎就是采用了用了緩存——索引非禍合策略來維護緩存內容的更新,這就是淘寶搜索系統中下架時間的最根本的來源。
總結
1、使用搜索引引擎緩存技術可以加快用戶響應速度並節省計算資源。
2、緩存系統的目標是最大化緩存命中率和保持緩存內容與索引內容的一致性。
3、緩存存儲對象主要包括網頁搜索結果及查詢詞對應的倒排列表。
4、緩存系統可以有多層級結構。
5、緩存淘汰策略方法眾多,從宏觀角度,可以將其分為動態策略和靜態動態混合策略。