分佈式搜索引擎設計

提示:本文較長,看完須要時間,若是你想看彩色重點標記版,請移步微信地址:這裏nginx

1.前言


分佈式,高可用,和機器學習同樣,最近幾年被說起得最多的名詞,聽名字多牛逼,來,咱們一步一步來擊破前兩個名詞,今天咱們首先來講說分佈式。redis

我我的感覺啊,分佈式和高可用是隨着最近這些年阿里的雙11活動火起來的,放眼全球,好像沒有哪一個公司的系統會在瞬間承接這麼大的流量,而且仍是絕對不能出錯的交易流量,因此阿里確實積累了全球最豐富的高可用和分佈式的經驗,再加上各類技術大會一分享,這兩個詞就變成一個互聯網公司技術系統的標配了。算法

可能不少人並非很理解分佈式和高可用,咱們簡單來講一下。sql

  • 分佈式的理論在上個世紀就已經比較成熟了,只是一直沒有實際可用的環境來驗證,直到google的出現,分佈式就是單機已經沒法處理了,把服務拆分紅多個服務,組成一個分佈式的服務來完成以前單個服務的功能。數據庫

  • 高可用實際上是一直在IT行業中存在的,最典型的就是異地災備系統了,這就是一個標準的高可用系統。編程

好,前面的概念說完了,進入主題,如何來設計一個分佈式系統呢?本篇沒有高深的理論,僅會使用幾個計算機編程的最基本概念,來一步一步闡述如何設計一個分佈式系統,咱們仍是會以搜索引擎爲例,畢竟搜索服務是一個標準的分佈式場景。服務器

請耐心讀完本篇,不要被前面的東西驚到,請耐心微信

2.代碼的底層是什麼


不是說分佈式麼,怎麼說到代碼底層了,別急,慢慢來。網絡

代碼是由數據結構算法組成的,是用來處理數據的,而數據結構和算法組合起來是什麼?是函數,因此,代碼就是函數,並且有函數式編程,而且從數學上也證實了純函數式編程也能完成全部的算法。session

代碼作的事情就是給一個數據做爲函數的輸入,而後經過函數,給出一個數據做爲輸出,在這個過程當中可能須要存儲一些數據到外部存儲器中供之後使用

3.服務是什麼


由多個代碼片斷(函數)組合起來,在加上一個外部的存儲數據,就造成了一個服務,若是一個代碼片斷(函數)能夠抽象成y=f(x),那麼其實服務咱們抽象成這樣子y= f(g(h(u(x))),也就是各個代碼片斷(函數)的組合。

全部的服務,最後均可以抽象成下面的樣子

圖片描述

右邊那個不是表明數據庫,是表明外部的存儲數據,你能夠仔細想想,是否是你寫的全部的服務器端程序,或者全部的程序,均可以抽象成上面這個圖的樣子?若是不行,很差意思,那是你想象力不夠。

好,咱們以搜索引擎爲例來講一下,上面的y= f(g(h(u(x)))對應出來之後

  • 若是是數據檢索過程的話,x就是輸入的query,y就是輸出的結果集,u函數表示query分析,簡單說也就是分詞啦,h就是倒排索引檢索,g就是結果集求交集並集,f就是排序。

  • 若是是數據更新過程的話,x就是新增的那個文檔,y就是更新是否成功,u函數就是分詞,h就是更新內存倒排,g就是寫磁盤。

因此說,一個服務,一定能夠拆分紅一個一個子服務,子服務還能夠繼續拆,拆到最後一定變成一個函數,而若是把一個或者一組函數拆出去單獨變成一個服務的話,那這個服務就變成一個分佈式的服務了,用時下比較流行的說法就是微服務。

4.爲何要分佈式


爲何要分佈式,也就是什麼狀況下須要分佈式。

首先咱們要搞清楚兩個概念,分佈式和集羣

好比上面說的搜索服務,咱們是一個單機版的搜索引擎,性能不錯,每次返回數據的時間大約在10ms左右,可是因爲請求量很是巨大,QPS單機已經扛不住了,因而咱們把這個單機版的變成一個像下圖這樣的結構,前面用一個nginx作負載均衡,後面掛N多搜索服務,這不叫分佈式,這是集羣。

圖片描述

只有當咱們發現因爲排序算法變複雜了,或者數據量增多了,每次返回數據的時間由10ms變成500ms了,這個時候只能把服務給拆成多個服務才能知足業務需求了,這才叫分佈式。

因此說,通常狀況下,集羣就已經夠用了,須要進行分佈式的服務其實並很少,並且集羣基本上都是高可用的,風險也小,因此除非無可奈何,沒有必要爲了分佈式而分佈式,這叫過分設計,單機其實挺好的,簡單易維護,抗造高可用。

5.如何進行分佈式設計


好,假如咱們的搜索引擎數據量和請求時間都已經達到極限了,必需要進行分佈式了,如何來作呢?通常一個服務要進行分佈式的設計,會從兩個方面進行。

  • 一是服務的功能分佈式,對應到代碼也就是把一個1000行的函數變成兩個500行的函數。

  • 二是數據的分佈式,就是數據已經太多了,單節點已經放不下了,或者單節點處理這麼多數據能力已經不夠了,那也得進行分佈式,數據分佈式其實你們見得多了,分庫分表就是一種數據分佈式。

正好,搜索引擎的分佈式這兩個方面均可以涉及到,檢索的時候須要對功能進行分佈式,而索引自己須要對數據進行分佈式,咱們一個一個來講。

6.服務功能的分佈式拆分


一個標準的檢索過程大體分紅如下幾個部分:query分析,數據檢索,結果集合並,排序。以下圖表示的這樣

圖片描述

若是咱們直接把這個服務分紅上面四個單獨的服務,完成服務的分佈式,行不行呢?能夠,但並很差,由於設計一個分佈式的服務仍是有一些東西須要考慮的,對於分佈式的服務,通常須要注意如下三個關鍵點。

6.1 儘可能減小網絡開銷


一個分佈式服務,應該儘可能的減小網絡通信上的開銷,分佈式的初衷是將計算能力分佈到多個節點上而提升總體的響應時間,若是在網絡通信上花費了不少的開銷,反而會使響應時間增加而違背了設計的初衷。

上面幾個模塊中,數據檢索,結果集合並,排序這幾個模塊的數據流都是搜索的結果集,通常都很是巨大,這麼大量的數據在網絡間傳來傳去,速度能快就出鬼了,因此拆分的話,也是將query分析拆出去變成一個單獨的服務,拆出去之後,它知足網絡開銷少這個條件,他的輸入就是用戶的query(好比姚明有多高),也就是一個url吧,輸出是一個改寫好的query(姚明/身高),也就是個url吧,網絡傳遞上開銷很小。

6.2 各個子服務應該是無狀態的


若是是分佈式服務中的一個子服務,它應該儘可能設計成無狀態的,放到編程語言上來講就是說這個服務應該是一個純函數的服務,一個輸入只會有一個輸出,不會由於不一樣的狀態產生不一樣的輸出,這樣主要是爲了方便調用,調用服務的時候不須要去考慮狀態。若是必定須要狀態的話(好比用戶的會話狀態session),對於分佈式服務,通常會將狀態單獨存儲在某個位置(好比redis中),須要的時候去取就好了,服務自己並不保存這個狀態。

咱們的query分析服務完美知足這個條件,他是無狀態的,query分析是個純算法的東西什麼輸入就有一個什麼輸出。

6.3 每一個子服務都應該是可橫向擴展的


雖然上面咱們說了集羣和分佈式的區別,可是分佈式的系統通常都得能知足集羣的部署,若是每一個子服務能知足無狀態的話,那麼橫向擴展就沒什麼問題了,由於無狀態,因此對外來講每臺機器都是同樣的,調用誰結果都同樣,天然就能橫向擴展了。

咱們再看咱們的query分析服務和主搜索服務,兩個都是無狀態的,都能橫向擴展,那麼這個搜索引擎經過一系列的功能拆解之後,將會變成這個樣子

圖片描述

若是你設計一個分佈式系統按照上面三條準則進行模塊的拆分的話,那麼至少方向上不會有太大的問題,系統耦合性也會比較低,若是每一個子系統都能作到以上三點的話,基本上會是一個比較好的分佈式系統。

這裏再說一下,對於搜索服務來講,我這裏只是爲了闡述功能的分佈式設計,把query分析給拆出去了,若是咱們的query分析就是個分詞的話,也徹底沒有必要拆分了,但若是是一個複雜的天然語言處理算法的話,仍是能夠拆出去的。另外,把排序單獨拆開做爲一個服務的,確實很少見,或者說我沒見過吧,因此搜索引擎的功能拆解,基本上就是這樣了,再拆就影響性能了。

如今不少人開口就是各類技術棧,各類開源組件,各類搭配組成一個看似牛逼的分佈式系統,請問,對於RestfulAPI+ZooKeeper+HDFS+KafkaMQ+Nginx+Dubbo+Mysql+Docker+...這種架構的分佈式系統,無論他是幹什麼的系統,你見了除了懵逼難道還有第二種表情麼?你的系統就那麼牛逼,須要把這些技術全都用上???

因此,一個好的分佈式系統,一樣適用於一個系統架構吧,關鍵點不在你用了多少新技術,而在於對模塊的拆分對不對。

7. 數據的分佈式拆分


服務已經進行了拆分,已經算是一個分佈式系統了,可是隨着搜索引擎的數據愈來愈多,單機的性能也會達到一個極限,好比五億條數據,單機基本上比較難抗住了(若是你硬要上一臺1T內存,100T硬盤,64CPU的機器,那是你任性),因此這時候的分佈式就是須要進行數據層面的分佈式設計了。

7.1 搜索引擎索引分片


對服務的分佈式拆分仍是比較容易的,把服務和數據一鍋端到另外的節點就好了,但你要對數據進行分佈式拆分,就沒那麼容易了,這就須要具體場景具體分析了。

先簡單介紹一下搜索引擎的數據拆分,對於搜索引擎來講,外部數據比較簡單,就是一條一條的文檔,數據拆分相對仍是比較簡單的,通常咱們能夠選一個字段作hash,將數據hash到各個節點中,也就是常常聽到的索引分片了,只要字段選得好,hash函數也選的好,基本上每一個節點的數據是比較平均的,那麼這麼一拆解下來,這個搜索引擎就變成下圖這個樣子了,紅框中每個節點都有一部分的索引內容,把每部分的內容合併起來就是整個結果集了。

圖片描述

上面的拆分有個致命的弱點,知足不了咱們上面說的每一個子服務都應該是可橫向擴展的,由於在咱們加分片機器的時候,咱們沒法保證相同分片機器的數據一致性,就像下圖,如何保證A1和A2,A3數據的一致性。

這就是數據的拆分和服務的拆分的本質區別,要考慮的地方也不同,服務說白了就是一堆函數,不會變化,拆到哪裏都是同樣的,橫向擴展徹底沒問題,而數據是會變的,如何拆分數據不是數據分佈式的重點,數據拆分須要解決的最重要的問題就是:如何保證各個相同節點的數據強一致性。這也是全部的分佈式系統須要的最重要的問題,纔會爲了這個出現那麼多一致性的理論。

7.2 Log


看上去很簡單吧,索引分片技術直觀有效,數據庫的分庫分表這種拆分也很簡單有效,但並非每一個服務的數據拆分均可以這麼簡單的,因此如何分片不是我要說的重點,若是不是那麼能直觀分片的數據如何進行分佈式的拆分?

咱們先不說那麼多一致性理論,好比Paxos這種高大上的算法。咱們從最最基礎的提及。

若是你看到這了尚未關閉這篇文章,那麼我告訴你,前面全部的都是開胃菜,咱們終於見到了這篇文章的主角了,那就是Log。

咱們再回到這張圖

圖片描述
右邊的外部數據有兩種狀況

  • 徹底是靜態數據,從始至終不會變化,那麼這種狀況隨便怎麼拆都好說

  • 徹底是動態數據,會常常變化,那麼這種狀況就要請主角出場了

7.2.1 Log是什麼

咱們說的Log不是咱們服務的日誌,日誌是用來記錄服務的一些信息的,主要用來進行錯誤排查的,是給人看的,而咱們這裏說的Log是給機器看的。

Log是什麼,一條Log是一個帶時間戳的消息,這裏的消息能夠任何東西,好比數據變化啊,某個事件啊,甚至是某個函數操做,任何東西均可以是Log的消息。你們熟悉的數據庫的binlog,就是一種Log形式,記錄的是數據的變化。

下圖就是一個標準的Log,首先他帶有時間戳(或者表示時間序列的ID),而後是Log的消息自己,全部Log是按時間一條一條排序好的,結構很是簡單,雖然Log簡單,可是Log記錄了整個系統最關鍵的東西,那就是這個系統在某個時刻幹了什麼。

圖片描述

7.2.2 Log有什麼用

Log最重要的功能就是重放。A節點進行了一系列的操做,產生了5條Log,輸出了一個結果R,那麼將這5條Log輸入到B節點,B節點最後也將輸出結果R,這就是重放。

由於咱們的顯示世界是個時域系統,任何狀態都是由一個事件和一個時間產生出來的,而Log這種簡單的數據結構剛好完美的記錄了時間和事件,那麼任何狀態均可以用Log還原出來。

7.2.3 Log怎麼用

Log雖然簡單而又異常強大,但不少時候咱們只須要使用不多的功能就能完成很重要的事情,好比咱們的搜索引擎,Log只須要記錄一個序號,一個操做類型,一條消息就好了,像下面的結構

1 . 更新數據 . id=MMM,name=XXX,content=XXX
2 . 更新數據 . id=NNN,name=YYY,content=ZZZ
3 . 新建字段 . fieldname=title,fieldtype=string
4 . 刪除數據 . id=MMM

這樣,一樣功能的節點經過Log就能夠保證數據的一致性,不止搜索引擎,MySql數據庫,Redis等的主從同步也是這麼作的,一條簡單的Log就達到了數據一致性的要求。

7.2.4 再把Log展開一點

Log既然這麼牛逼,爲何說來講去還只是個數據複製呢?有別的高大上點的應用麼?

首先,Log記錄了系統在整個時間週期中每次的狀態變動,他是可重放的。

  • 由於Log有上述特性,數據複製過程當中即使從節點掛了,重啓之後根據上次的Log的編號也能重放並同步數據,

其次,咱們能夠把任何結構化的數據(好比數據庫表,搜索索引,KV數據庫)異構成Log這種標準的數據存儲格式,而且最重要的是能夠經過Log這種結構再次重建任何結構化數據。

  • 由於Log有上述特性,因此Log能夠用來做爲系統解耦的中間結構,如今那麼多消息隊列,哪一個系統沒有用消息隊列,你覺得是啥?就是Log啊,Kafka就是個高級的Log服務。

7.3 回到索引分片


好了,扯了一段Log,咱們回到索引分片來,有了Log之後,咱們想怎麼加機器就能怎麼加機器了,反正經過Log能夠保證各個相同節點的數據一致性,上面那個帶問號的圖經過Log就解決了。

圖片描述

8.節點間通信


最後說一說節點間的通信,這不是本篇重點,捎帶說一下,通常不少分佈式系統使用RPC這種遠程調用再加上一種序列化算法來節省帶寬來進行節點間通信,簡單的話,用http也行,這裏就不展開了。

9.部署


最後,咱們畫個分佈式的最終圖,搜索服務拆分紅分佈式了之後就是這樣子了。

圖片描述
注意啊,這是個可用的分佈式搜索引擎,只有咱們第二篇出來了之後才能變成一個高可用的分佈式搜索引擎:)

10.總結


好了,說了這麼多了,已經沒體力了,不知道你看完沒有,這只是第一部分,分佈式的部分,後面還有一篇高可用的,還沒寫呢,歡迎繼續閱讀,若是你以爲還不錯,歡迎轉發:)


最後,歡迎關注個人公衆號,主要聊聊搜索,推薦,廣告技術,還有瞎扯。。文章會在這裏首先發出來:)掃描或者搜索微信號XJJ267或者搜索中文西加加語言就行

圖片描述

相關文章
相關標籤/搜索