搜索引擎如何工做?
信息檢索已經發展的很是成熟了,應該全部人都不陌生。我有幸這幾年接觸過而且實際作過一些搜索引擎開發的工做,特此總結並分享給你們。實際上,一個成熟的搜索引擎是想當複雜的,好比百度的,就分nginx,vui,us,as,bs,da.....等等這些模塊,固然這些簡寫的字母你們也沒必要了解,只要知道它確實複雜就能夠。html
今天我所講的是一個簡化版的搜索引擎,簡化到只涉及到倒排創建和拉取。雖然簡單,可是它是整個搜索引擎的最核心組件。一個最簡單的搜索引擎以下圖所示:nginx
- merger: 接收查詢請求,分詞後請求下游Indexer分別獲取各個indexer的局部TopK文檔,歸攏後排序返回全局類似度最高的TopK文檔。
- indexer:負責倒排拉取,並利用夾角餘弦算法計算類似度,返回TopK結果。夾角餘弦能夠在 http://www.cnblogs.com/haolujun/p/8011776.html 中瞭解。
- index:倒排索引。
倒排索引長成什麼樣子呢?就是圖中標記的那樣,每一個詞後面有一個拉鍊,拉鍊中存放包含該詞的文檔編號,利用這個數據結構能快速的找到包含某一個詞的全部文檔。算法
今天介紹則是搜索引擎的核心中的核心:倒排索引。接下來的全部內容都圍繞着倒排索引展開。後端
如何創建倒排索引
對幾億甚至幾十億幾百億規模的文檔集合創建倒排索引並非一件很輕鬆的事情,它將面對着IO以及CPU計算的雙重瓶頸,須要根據實際狀況找到最優方法,接下來介紹兩種不一樣的創建倒排的方式。數據結構
單遍內存型
內存中維護每一個詞對應的文檔編號列表,當一個詞對應的buffer滿時,把內存中的數據flush到磁盤上,這樣每一個詞對應一個文件。最後按照詞編號由小到大的合併全部文件,獲得最終的倒排索引。併發
- 優勢:使用內存存放倒排索引,並分批flush到磁盤。這樣減小了一些排序操做,速度快。
- 缺點:內存使用量稍大。能夠減小內存buffer大小來下降內存使用量,可是這樣作得缺點就是增長了磁盤隨機寫的次數。想一想,每當一個buffer滿時都會寫對應的文件,而且buffer是隨機的,因此增長了磁盤尋址的開銷。
多路並歸型
步驟以下:運維
- 首先,解析文檔,把<詞,文檔編號,tf>寫入到磁盤文件。
- 而後,對磁盤文件進行外部排序,排序規則:按照詞的字典序從小到大排序,若是詞相同,則按照文檔編號從小到大排序,這樣相同的詞就排到了一塊兒。
- 最後,順序掃描排序後的文件,創建倒排索引。因爲相同的詞緊挨在一塊兒,能夠一個詞一個詞的創建倒排索引。
與內存型相比,這種方式適合在內存小,磁盤大的狀況下進行倒排索引的創建,它的優缺點以下。oop
- 優勢:內存使用量小,沒有磁盤隨機讀寫,基本全是順序讀寫。對於超大規模文檔集合來講,這種方式相對靈活一些(主要是排序靈活)。
- 缺點:屢次歸併排序,比較慢。可是,可使用併發進行歸併排序,這樣可以提升一些速度。
索引切分
考慮到在海量文檔下,倒排索引很是大,單臺機器沒法在內存中裝下所有索引,因此有必要把索引進行切分,使得每個索引服務只對文檔中一部分的內容進行拉取、計算。常見的有兩種可選擇的方式。 性能
按文檔編號切
按照文檔編號把文檔分紅幾個小的集合,對每一個小的文檔集合單獨創建索引。在這種方式創建索引上進行查詢時,merge須要把查詢請求下發到全部的後端indexer服務(由於每一個index都有可能存在包含查詢詞的文檔),indexer服務的計算量比較大。可是它也有一個優勢:每一個詞的倒排拉鍊的長度可控。學習
按term切分
按照詞進行索引劃分,每一個索引只保存若干詞的全部文檔編號。在這種方式創建的索引上進行查詢時,merge能夠根據查詢詞精確的把請求下發到對應的indexer上,減小了後端indexer的計算量。 可是這麼作引入幾個新問題:
- 1:某些高頻詞倒排拉鍊過長,致使這臺indexer計算時間超出可忍受範圍,出現拖後腿現象;
- 2:維護詞表與indexer的對應關係,運維複雜;
- 3:對於熱詞所在的索引,對應的indexer請求量大,計算量大,負載高,須要考慮熱詞打散。
那麼在實際中該如何進行索引切分呢?主要看是什麼類型的查詢、查詢的量、以及文檔集合的規模。
- 對於普通搜索引擎,用戶輸入的是數量較少的查詢詞,按照term切分能夠有效減小查詢時後端indexer的計算量,收益比較大。
- 對於如今流行的拍照搜題,拍攝的圖片中文字通常較多,按照term切分的優點不明顯並且引入了複雜的運維,建議按照文檔編號切分。
- 對於高頻詞出現的文檔,能夠把這些文檔選出來,而後按文檔切分的方式創建索引,這樣不會出現倒排拉鍊過長。總結下來就是:高頻詞按文檔切分,低頻詞按照term切分,檢索的時候,根據查詢詞是不是高頻詞執行不一樣的檢索策略。
- 在不差錢而且文檔規模不大,而且查詢量沒有達到必須優化索引的前提下,儘可能使用運維更簡單的索引:按照文檔編號切分。
增量索引
不少搜索引擎都注重時效性搜索,好比對於時下剛剛發生的某件熱門事件,須要搜索引擎可以第一時間搜索到該熱門事件的頁面,這該如何作到呢?因爲創建一次全量文檔倒排的時間基本都是按天計,若是不設計一些實時增量索引,那麼根本知足不了時效性的檢索。下面介紹增量索引,能夠解決時效性搜索問題。
倒排索引雙buffer設計方案
增量步驟:
- 1:indexer從索引2切換到索引1
- 2:更新索引2
- 3:indexer從索引1切回到索引2
- 4:更新索引1
這樣能夠保證明時的動態更新,可是它的缺點也很明顯:必須使用2倍索引大小的內存,機器成本比較高,實際中更經常使用的是下面一種方案。
增量索引服務+雙buffer方案
全量索引服務用來查詢截止到某一個日期的所有文檔,增量索引服務使用雙buffer設計方案查詢最近一段時間(多是小時級或者分鐘級)內實時更新的文檔內容,而後按期(天天、每週、每個月一次)把最近一段時間更新的文檔追加在全量索引中。這樣作的好處就在於只有少許近期更新文檔的查詢須要使用雙倍內存,機器成本下降。須要注意的一點是,用這種方式創建增量索引時,必須更新全局word的df信息,對於發現的新詞還需爲其添加全局惟一id,這些信息通通要更新到線上正在運行的全量索引服務。
利用Hadoop並行創建倒排索引
對於超大規模的文檔集合,可使用Hadoop創建倒排索引。
- map端讀入每一個文檔並進行解析,生成一個tuple,key爲詞,value爲tf+文檔編號,發送該tuple給下游reduce,這樣相同的詞會分配給同一個reduce。
- reduce保存每一個tuple,並在結束時對每一個詞倒排按照文檔編號由小到大排序,並保存到對應的文件中。
- 有多少個reduce就會有多少個索引文件,最後彙總這些文件獲得最終的倒排索引。
實際工做中,爲了增長map端讀數據性能,並非每一個文檔存放成單獨一個文件,而是先把文檔序列化成文件中的一行,這樣每一個文件能夠存放多個文檔內容,這就減小了小文件數,增長了map端的吞吐量。
總結
倒排的創建還有查詢涉及到的技術內容遠遠不止於此,在這裏能夠推薦兩本書給你們,有興趣的小夥伴能夠進行深刻的學習,共同進步。 -《深刻搜索引擎-海量信息的壓縮、索引和查詢》 Lan H.Witten, Alistair Moffat, Timothy C.Bell著,梁斌譯。 -《信息檢索導論》 Christopher D.Manning, Prabhakar Raghavan, Hinrich Schutze著,王斌譯。