當前位置:
首頁 > 美文 > 搜索之路:Elasticsearch的誕生

搜索之路:Elasticsearch的誕生

碼農翻身劉欣 合天智匯

經過三年的歷練,張大胖已經成為了一個利用Lucene這個著名的開源軟體做搜索的高手,各種細節知識和最佳實踐盡在掌握。

(張大胖的學搜索的歷程參見上一篇文章:《搜索之路》)

隨著互聯網應用的爆炸式增長,搜索變成了網站的一個常見需求,各個網站都想搜索產品,搜索帖子,搜索服務......張大胖的「業務」變得十分繁忙,經常在業餘時間給人做Lucene的諮詢,賺了不少外快。

但是張大胖也敏銳地覺察到了兩個問題:

1. Lucene做搜索很強大,但是API用起來太「低級」,很多人抱怨:我就想搜索一下我的產品描述,現在還得理解什麼「Directory」,"Analyzer","IndexWriter",實在是太複雜了!

2. 互聯網的數據是海量的,僅僅是單機存儲索引是遠遠不夠的。

俗話說:「軟體開發中遇到的所有問題,都可以通過增加一層抽象而得以解決」。 張大胖覺得,是時候對Lucene做一個抽象了。

Java API -> Web API

保險起見,張大胖拉了大神Bill來做顧問,幫自己設計。

這個新的抽象層應該對外提供一個什麼樣的API呢?

很多時候,Web開發面對的都是領域模型,比如User,Product, Blog,Account之類。用戶想做的無非就是搜索產品的描述, 搜索Blog的標題,內容等等。

張大胖說:「如果能圍繞著領域模型的概念進行搜索的各種操作就好了,這樣簡單而直接,如同CRUD。」

Bill 提示到:「Web時代了,程序員都喜歡採用RESTful的方式來描述一個Web資源, 對於搜索而言,完全可以借鑒一下嘛!」

張大胖眼前一亮: 「要不這樣?」

/coolspace/blog/1001 : 表示一個編號為1001的博客

/coolspace/user/u3876:表示一個ID為u3876的用戶

/coolspace表示一個「索引庫

/blog ,/user 表示「索引的類型」(可以理解為編程中的領域模型)。 1001, u3876表示數據的ID

格式是/<index>/<type>/<id>

如果和關係資料庫做個類比的話:

索引庫<--->資料庫

索引的類型 <--->資料庫的表

Bill說:「這樣挺好的,用戶看到的就是領域模型, 當用戶想去操作時候,用HTTP的GET, PUT等操作就好了,交互的數據可以使用JSON這個簡單的格式。」

張大胖開始定義基本的操作。

(1) 把文檔加入索引庫

例如:把一個blog的「文檔」加入索引庫,這個文檔的數據是JSON格式,其中的每個欄位將來都可以被搜索:

PUT /coolspace/blog/1001

{

"title" : "xxxxxxx",

"content" : "xxxxxxxxxxxx",

"author" : "xxxxx",

"create_date": "xxxxxx"

...

}

(註:當然,在發起HTTP請求的時候,需要加上伺服器的地址和埠,下同。)

(2)把一個blog文檔刪除,從此就再也搜索不到了

DELETE /coolspace/blog/1001

(3) 用戶搜索

用戶想搜索的時候也很簡單,發起一個這樣的請求就行:

GET /coolspace/blog/_search

但是如何表達查詢的具體需求呢,這時候必須得定義一個規範了,例如:想查詢一個內容欄位(content)包含「java"的 blog。

GET /coolspace/blog/_search

{

"query" : {

"match" : {

"content" : "java"

}

}

}

這個query下面可以增加更加複雜的條件,表示用戶的查詢需求,反正是JSON格式,可以非常靈活。

返回值也是JSON, 這裡就不再展示了。

這個抽象層是以HTTP+JSON來表示的, 和具體的編程語言無關,不管是Java, 還是Python,Ruby,只要能發起HTTP調用,就可以使用。

搜索之路:Elasticsearch的誕生

通過這樣一個抽象層, Lucene那些複雜的API全部被隱藏到了海平面以下。

對於程序員來說,使用HTTP+JSON是非常自然的事情,好用就是最大的生產力。

分散式

到目前為止,進展還算順利,接下來要考慮的就是如何存儲海量的索引數據。

張大胖說: 「這個簡單,如果索引太大,我們把它切割一下,分成一片一片的,存儲到各個機器上不就得了?」

搜索之路:Elasticsearch的誕生

Bill問道: 「想得美! 你分片以後,用戶去保存索引的時候,還有搜索索引數據的時候,到哪個機器上去取?」

張大胖說:「這個簡單,首先我們保存每個分片和機器之間的對應關係, 嗯,我覺得叫node顯得更專業。」

分片1 :node1

分片2 :node2

分片3 :node3

「分片在英文中叫做shard。 」 Bill 友情提示。

「好的, 然後可以用餘數演算法來確定一個『文檔』到底保存在哪個shard中。」 雖然張大胖覺得這個詞看起來不爽,還是開始使用了

shard 編號 = hash(文檔的ID) % shard 總數

「這樣對於任意一個文檔,對它的ID做hash計算,然後對總分片數量求余, 就可以得到shard的編號,然後就可以找到對應的機器了。 」 張大胖覺得自己的這個演算法又簡單,效率又高,洋洋得意。

Bill覺得這兩年張大胖進步不小,開始使用演算法來解決問題了, 他問道:「如果用戶想增加shard數該怎麼處理呢? 這個餘數演算法就會出問題啊 !」


比如原來shard 總數是3, hash值是100, shard編號 = 100 % 3 = 1

假設用戶又增加了兩台機器,shard總數變成了5, 此時 shard 編號 = 100 % 5 = 0 , 如果去0號機器上去找索引,肯定是找不到的。

張大胖撓撓頭:「要不採用分散式一致性演算法, 嗯,它會減少出錯的情況,還是無法避免,這該怎辦?」

Bill建議:「要不這樣,我們可以立下一個規矩: 用戶在創建索引庫的時候,必須要指定shard數量,並且一旦指定,就不能更改了!」

PUT /coolspace

{

"settings" : {

"number_of_shards" : 3

}

}

雖然對用戶來說有點不爽, 但餘數演算法的高效和簡單確實太吸引人了,張大胖表示同意。

「索引數據分布了,如果某個節點壞掉了,數據就會丟失,我們得做個備份才行。」 張大胖的思考很深入。

「對, 我們可以用新的node 來做replica,也可以為了節省空間, 復用現有的node來做replica。為了做區分,可以把之前的分片叫做主分片,primary shard。」 Bill英文就是好。

搜索之路:Elasticsearch的誕生

此處的設置為:每個主分片有兩個副本

PUT /coolspace/_settings

{

"number_of_replicas" : 2

}

雖然主分片的數目在創建「索引庫」的時候已經確定,但是副本的數目是可以任意增減的,這依賴於硬體的情況:性能和數量。

「現在每個主分片都有兩個副本, 如果某個節點掛了也不怕,比如節點1掛了,我們可以位於節點3上的副本0提升為 主分片0, 只不過每個主分片的副本數不夠兩個了。」 張大胖說道。

搜索之路:Elasticsearch的誕生

Bill 滿不在乎地說: 「沒事,等到節點1啟動後,還可以恢復副本。」

集群

Bill和張大胖立刻意識到,他們建立了一個集群, 這個集群中可以包含若干node , 有數據的備份,能實現高可用性。

但是另外一個問題馬上就出現了:對於客戶端來說,通過哪個node來讀寫『文檔』呢?

比如說用戶要把一個"文檔"加入索引庫: PUT /coolspace/blog/1001, 該如何處理?

Bill說:「這樣吧,我們可以讓請求發送到集群的任意一個節點,每個節點都具備處理任何請求的能力。」

張大胖說:「具體怎麼做呢? 」

Bill 寫下了處理過程:

(1)假設用戶把請求發給了節點1

(2)系統通過餘數演算法得知這個"文檔"應該屬於主分片2,於是請求被轉發到保存該主分片的節點3

(3)系統把文檔保存在節點3的主分片2中,然後將請求轉發至其他兩個保存副本的節點。副本保存成功以後,節點3會得到通知,然後通知節點1, 節點1再通知用戶。

搜索之路:Elasticsearch的誕生

「如果是做查詢呢? 比如說用戶查詢一個文檔: GET /coolspace/blog/1001, 該如何處理?」 張大胖問道。

「同樣,查詢的請求也可以分發到任意一個節點,然後該節點可以找到主分片或者任意一個副本,返回即可。 」

(1) 請求被發給了節點1

(2)節點1計算出該數據屬於主分片2,這時候,有三個選擇,分別是位於節點1的副本2, 節點2的副本2,節點3的主分片2, 假設節點1為了負載均衡,採用輪詢的方式,選中了節點2,把請求轉發。

(3) 節點2把數據返回給節點1, 節點1 最後返回給客戶端。

搜索之路:Elasticsearch的誕生

「這個方式比較靈活,但是要求各個節點之間得能互通有無才行!」 張大胖說道。

「不僅如此,對於一個集群來說,還得有一個主節點(master node),這個主節點在處理數據請求上和其他節點是平等的,但是它還有更重要的工作,需要維護整個集群的狀態,增加移除節點,創建/刪除索引庫,維護主分片和集群的關係等等。」

「那如果這個主節點掛了呢? 」 張大胖追問。

「那隻好從剩下的節點中重新選舉嘍!」

「哎呀,這就涉及到分散式系統的各種問題了,什麼一致性,腦裂,太難了!」 張大胖開始打退堂鼓。

「我們只是選取一個Master, 要簡單得多,你可以看看一個叫做Bully的演算法, 改進一下應該就可以用了。 」

開發分散式系統的難度要遠遠大於一個單機系統,半年以後,這個被Bill命名為Elasticsearch的系統才發布了第一個版本。

由於它屏蔽了很多Lucene的細節,又支持海量索引的存儲,很快就大受歡迎。

Elasticsearch 的真正傳奇

當然, Elasticsearch不是Bill和張大胖創造的,這裡才是其傳奇的歷史:(來源:《Elasticsearch權威指南》)

許多年前,一個剛結婚的名叫 Shay Banon 的失業開發者,跟著他的妻子去了倫敦,他的妻子在那裡學習廚師。 在尋找一個賺錢的工作的時候,為了給他的妻子做一個食譜搜索引擎,他開始使用 Lucene 的一個早期版本。

直接使用 Lucene 是很難的,因此 Shay 開始做一個抽象層,Java 開發者使用它可以很簡單的給他們的程序添加搜索功能。 他發布了他的第一個開源項目 Compass。

後來 Shay 獲得了一份工作,主要是高性能,分散式環境下的內存數據網格。這個對於高性能,實時,分散式搜索引擎的需求尤為突出, 他決定重寫 Compass,把它變為一個獨立的服務並取名 Elasticsearch。

第一個公開版本在2010年2月發布,從此以後,Elasticsearch 已經成為了 Github 上最活躍的項目之一,他擁有超過300名 contributors(目前736名 contributors )。 一家公司已經開始圍繞 Elasticsearch 提供商業服務,並開發新的特性,但是,Elasticsearch 將永遠開源並對所有人可用。

據說,Shay 的妻子還在等著她的食譜搜索引擎…

文章轉自公眾號:碼農翻身

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!

TAG: |