有贊搜尋系統的技術內幕

有贊技術發表於2018-09-18

上文說到有贊搜尋系統的架構演進,為了支撐不斷演進的技術架構,除了 Elasticsearch 的維護優化之外,我們也開發了上層的中介軟體來應對不斷提高的穩定性和效能要求。

Elasticsearch 的檢索執行效率可以表示為: O(num_of_files * logN)

其中 num_of_files 表示索引檔案段的個數,N 表示需要遍歷的資料量,從這裡我們可以總結出提升查詢效能可以考慮的兩點:

  1. 減少遍歷的索引檔案數量
  2. 減少遍歷的索引文件總數

從 Elasticsearch 自身來說,減少索引檔案數量方面可以參考幾點:

  1. 通過 optimize 介面強制合併段
  2. 增大 index buffer/refresh_interval,減少小段生成,控制相同數量的文件生成的新段個數

不論是強制合併或者 index buffer/refresh_interval,都有其應用場景的限制,比如調整 index buffer / refresh_interval 會相應的延長資料可見時間;optimize 對冷資料集比較適用,如果資料在不斷變化過程中,除了新段的生成,老資料可能因為舊段過大而得不到物理刪除,反而造成較大的負擔。

而減少文件總數方面,也可以做相應的優化:

  1. 減少文件更新
  2. 指定 _routing 來路由查詢到指定的 shard
  3. 通過 rollover 介面進行冷熱隔離

這裡尤其需要注意的是減少文件更新,由於 LSM 追加寫的資料組織方式,更新資料其實是新增資料+標記老資料為刪除狀態的組合,真實參與計算的資料量是有效資料和標記刪除的資料量之和,減少文件更新次數除了減少標記刪除資料之外,還可以降低段 merge 以及索引重新整理的消耗。

考慮到實際的業務場景,如果將海量資料儲存於單個索引,由於 shard 個數不可變,一方面會使得索引分配大量的 shard,資料量持續增長會逐漸拖慢索引訪問效能,另一方面想要通過擴充套件 shard 提高讀寫效能需要重建海量資料,成本相當高昂。

索引拆分

為了增強索引的橫向擴容能力,我們在中介軟體層面進行了索引拆分,參考實際的業務場景將大索引拆分為若干個小索引。 在索引拆分前,首先需要檢查索引對應業務是否滿足拆分的三個必要條件:

  1. 讀寫操作必定會帶入固定條件
  2. 讀寫操作維度唯一
  3. 使用者不關心全域性的搜尋結果

比較典型的比如店鋪內商品搜尋,不論買賣家都只關心固定店鋪內的商品檢索結果,沒有跨店鋪檢索需求,後臺店鋪與商品也有固定的對映關係,這樣就可以在中介軟體層面對讀寫請求進行解析,並路由到對應的子索引中,減少遍歷的文件總量,可以在效能上獲得明顯的提升。

有贊搜尋系統的技術內幕

相對 Elasticsearch 自帶的 _routing,這個方案具備更加靈活的控制粒度,比如可以配置白名單,將部分店鋪資料路由到其他不同 SLA 級別的索引或叢集,當然可以配合 _routing 以獲得更好的表現。

索引拆分首先會帶來全域性索引檔案資料上升的問題,不過因為沒有全域性搜尋需求,所以不會帶來實質的影響;其次比較需要注意的是資料傾斜問題,在拆分前需要先通過離線計算模擬索引拆分效果,如果發現資料傾斜嚴重,就可以考慮將子索引資料進行重平衡。

有贊搜尋系統的技術內幕

如圖所示,資料重平衡在原有的拆分基礎上加入一個邏輯拆分步驟:

  1. 資料首先拆分為 5 個邏輯索引
  2. 設定重平衡因子,假設為 N
  3. 根據重平衡因子將邏輯索引資料順序雜湊到N個連續的物理索引中

有贊搜尋系統的技術內幕

如圖,按平衡因子3重平衡之後文件資料量的最大差值從 510 降為了 160,單索引佔全域性資料量比例從之前的 53% 降低為 26%,能夠起到不錯的資料平衡效果。

冷熱隔離

在查詢維度不唯一的場景下,索引拆分就不適用了,為了解決此類場景下的效能問題,可以考慮對索引進行冷熱隔離。 比如日誌/訂單型別的資料,具備比較明顯的時間特徵,大量的操作都集中在近期的一段時間內,這時就可以考慮依據時間欄位對其拆分為冷熱索引。

有贊搜尋系統的技術內幕

Elasticsearch 自帶有 rollover 介面供索引進行自動輪轉,通過索引存活時間和儲存的文件數量作為輪轉條件,滿足其中之一即可建立一個新索引並將其作為當前的活躍索引。 在實際應用過程中,rollover 介面需要使用者感知活躍索引的變更,且自行計算查詢需要訪問的索引範圍,為了對使用者遮蔽底層這些複雜操作的細節,我們在中介軟體封裝了索引的冷熱隔離特性,從使用者視角只須訪問固定索引並帶入固定欄位即可。

有贊搜尋系統的技術內幕

首先配置路由表,根據不同的時間跨度劃定不同的路由規則,比如業務初期資料增量並不大,可以用50的時間跨度建立子索引,後期業務增量變大後逐漸縮短時間跨度至 10 來建立子索引。 通過查詢條件的起止時間點分別從路由表中計算對應的子索引偏移量,得到起止範圍,以上圖為例,連續的子索引範圍為 index2~index14,也就是該條件將命中此區間內的全部子索引,寫操作也類似,區別在於寫資料只會落到一個子索引,一般是當前的活躍索引。 這樣冷熱隔離的方式拆分可以相容多維度的查詢需求,比如訂單的買賣家查詢維度,而且拆分規則比較靈活,可以動態調整,另外刪除資料只需要刪除整個過期索引,而不必通過 delete_by_query 的方式緩慢刪除索引資料。 除此之外,為了更好的配合使用者使用,我們還對此開發了索引的自動輪轉/定時清理等輔助功能。

HA

隨著搜尋系統的廣泛使用,使用者對系統的穩定性也提出了更高的要求,比如在機房發生斷電等故障情況下,依然能夠保證服務可用,這就需要我們能夠將資料進行跨機房複製同步。 首先考慮的方案是跨機房組建 Elasticsearch 叢集,這樣實現非常簡單,但是問題是機房的網路互動是走專線的,在叢集當機恢復資料過程中會有很大的頻寬消耗,可能引起機房間的網路擁堵,因此這個方案並不可行。 Elasticsearch 本身也在開發 Changes API 特性,可以用於跨叢集的資料同步,但可惜的是該特性仍然在開發中,在參考了主流的資料同步方法後,我們在中介軟體層開發了一套非同步資料複製系統。

有贊搜尋系統的技術內幕

在確認索引開啟多機房複製後,首先在 proxy 側啟動增量同步,傳送同步訊息給 mq 作為同步程式 clones 的增量資料來源,然後通過 reindex 功能從主索引全量複製資料到從索引。

在跨機房同步過程中,資料容易因為 MQ、proxy 非同步傳送等影響而亂序,Elasticsearch 可以通過樂觀鎖來保證資料變更的一致性,避免亂序的影響,前提是 version 能夠一直保持在索引中。 但是在物理刪除模式下,由於資料被物理清理,無法繼續保持版本號的延續,這就有可能導致跨機房資料同步的髒寫。

有贊搜尋系統的技術內幕

為了避免樂觀鎖失效,我們的解決方法是軟刪除的方式:

  1. delete 操作在中介軟體轉換為 index 操作,文件內容僅包含一個特殊欄位,不會命中正常的搜尋條件,也就是正常情況下無法搜尋得到該文件,達到實際的刪除效果
  2. 在中介軟體將 create/get/update/delete 等操作轉換為 script 請求,保持原有語義不變
  3. 通過軟刪除文件中特殊欄位記錄的時間戳定時清理資料(可選)

為了能夠感知到主從索引間的資料一致性和同步延遲,還有一套輔助的資料對賬系統實時執行,可以用於主從索引資料的校驗、修復並通過 MQ 消費延時計算主從資料的延遲。

小結

到這裡有贊搜尋系統的大致框架已經介紹完畢,因為篇幅的原因還有很多細節的功能設計並沒有完整表述,也歡迎有興趣的同學聯絡我們一起探討,有表述錯誤的地方也歡迎大家聯絡我們糾正。 關於 Elasticsearch 方面本次的兩篇文章都沒有太多涉及,後續待我們整理完善之後會作為一個擴充套件閱讀奉送給大家。

有贊搜尋系統的技術內幕

相關文章