天天看點

購物車從 Cassandra 遷移到 Mongo,以增強購物體驗-譯文

作者:閃念基因

介紹

購物車是電子商務業務中的關鍵組成部分。順暢的結賬流程可增強使用者對平台的信心。鑒于購物車在保持使用者購買意向方面的重要性,我們需要確定購物車系統高度穩定、性能卓越、容錯能力強且能适應高流量。

Myntra 的使用者群持續快速增長。平台上的使用者越多,建立的購物車就越多,每個購物車的操作也越多。購物車資料儲存在 Cassandra 資料存儲中。Cassandra 是購物車持久化的有力選擇,因為它在寫入操作和容錯能力方面具有巨大的可擴充性。然而,為什麼它不是我們完美的選擇以及我們向 MongoDB 遷移的過程正是我們将在這篇博文中讨論的内容。

從 Cassandra 遷移到 MongoDB 的決定

在流量高峰期,購物車服務開始出現約 0.05% 的錯誤率,這是由于逾時和延遲增加約 1 秒所緻。Cassandra DB 的延遲超過 250 毫秒。這導緻大量使用者無法“添加到購物車”,并導緻我們的收入下降。以下是 Cassandra DB 的一些主要觀察結果。

Cassandra 存在的問題

我們使用 Cassandra 作為主要資料庫來儲存使用者購物車。雖然最初考慮到高可用性和可擴充性,這樣做是合理的,但最近我們開始注意到不少性能問題影響了購物車應用程式的整體穩定性。

  • 墓碑導緻高讀取延遲:在購物車中,我們有近 35% 的操作是更新(覆寫)、删除和 TTL 過期,這導緻 Cassandra 中的墓碑數量非常高。購物車上的每個删除/更新操作都會産生 16 個墓碑。當墓碑大小增加太多時,會導緻讀取查詢速度變慢,因為資料庫需要掃描大量墓碑才能獲得實時資料。在我們的案例中,這導緻延遲峰值超過 250 毫秒,進而增加了我們的 SLA 和服務性能。為了解決這個問題,我們需要定期修複和壓縮資料庫,尤其是在 HRD 事件之前。
  • 使用批處理語句:在 Cassandra 中建立的多個查找表導緻每次建立、合并或删除購物車時至少需要 4-5 個查詢來讀取和更新表。這些批處理語句用于保持一緻性。這增加了流向資料庫的網絡流量,增加了整體網絡帶寬使用率,并增加了延遲。
  • 對非列的支援:缺乏對表中計數器列和非計數器列的支援。我們需要一個單獨的表來跟蹤産品數量,這增加了管理多個更新的複雜性。

Cassandra 改進

幾乎沒有做出任何改進來糾正這些問題。

  • GC 寬限期縮短:為了減少墓碑累積,GC 寬限期從預設的 10 天縮短至僅 6 小時,進而加快了墓碑删除速度并提高了性能。我們使用仲裁作為一緻性,這確定了我們不會出現由于墓碑在壓縮之前未傳播到副本而導緻已删除資料重新出現的問題。
  • GC 長時間暫停:觀察到頻繁的系統停頓和讀取延遲峰值,與持續 2 到 3 秒的 GC 長時間暫停相吻合。啟用 GC 日志記錄并分析 GC 性能有助于識别此問題。識别和解決長時間 GC 暫停後,系統穩定性得到增強,讀取延遲峰值減少。
  • 堆外緩存:為了解決性能問題,重新配置了 Cassandra 中的緩存設定。key_cache_size_in_mb 和 row_cache_size_in_mb 分别從 2GB 減少到 512MB 和從 4GB 減少到 512MB,而 file_cache_size_in_mb 從 8GB 增加到 18GB。使用塊緩存等堆外緩存選項來并行緩存從磁盤讀取的 SS 表部分。重新配置緩存設定和利用堆外緩存提高了緩存使用效率并加快了讀取操作。
  • 調整布隆過濾器大小:布隆過濾器通過避免在 SS 表中進行不必要的檢查來幫助加快讀取操作,經過調整以優化其在記憶體中的大小。優化記憶體中的布隆過濾器大小通過避免在 SS 表中進行不必要的檢查來加速讀取操作。
  • 壓縮政策:根據工作負載選擇合适的壓縮政策至關重要。預設的 Size Tiered Compaction 政策适用于寫入密集型工作負載,而 Leveled Compaction 可能更适合讀取密集型工作負載。根據工作負載選擇合适的壓縮政策需要優化資料存儲和查詢響應時間。

總體而言,這些措施使 Cassandra 系統更加高效和穩定,減少了停頓、提高了緩存使用率,并針對特定工作負載優化了壓縮政策。實施這些更改後,我們能夠支援之前 1.5 倍的負載。但是,這些措施不足以解決我們的問題,因為我們的資料庫本質上是瞬态的,需要針對讀取和删除進行高度優化的解決方案。

選擇 Mongo

在為 Cart 選擇資料存儲時,需要考慮一些主要因素。強一緻性、對二級索引的支援等有助于我們評估 Mongo 是否是正确的選擇。

  • Cart 是一個讀取量很大的系統,讀取量與寫入量之比為 3:1。可以通過向 MongoDB 叢集添加更多從屬節點來處理讀取量較大的負載。
  • MongoDB 支援文檔中任何字段的二級索引,這使其更适合某些購物車用例。
  • Cassandra 節點需要定期進行修複和壓縮操作,而 MongoDB 叢集可以避免這些操作。
  • Mongo 是一種低維護伺服器,與支援相同流量所需的 Cassandra 節點相比,其節點數量較少。我們将占用空間減少了 40%。
  • 與 Cassandra 相比,Mongo 可以輕松地将業務資料映射到具有可選屬性的模式。

在推進 MongoDB 的過程中,我們還考慮了一些其他因素,

  • 整個購物車資料都存儲為一個文檔。雖然這可以優化一次性加載整個購物車的讀取,但這可能會增加磁盤資料吞吐量,因為每次讀取或寫入都會産生大量 IO。但是,對于加載整個購物車的工作負載,此模型可以通過内部資料庫緩存和作業系統頁面緩存提供更好的性能。
  • 結賬操作需要高度一緻的資料,是以整個購物車的結賬操作都需要由主節點支援

資料模組化和模式設計

舊 Cassandra 架構

舊 Cassandra 模式的一個主要問題是使用多個查找表來擷取資料。要通路購物車,系統必須執行兩次讀取調用:一次是查找表以擷取購物車 ID,另一次是查找表以擷取相關資料。這導緻每次購物車讀取操作至少需要兩次讀取調用。執行任何插入或删除操作時,我們必須至少執行 2 次寫入調用。

Mongo 模式

在新的 Mongo 架構中,我們進行了一些更改以糾正上述問題。我們決定不再使用查找表來擷取購物車 ID,而是使用購物車 ID 的明确使用者辨別符部分,這樣就無需使用多個查找表。

  • Logged-In-Cart:此文檔存儲購物車 ID 和會話 ID 以及與購物車相關的資料。決定将會話 ID 明确存儲在登入購物車文檔中是為了減少合并操作的數量。
  • Non-Logged-In-Cart:此文檔包含未登入使用者的購物車。此購物車的有效期與已登入的購物車不同。我們不會在此購物車中存儲與使用者相關的資料。購物車合并過程完成後,此購物車将被删除。

購物車合并流程

當未登入的使用者建立購物車時。然後客戶登入到他/她的 Myntra 帳戶,然後客戶希望在登入後顯示來自客戶未登入購物車和登入購物車的商品。合并登入購物車和未登入購物車的過程稱為購物車合并。

Cassandra 與 Mongo DB 處理

Cassandra 流

購物車從 Cassandra 遷移到 Mongo,以增強購物體驗-譯文

Mongo 流

購物車從 Cassandra 遷移到 Mongo,以增強購物體驗-譯文

MongoDB購物車過期TTL解決方案

系統為登入和未登入的使用者購物車維護一個集合,每個集合具有不同的 TTL(生存時間)。到期日期設定為 0,實際到期時間通過應用程式使用 TTL 的日期時間格式進行管理。TTL 螢幕定期掃描通過 TTL 建立的 Mongo 索引并識别需要删除的到期文檔。檢查點每 50 秒發生一次,在接下來的 10 秒内,螢幕會要求 MongoDB 線程使盡可能多的文檔到期。

如果未在 10 秒内處理,未過期的文檔會堆積在堆棧中,然後 MongoDB 線程會被要求批量過期它們。過期後,會觸發重新索引過程。為了最大限度地減少高索引期間的停機時間,文檔的過期時間安排在午夜,此時整體流量較低。這種方法減少了建立的索引數量,并確定所有過期時間都發生在非高峰流量時段。

MongoDB 規模基準測試

基準測試的目标是确定正确的硬體和軟體配置來處理購物車流量,同時牢記購物車應用程式與 Cassandra 相比的一緻性要求。

  • 第一步是确定要進行基準測試的 NFR
  • 滿足未來需求的并發請求數
  • 每個 API 的預期吞吐量
  • 考慮所有正在進行的功能以實作規模
  • 預期延遲水準
  • 确定與 Mongo 一起用于基準測試練習的資料模型
  • 确定硬體配置 -
  • 核心數
  • 考慮工作負載的熱資料(例如 25–30%)的 RAM
  • 估計存儲容量
  • 需要啟用緩存的硬碟
  • 确定 MongoDB 配置。
  • Mongo 版本
  • 主站、從站設定(開始時為 1 個主站、2 個從站)
  • 檢查點 — 每 1 分鐘
  • 日志同步 — 每 100 毫秒
  • 緩存配置
  • 一緻性級别
  • 根據目前的流量模式确定工作負載。
  • 重度讀取,讀取:寫入比率為 3:1
  • 準備示例資料集
  • 确定用戶端配置
  • 連接配接池大小
  • 執行不同的工作負載運作
  • 考慮所有讀取
  • 考慮混合負載
  • 考慮不同的一緻性級别
  • 捕獲應用程式和資料庫名額
  • 延遲、吞吐量
  • CPU 使用率、RAM 使用率
  • 磁盤每秒讀取和寫入的 IOPS 數
  • 磁盤 IO 使用率
  • 磁盤 IO 吞吐量
  • P99 和 p99.9 延遲

我們能夠對~170 萬 RPM 和延遲~50ms(p99.9)進行基準測試

遷移政策

以下是移民戰略設定的目标,

  • 零停機時間
  • 對交通零影響
  • 啟用 AB 來路由流量

啟用 Cassandra 和 Mongo 之間的雙向雙同步

在執行一次性遷移之前,我們在 Cassandra 和 MongoDB 之間建立了資料同步管道。對 Cassandra 的任何寫入都将同步到 MongoDB,反之亦然。這需要異步處理,這樣才不會影響流入的使用者請求的寫入延遲。處理所有競争條件、故障和重試,以便兩個資料存儲之間的資料始終保持同步。

作為雙同步過程的一部分,在一個資料庫中執行的所有操作都将同步到另一個資料庫。建立了從主資料庫到輔助資料庫的基于 Kafka 的管道。當使用者執行任何操作時,都會建立一個日記條目,以捕獲他/她所執行的操作。在 Kafka 管道中建立并釋出事件。資料庫中的這個日記條目確定我們不會在沒有使用輔助資料庫中的事件的情況下切換使用者配置設定的資料庫。一旦消費者使用了該事件,資料庫條目就會被删除。如果我們找不到與使用者相關的任何條目,那麼使用者就有資格進行遷移。

一次性資料遷移

該計劃涉及一次性将完整的購物車資料集從 Cassandra 遷移到 MongoDB。這将使用批處理應用程式完成,該應用程式讀取查找表并通過查詢 Cassandra Cart 表擷取最新資料來移動與每個條目相關的購物車。為防止重複遷移,已認證雙同步遷移的記錄将不會進行批量遷移。

使用 A/B 進行增量流量路由

釋出功能門控和使用者驅動 A/B 的一次性遷移配置。啟用 A/B 以便流量流入 MongoDB 叢集(例如 1% 的使用者)。一旦沒有報告問題,就向 100% 的使用者推出。

確定容錯能力

雙同步日志

  • 為了確定在雙同步或一次性遷移期間不會丢失資料,我們決定維護一個日志。這個日志的理念是,每當在使用者的主資料庫(可以是 Cassandra 或 Mongodb)上執行寫入操作時,我們都會在日志中維護該事件,直到該資料同步到另一個資料庫。
  • 讓我們考慮這樣一種情況,如果使用者對他們的購物車進行了一些更改,并且在此期間我們為該使用者啟用 AB,那麼可能會丢失一些資料,因為雙同步是一個異步過程,是以需要一些時間來複制整個資料。
  • 是以,每當使用者嘗試通路他們的購物車時,我們首先調用日志并檢查該使用者是否已經存在事件,我們獲得主資料庫(即上次寫入發生的位置),該資料庫也在該模式中維護。
  • 一旦雙同步作業完成,該事件就會從日志中删除。
  • 此外,我們在日志中隻維護任何使用者的單個事件,這意味着如果使用者連續執行 2 次寫入,即使第一次寫入未複制到輔助資料庫,我們也将隻保留最新寫入的事件,這確定始終将最新資料同步到其他資料庫。
  • 對于所有未能在 Kafka 中釋出的記錄(無論由于何種原因),同步作業将擷取與更改相關的資料庫條目,然後根據這些條目更新第二個資料庫。
  • 在一次性遷移期間,我們有一項工作就是不斷對随機使用者的資料庫資料進行采樣,以查找任何差異。這非常重要,因為我們對兩個資料存儲中的架構設計進行了重大更改。
購物車從 Cassandra 遷移到 Mongo,以增強購物體驗-譯文

名額、警報和監控

以下是一些有助于更快解決問題的名額。

一次性資料遷移名額

  • 一次性遷移(速率,總體)
  • 一次性遷移中的錯誤(比率,總體)
  • 資料驗證錯誤(率,總體)

雙同步名額

  • 更新事件釋出(速率,總體)
  • 更新事件消耗(速率,總體)
  • 出版錯誤、消費(率、總體)

部署政策

MongoDB 是“暗釋出”的,以避免對傳入流量造成任何風險。按順序推出對于無縫遷移到 Mongo 發揮了重要作用。

  • 首先,我們啟用雙同步
  • 然後我們啟用同步日志(資料庫決策表,它将確定您的資料庫在資料完全遷移之前不會發生變化)
  • 對現有購物車進行一次性遷移(所有未通過雙同步移動的購物車)
  • 協調一次性遷移中的所有錯誤
  • 打開功能門控
  • 為一小部分使用者開啟 A/B 功能,以将流量路由到 Mongo
  • 監控儀表闆是否存在任何異常
  • 修複兩個資料存儲之間的任何同步相關問題
  • 逐漸增加 A/B 使用者
  • 覆寫流向 Mongo 的 100% 使用者流量
  • 清理 Cassandra 中的所有現有資料
  • 一旦穩定,計劃退役 Cassandra 節點

結論

現在已經過去了幾個月,自遷移以來我們沒有遇到任何問題,并且我們已成功解決了我們在 Cassandra 中觀察到的所有性能瓶頸。數千萬輛購物車已遷移,延遲從 250 毫秒(高峰期間)改善到 < 25 毫秒。通過更好的架構設計和 Mongo DB 的讀取性能,讀取性能已大幅提高。這還将我們的 硬體占用空間減少了 40%,并且消除了 Cassandra 所需的大量維護和營運成本。但我們從不想排除這樣一個事實,即從購物車演變之初就采用更好架構的 Cassandra 可以實作比我們更好的性能。

總的來說,通過擴充 Mongo 節點,我們現在有能力處理高達目前 3 倍的流量,之後我們才需要重新考慮未來的下一個最佳選擇。

作者:Parul Agnihotri-Myntra Engineering

Co-Contributors: Shubham Shailesh Doijad, Pramod MG,Ajit Kumar Avuthu, Vindhya Shanmugam, Subhash Kumar Mamillapalli

出處:https://medium.com/myntra-engineering/cart-migration-from-cassandra-to-mongo-for-an-enhanced-shopping-performance-104c5d3b0da7

繼續閱讀