有贊基於ES的搜索系統架構是如何演進的?



本文從架構上介紹了有贊搜索系統演進產生的背景以及但願解決的問題。數據庫


image.png

有贊搜索平臺是一個面向公司內部各項搜索應用以及部分 NoSQL 存儲應用的 PaaS 產品,幫助應用合理高效的檢索和多維過濾功能。有贊搜索平臺目前支持了大大小小一百多個檢索業務,服務於近百億數據。
緩存


在爲傳統的搜索應用提供高級檢索和大數據交互能力的同時,有贊搜索平臺還須要爲其餘好比商品管理、訂單檢索、粉絲篩選等海量數據過濾提供支持。網絡


從工程的角度看,如何擴展平臺以支持多樣的檢索需求是一個巨大的挑戰。架構


咱們搜索團隊目前主要負責平臺的性能、可擴展性和可靠性方面的問題,並儘量下降平臺的運維成本以及業務的開發成本。app


Elasticsearch 是一個高可用分佈式搜索引擎,一方面技術相對成熟穩定,另外一方面社區也比較活躍,所以咱們在搭建搜索系統過程當中也是選擇了 Elasticsearch 做爲咱們的基礎引擎。運維


架構 1.0分佈式


時間回到 2015 年,彼時運行在生產環境的有贊搜索系統是一個由幾臺高配虛擬機組成的 Elasticsearch 集羣,主要運行商品和粉絲索引,數據經過 Canal 從 DB 同步到 Elasticsearch,大體架構以下圖:ide

image.png

經過這種方式,在業務量較小時,能夠低成本的快速爲不一樣業務索引建立同步應用,適合業務快速發展時期。oop


但相對的每一個同步程序都是單體應用,不只與業務庫地址耦合,須要適應業務庫快速的變化,如遷庫、分庫分表等,並且多個 Canal 同時訂閱同一個庫,也會形成數據庫性能的降低。
性能


另外 Elasticsearch 集羣也沒有作物理隔離,有一次促銷活動就由於粉絲數據量過於龐大致使 Elasticsearch 進程 Heap 內存耗盡而 OOM,使得集羣內所有索引都沒法正常工做,這給我上了深深的一課。


架構 2.0


咱們在解決以上問題的過程當中,也天然的沉澱出了有贊搜索的 2.0 版架構,大體架構以下圖:

image.png

首先數據總線將數據變動消息同步到 MQ,同步應用經過消費 MQ 消息來同步業務庫數據,借數據總線實現與業務庫的解耦,引入數據總線也能夠避免多個 Canal 監聽消費同一張表 Binlog 的虛耗。


高級搜索(Advanced Search)


隨着業務發展,咱們也逐漸出現了一些比較中心化的流量入口,好比分銷、精選等。


這時普通的 Bool 查詢並不能知足咱們對搜索結果的細粒率排序控制需求,將複雜的 function_score 之類專業性較強的高級查詢編寫和優化工做交給業務開發負責顯然是個不可取的選項。


這裏咱們考慮的是經過一個高級查詢中間件攔截業務查詢請求,在解析出必要的條件後從新組裝爲高級查詢交給引擎執行,大體架構以下:

image.png

這裏另外作的一點優化是加入了搜索結果緩存,常規的文本檢索查詢 Match 每次執行都須要實時計算。


在實際的應用場景中這並非必須的,用戶在必定時間段內(好比 15 或 30 分鐘)經過一樣的請求訪問到一樣的搜索結果是徹底能夠接受的。


在中間件作一次結果緩存能夠避免重複查詢反覆執行的虛耗,同時提高中間件響應速度。


大數據集成


搜索應用和大數據密不可分,除了經過日誌分析來挖掘用戶行爲的更多價值以外,離線計算排序綜合得分也是優化搜索應用體驗不可缺乏的一環。


在 2.0 階段咱們經過開源的 ES-Hadoop 組件搭建 Hive 與 Elasticsearch 之間的交互通道,大體架構以下:

image.png

經過 Flume 收集搜索日誌存儲到 HDFS 供後續分析,也能夠在經過 Hive 分析後導出做爲搜索提示詞,固然大數據爲搜索業務提供的遠不止於此,這裏只是簡單列舉了幾項功能。


遇到的問題


這樣的架構支撐了搜索系統一年多的運行,可是也暴露出了許多問題,首當其衝的是愈加高昂的維護成本。


除去 Elasticsearch 集羣維護和索引自己的配置、字段變動,雖然已經經過數據總線與業務庫解耦,可是耦合在同步程序中的業務代碼依舊爲團隊帶來了極大的維護負擔。


消息隊列雖然必定程序上減輕了咱們與業務的耦合,可是帶來的消息順序問題也讓不熟悉業務數據狀態的咱們很難處理。


除此以外,流經 Elasticsearch 集羣的業務流量對咱們來講呈半黑盒狀態,能夠感知,但不可預測,也所以出現過線上集羣被內部大流量錯誤調用壓到 CPU 佔滿不可服務的故障。


目前的架構 3.0


針對 2.0 時代的問題,咱們在 3.0 架構中作了一些針對性調整,列舉主要的幾點:

  • 經過開放接口接收用戶調用,與業務代碼徹底解耦。

  • 增長 Proxy 用來對外服務,預處理用戶請求並執行必要的流控、緩存等操做。

  • 提供管理平臺簡化索引變動和集羣管理,這樣的演變讓有贊搜索系統逐漸的平臺化,已經初具了一個搜索平臺的架構。


Proxy


做爲對外服務的出入口,Proxy 除了經過 ESLoader 提供兼容不一樣版本 Elasticsearch 調用的標準化接口以外,也內嵌了請求校驗、緩存、模板查詢等功能模塊。


請求校驗主要是對用戶的寫入、查詢請求進行預處理,若是發現字段不符、類型錯誤、查詢語法錯誤、疑似慢查詢等操做後,以 Fast Fail 的方式拒絕請求或者以較低的流控水平執行,避免無效或低效能操做對整個 Elasticsearch 集羣的影響。


緩存和 ESLoader 主要是將原先高級搜索中的通用功能集成進來,使得高級搜索能夠專一於搜索自身的查詢分析和重寫排序功能,更加內聚。


咱們在緩存上作了一點小小的優化,因爲查詢結果緩存一般來講帶有源文檔內容會比較大,爲了不流量高峯頻繁訪問致使 Codis 集羣網絡擁堵,咱們在 Proxy 上實現了一個簡單的本地緩存,在流量高峯時自動降級。


這裏提一下模板查詢,在查詢結構(DSL)相對固定又比較冗長的狀況下,好比商品類目篩選、訂單篩選等,能夠經過模板查詢(Search Template)來實現。


一方面簡化業務編排 DSL 的負擔,另外一方面還能夠經過編輯查詢模板 Template,利用默認值、可選條件等手段在服務端進行線上查詢性能調優。


管理平臺


爲了下降平常的索引增刪、字段修改、配置同步上的維護成本,咱們基於 Django 實現了最第一版本的搜索管理平臺。


主要提供一套索引變動的審批流以及向不一樣集羣同步索引配置的功能,以可視化的方式實現索引元數據的管理,減小咱們在平臺平常維護上的時間成本。


因爲開源 Head 插件在效果展現上的不友好,以及暴露了部分粗暴功能:

image.png

如上圖,能夠經過點按字段使得索引按指定字段排序展現結果,在早期版本 Elasticsearch 會經過 Fielddata 加載須要排序的字段內容。


若是字段數據量比較大,很容易致使 Heap 內存佔滿引起 Full GC 甚至 OOM。


爲了不重複出現此類問題,咱們也提供了定製的可視化查詢組件以支持用戶瀏覽數據的需求。


ESWriter


因爲 ES-Hadoop 僅能經過控制 Map-Reduce 個數來調整讀寫流量,實際上 ES-Hadoop 是以 Elasticsearch 是否拒絕請求來調整自身行爲,對線上工做的集羣至關不友好。


爲了解決這種離線讀寫流量上的不可控,咱們在現有的 DataX 基礎上開發了一個 ESWriter 插件,可以實現記錄條數或者流量大小的秒級控制。


面臨挑戰


平臺化以及配套的文檔體系完善下降了用戶的接入門檻,隨着業務的快速增加,Elasticsearch 集羣自己的運維成本也讓咱們逐漸不堪。


雖然有物理隔離的多個集羣,但不可避免的會有多個業務索引共享同一個物理集羣,在不一樣業務間各有出入的生產標準上支持不佳,在同一個集羣內部署過多的索引也是生產環境穩定運行的一個隱患。


另外集羣服務能力的彈性伸縮相對困難,水平擴容一個節點都須要經歷機器申請、環境初始化、軟件安裝等步驟,若是是物理機還須要更長時間的機器採購過程,不能及時響應服務能力的不足。


將來的架構 4.0


當前架構經過開放接口接受用戶的數據同步需求,雖然實現了與業務解耦,下降了咱們團隊自身的開發成本,可是相對的用戶開發成本也變高了。


數據從數據庫到索引須要經歷從數據總線獲取數據、同步應用處理數據、調用搜索平臺開放接口寫入數據三個步驟。


其中從數據總線獲取數據與寫入搜索平臺這兩個步驟在多個業務的同步程序中都會被重複開發,形成資源浪費。


這裏咱們目前也準備與 PaaS 團隊內自研的 DTS(Data Transporter,數據同步服務)進行集成,經過配置化的方式實現 Elasticsearch 與多種數據源之間的自動化數據同步。


要解決共享集羣應對不一樣生產標準應用的問題,咱們但願進一步將平臺化的搜索服務提高爲雲化的服務申請機制,配合對業務的等級劃分,將核心應用獨立部署爲相互隔離的物理集羣。


而非核心應用經過不一樣的應用模板申請基於 K8S 運行的 Elasticsearch 雲服務。


應用模板中會定義不一樣應用場景下的服務配置,從而解決不一樣應用的生產標準差別問題,並且雲服務能夠根據應用運行情況及時進行服務的伸縮容。

相關文章
相關標籤/搜索