ElasticSearch數據同步與無縫遷移

ElasticSearch作爲一款開源的全文搜索引擎在如今的軟件開發得到了越來越廣泛的應用,在業務功能開發方面,可以選用ElasticSearch提供比數據庫查詢更強大的搜索方式,同時基於搜索結果評分(權重)和高亮讓我們很輕易地通過它實現一個站內的搜索引擎。

ElasticSearch VS 數據庫

剛接觸ElasticSearch(ES)時我們經常將它與數據庫類比起來學習,從結構上:

  • Indices類似於數據庫的database
  • Type類似於數據庫的table
  • Fields類似於數據表的列
  • Documents類似於數據表的行(即每條記錄) 同時,數據庫提供的搜索語法都能在ES上找到影子,比如數據庫提供AND、OR邏輯運算符,ES中有must, should,而數據庫的如“like”等文字匹配功能在ES中則更加強大。

儘管如此,ES本質上的定位仍是一個搜索引擎。NoSQL和ES一樣都有着相同鬆散的結構,雖然我們也有一些討論是否可以用ES來替代非關係型數據庫(撇開ES是不是一種NoSQL),但是一個現實是ES和NoSQL依舊是互有利弊;再者,傳統關係型數據庫的事務性、多表關聯結構也是ES無法提供的。 所以,在實際的開發過程中,關係型數據庫、NoSQL、ES依舊是相輔相成的關係,我們一般只會在較複雜的搜索場景下會選用ES提供搜索服務,而其源數據依舊來自於數據庫,所以這就引出了ES與數據庫之間的數據同步問題。

全量數據導入

在第一次將存儲在數據庫裏面的數據導入到ES需要執行全量導入,當後續有數據更新時通過消息隊列通知ES更新數據。

使用消息隊列實現ES增量同步

消息隊列在軟件開發領域是一個十分常見的名詞。 在操作系統層面,我們可以利用消息隊列做進程間的通信;在一個單體應用,比如Android應用,利用一個MessageQueue類來解決UI線程與耗時子線程之間的界面刷新問題,在物聯網領域,基於發佈/訂閱模型模型的MQTT協議被廣泛應用於平臺對海量設備的消息分發,而在分佈式系統,以及最近幾年日益熱門的微服務架構中,是一個十分常用的實現異步消息、解耦應用、最終一致性的組件。 常見的消息隊列採用“發佈-訂閱”模式,初入門者幾乎可以認爲這是個“觀察者模式”。

消息隊列模式-“發佈-訂閱”模式

目前常用的消息隊列框架有KafkaRabbitMQ。 消息隊列實現增量同步的方式,是在主服務對數據庫進行創建、刪除、修改一條記錄時,發佈一條主題消息給消息隊列,同時同步服務需要訂閱相關主題,這樣消息隊列就可以將更新的記錄轉發給同步服務,同步服務再根據消息的內容在ES裏面進行更新記錄。 消息隊列實現增量同步除了可以解耦主服務和同步服務,還有一個好處就是保證同步的容錯性,比如當數據庫添加一條記錄時,如果直接採用HTTP的方式(可能是一個post請求)與同步服務取得聯繫時出現連接失敗、post請求失敗的時候,如果不採取任何措施這條記錄就會無法得到同步。而消息隊列的失敗重發的機制可以很好的解決這個問題,同時消息隊列,FIFO(先進先出)的機制也保證了消息轉發的順序。

ES索引更改後怎樣做無縫重建

ES索引更改發生在ElasticSearch 索引結構發生變化,比如隨着業務的發展對Type中字段的增減以及字段類型的更改,或者發生在ES版本升級帶來的結構變化時,例如ES 5.0版本將之前的string類型拆分爲textkeyword類型,當我們希望對ES進行版本升級時,那麼之前的string類型就不可再用了。

與常見的Web服務的藍綠部署實現無宕機升級類似,ES無縫升級也可以類比實現。Web服務的藍綠部署的原理是使用LoadBalancer(負載均衡器)做流量切換,新舊服務都有不同的訪問URL,但是隻有LoadBalancer的URL對外可訪問,即:

  • 服務升級前:負載均衡指向舊服務V1
  • 服務升級中:發佈新服務V2,負載均衡依舊指向舊服務,此時存在新舊服務同時存在
  • 服務升級完成:新服務V2啓動完成,負載均衡切換指向,將訪問流量導向新服務V2
  • 服務升級完成:負載均衡切換指向後停掉舊服務V1

藍綠部署

ES索引的別名

ES提供了通過索引別名(alias)來訪問索引的方式:比如

curl -XPOST 'http://localhost:9200/_aliases' -d{ "actions": [ {"add": {"index": "test_20181007", "alias": "test"}} ]}

就爲索引 test_20181007創建了一條別名 test,這樣訪問 localhost:9200/test/_searchlocalhost:9200/test_20181007/_search都可以搜索索引裏面的內容。 ES的別名的存在爲ES的無縫升級和切換提供了可能,類似於負載均衡切換指向一樣,我們可以讓ES別名在升級前後,指向新舊不同版本的索引即可。

ES無縫升級

  • 新建帶版本的新索引
PUT /test_v2{ "settings": { ... any settings ... }, "mappings": { "type_one": { ... any mappings ... }, }}
  • 暫停增量更新 由於在升級期間我們不希望後續的記錄更新到舊索引上,所以需要將消息隊列進行暫停(pause)操作,在新索引創建成功後再開啓。
  • 執行全量數據導入
  • 切換對外別名指向 一個別名可以指向多個索引,所以我們在添加別名到新索引的同時必須從舊的索引中刪除它。這個操作需要原子化,這意味着我們需要使用 _aliases 操作:
POST /_aliases{ "actions": [ { "remove": { "index": "test_v1", "alias": "test" }}, { "add": { "index": "test_v2", "alias": "test" }} ]}
  • 刪除舊索引
DELETE /test_v1
  • 開啓增量更新 這樣在升級過程中的數據庫中有更新的記錄將會在新索引上同步
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章