日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

技術瑣話發表於2022-12-05

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

京東到家訂單中心繫統業務中,無論是外部商家的訂單生產,或是內部上下游系統的依賴,訂單查詢的呼叫量都非常大,造成了訂單資料讀多寫少的情況。

我們把訂單資料儲存在MySQL中,但顯然只透過DB來支撐大量的查詢是不可取的。同時對於一些複雜的查詢,MySQL支援得不夠友好,所以訂單中心繫統使用了Elasticsearch來承載訂單查詢的主要壓力。

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

Elasticsearch作為一款功能強大的分散式搜尋引擎,支援近實時的儲存、搜尋資料,在京東到家訂單系統中發揮著巨大作用,目前訂單中心ES叢集儲存資料量達到10億個文件,日均查詢量達到5億。

隨著京東到家近幾年業務的快速發展,訂單中心ES架設方案也不斷演進,發展至今ES叢集架設是一套實時互備方案,很好地保障了ES叢集讀寫的穩定性,下面就給大家介紹一下這個歷程以及過程中遇到的一些坑。

ES 叢集架構演進之路

1、初始階段

訂單中心ES初始階段如一張白紙,架設方案基本沒有,很多配置都是保持叢集預設配置。整個叢集部署在集團的彈性雲上,ES叢集的節點以及機器部署都比較混亂。同時按照叢集維度來看,一個ES叢集會有單點問題,顯然對於訂單中心業務來說也是不被允許的。

2、叢集隔離階段

和很多業務一樣,ES叢集採用的混布的方式。但由於訂單中心ES儲存的是線上訂單資料,偶爾會發生混布叢集搶佔系統大量資源,導致整個訂單中心ES服務異常。

顯然任何影響到訂單查詢穩定性的情況都是無法容忍的,所以針對於這個情況,先是對訂單中心ES所在的彈性雲,遷出那些系統資源搶佔很高的叢集節點,ES叢集狀況稍有好轉。但隨著叢集資料不斷增加,彈性雲配置已經不太能滿足ES叢集,且為了完全的物理隔離,最終乾脆將訂單中心ES叢集部署到高配置的物理機上,ES叢集效能又得到提升。

3、節點副本調優階段

ES的效能跟硬體資源有很大關係,當ES叢集單獨部署到物理機器上時,叢集內部的節點並不是獨佔整臺物理機資源,在叢集執行的時候同一物理機上的節點仍會出現資源搶佔的問題。所以在這種情況下,為了讓ES單個節點能夠使用最大程度的機器資源,採用每個ES節點部署在單獨一臺物理機上方式。

但緊接著,問題又來了,如果單個節點出現瓶頸了呢?我們應該怎麼再最佳化呢?

ES查詢的原理,當請求打到某號分片的時候,如果沒有指定分片型別(Preference引數)查詢,請求會負載到對應分片號的各個節點上。而叢集預設副本配置是一主一副,針對此情況,我們想到了擴容副本的方式,由預設的一主一副變為一主二副,同時增加相應物理機。

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

訂單中心ES叢集架設示意圖

如圖,整個架設方式透過VIP來負載均衡外部請求:

整個叢集有一套主分片,二套副分片(一主二副),從閘道器節點轉發過來的請求,會在打到資料節點之前透過輪詢的方式進行均衡。叢集增加一套副本並擴容機器的方式,增加了叢集吞吐量,從而提升了整個叢集查詢效能。

下圖為訂單中心ES叢集各階段效能示意圖,直觀地展示了各階段最佳化後ES叢集效能的顯著提升:

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

當然分片數量和分片副本數量並不是越多越好,在此階段,我們對選擇適當的分片數量做了進一步探索。分片數可以理解為MySQL中的分庫分表,而當前訂單中心ES查詢主要分為兩類:單ID查詢以及分頁查詢。

分片數越大,叢集橫向擴容規模也更大,根據分片路由的單ID查詢吞吐量也能大大提升,但聚合的分頁查詢效能則將降低;分片數越小,叢集橫向擴容規模也更小,單ID的查詢效能也會下降,但分頁查詢的效能將會提升。

所以如何均衡分片數量和現有查詢業務,我們做了很多次調整壓測,最終選擇了叢集效能較好的分片數。

4、主從叢集調整階段

到此,訂單中心的ES叢集已經初具規模,但由於訂單中心業務時效性要求高,對ES查詢穩定性要求也高,如果叢集中有節點發生異常,查詢服務會受到影響,從而影響到整個訂單生產流程。很明顯這種異常情況是致命的,所以為了應對這種情況,我們初步設想是增加一個備用叢集,當主叢集發生異常時,可以實時的將查詢流量降級到備用叢集。

那備用叢集應該怎麼來搭?主備之間資料如何同步?備用叢集應該儲存什麼樣的資料?

考慮到ES叢集暫時沒有很好的主備方案,同時為了更好地控制ES資料寫入,我們採用業務雙寫的方式來搭設主備叢集。每次業務操作需要寫入ES資料時,同步寫入主叢集資料,然後非同步寫入備叢集資料。同時由於大部分ES查詢的流量都來源於近幾天的訂單,且訂單中心資料庫資料已有一套歸檔機制,將指定天數之前已經關閉的訂單轉移到歷史訂單庫。

所以歸檔機制中增加刪除備叢集文件的邏輯,讓新搭建的備叢集儲存的訂單資料與訂單中心線上資料庫中的資料量保持一致。同時使用ZK在查詢服務中做了流量控制開關,保證查詢流量能夠實時降級到備叢集。在此,訂單中心主從叢集完成,ES查詢服務穩定性大大提升。

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

5、現今:實時互備雙叢集階段

期間由於主叢集ES版本是較低的1.7,而現今ES穩定版本都已經迭代到6.x,新版本的ES不僅效能方面最佳化很大,更提供了一些新的好用的功能,所以我們對主叢集進行了一次版本升級,直接從原來的1.7升級到6.x版本。

叢集升級的過程繁瑣而漫長,不但需要保證線上業務無任何影響,平滑無感知升級,同時由於ES叢集暫不支援從1.7到6.x跨越多個版本的資料遷移,所以需要透過重建索引的方式來升級主叢集,具體升級過程就不在此贅述了。

主叢集升級的時候必不可免地會發生不可用的情況,但對於訂單中心ES查詢服務,這種情況是不允許的。所以在升級的階段中,備叢集暫時頂上充當主叢集,來支撐所有的線上ES查詢,保證升級過程不影響正常線上服務。同時針對於線上業務,我們對兩個叢集做了重新的規劃定義,承擔的線上查詢流量也做了重新的劃分。

備叢集儲存的是線上近幾天的熱點資料,資料規模遠小於主叢集,大約是主叢集文件數的十分之一。叢集資料量小,在相同的叢集部署規模下,備叢集的效能要優於主叢集。

然而線上上真實場景中,線上大部分查詢流量也來源於熱點資料,所以用備叢集來承載這些熱點資料的查詢,而備叢集也慢慢演變成一個熱資料叢集。之前的主叢集儲存的是全量資料,用該叢集來支撐剩餘較小部分的查詢流量,這部分查詢主要是需要搜尋全量訂單的特殊場景查詢以及訂單中心繫統內部查詢等,而主叢集也慢慢演變成一個冷資料叢集。

同時備叢集增加一鍵降級到主叢集的功能,兩個叢集地位同等重要,但都可以各自降級到另一個叢集。雙寫策略也最佳化為:假設有AB叢集,正常同步方式寫主(A叢集)非同步方式寫備(B叢集)。A叢集發生異常時,同步寫B叢集(主),非同步寫A叢集(備)。

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?


ES 訂單資料的同步方案

MySQL資料同步到ES中,大致總結可以分為兩種方案:

  • 方案1:監聽MySQL的Binlog,分析Binlog將資料同步到ES叢集中。

  • 方案2:直接透過ES API將資料寫入到ES叢集中。

考慮到訂單系統ES服務的業務特殊性,對於訂單資料的實時性較高,顯然監聽Binlog的方式相當於非同步同步,有可能會產生較大的延時性。且方案1實質上跟方案2類似,但又引入了新的系統,維護成本也增高。所以訂單中心ES採用了直接透過ES API寫入訂單資料的方式,該方式簡潔靈活,能夠很好的滿足訂單中心資料同步到ES的需求。

由於ES訂單資料的同步採用的是在業務中寫入的方式,當新建或更新文件發生異常時,如果重試勢必會影響業務正常操作的響應時間。

所以每次業務操作只更新一次ES,如果發生錯誤或者異常,在資料庫中插入一條補救任務,有Worker任務會實時地掃這些資料,以資料庫訂單資料為基準來再次更新ES資料。透過此種補償機制,來保證ES資料與資料庫訂單資料的最終一致性。

遇到的一些坑

1、實時性要求高的查詢走DB

對於ES寫入機制的有了解的同學可能會知道,新增的文件會被收集到Indexing Buffer,然後寫入到檔案系統快取中,到了檔案系統快取中就可以像其他的檔案一樣被索引到。

然而預設情況文件從Indexing Buffer到檔案系統快取(即Refresh操作)是每秒分片自動重新整理,所以這就是我們說ES是近實時搜尋而非實時的原因:文件的變化並不是立即對搜尋可見,但會在一秒之內變為可見。

當前訂單系統ES採用的是預設Refresh配置,故對於那些訂單資料實時性比較高的業務,直接走資料庫查詢,保證資料的準確性。

日均5億查詢量的京東訂單中心,為什麼舍MySQL用ES?

2、避免深分頁查詢

ES叢集的分頁查詢支援from和size引數,查詢的時候,每個分片必須構造一個長度為from+size的優先佇列,然後回傳到閘道器節點,閘道器節點再對這些優先佇列進行排序找到正確的size個文件。

假設在一個有6個主分片的索引中,from為10000,size為10,每個分片必須產生10010個結果,在閘道器節點中匯聚合併60060個結果,最終找到符合要求的10個文件。

由此可見,當from足夠大的時候,就算不發生OOM,也會影響到CPU和頻寬等,從而影響到整個叢集的效能。所以應該避免深分頁查詢,儘量不去使用。

3、FieldData與Doc Values

FieldData

線上查詢出現偶爾超時的情況,透過除錯查詢語句,定位到是跟排序有關係。排序在es1.x版本使用的是FieldData結構,FieldData佔用的是JVM Heap記憶體,JVM記憶體是有限,對於FieldData Cache會設定一個閾值。

如果空間不足時,使用最久未使用(LRU)演算法移除FieldData,同時載入新的FieldData Cache,載入的過程需要消耗系統資源,且耗時很大。所以導致這個查詢的響應時間暴漲,甚至影響整個叢集的效能。針對這種問題,解決方式是採用Doc Values。

Doc Values

Doc Values是一種列式的資料儲存結構,跟FieldData很類似,但其儲存位置是在Lucene檔案中,即不會佔用JVM Heap。隨著ES版本的迭代,Doc Values比FieldData更加穩定,Doc Values在2.x起為預設設定。

總結

架構的快速迭代源於業務的快速發展,正是由於近幾年到家業務的高速發展,訂單中心的架構也不斷最佳化升級。而架構方案沒有最好的,只有最合適的,相信再過幾年,訂單中心的架構又將是另一個面貌,但吞吐量更大,效能更好,穩定性更強,將是訂單中心繫統永遠的追求。

作者介紹

張sir,京東到家研發工程師,主要負責訂單中心、商家中心、計費等系統。

本文轉載自DBAPlus。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562044/viewspace-2650394/,如需轉載,請註明出處,否則將追究法律責任。

相關文章