攜程門票秒殺系統的設計與實踐
作者簡介
Liang,攜程技術專家,專注系統性能、穩定性、承載能力和交易質量,在技術架構演進、高并發等領域有豐富的實踐經驗。
團隊開放崗位:后端開發-資深/專家(海外交易系統)、資深后端開發專家-BMS
本文概述了攜程門票預訂交易系統在應對秒殺活動中面臨的挑戰與應對策略。第一部分闡述了業務激增對系統架構的考驗;第二部分深入剖析了系統架構的優化路徑,涵蓋讀熱點、寫入性能瓶頸、強一致性事務處理及流量精細化控制等關鍵問題的解決方案,并總結了確保系統高可用性與持續性的治理措施。希望這些內容能夠對大家有所幫助或啟發。
一、背景
后疫情時代旅游行業快速復蘇,各類營銷秒殺活動變得越發頻繁,面對億級流量的沖擊,系統架構面臨挑戰。研發團隊需要保障大流量下的功能穩定性,為國內外用戶提供流暢的預訂體驗,因此需要對核心的預訂交易系統進行應用架構升級,從而確保系統在高并發情況下仍能穩定高效運行。
本文將介紹在應對流量高峰、突破系統瓶頸、強化系統穩定性等方面的應對策略與優化效果。
二、秒殺活動案例分析
回顧大家曾經參與過的秒殺或大促活動,如雙十一、618、12306節假日搶票、演唱會搶票時,會有相似的感受:
1) 緊張刺激:活動通常定時開售,期待與緊張并存。
2) 系統壓力:在高峰期,系統容易出現卡頓、宕機或提示“太火爆”或需要排隊等待,讓人倍感焦慮。
3) 結果未知:盡管全力以赴,但結果往往不盡如人意,有時搶到了票無法支付或者可能被退單。
這些活動在預訂交易系統中也會呈現相似的特征:
1) 大流量、高并發:大流量、高并發、強事務性,對系統性能提出嚴峻挑戰。
2) 時間敏感性:準時開售,用戶爭搶熱點資源,系統需要確保實時、準確地響應。
3) 履約保障:從訂前到訂后,系統需要確保履約的順利進行,避免用戶因系統問題而遭受損失。
與傳統電商相比,攜程門票交易系統具有兩大特點:
1) 強一致性:用戶預訂后保證出票且盡可能快速確認,確保每一筆交易都能履約。
2) 多維度和跨商品組合限購:限購規則復雜多變,例如多維度和跨商品組合限購,保障每位用戶有公平購票的機會,避免囤票行為。
接下來回顧歷史上有過的攜程門票大型秒殺/活動案例。
1) 2020年8月8日~9月1日:“惠游湖北”活動,攜程獨家承辦,首次面對日常流量45倍 (數十萬QPS) 峰值的流量挑戰,雖然剛開始系統出現不穩定的情況,但最終還是成功應對。
2) 2021年9月14日:北京環球影城開業開售活動,攜程門票在與其他友商的同期競爭中,成為唯一穩定出票且銷量最高的交易平臺。
3) 2023年9月15日:武漢動物園開園,在供應商系統出現異常、友商頁面卡頓有大量退單的情況下,攜程門票預訂依然能保持順暢下單。
4) 2024年4月10日:IU(李知恩)全球演唱會門票在Ctrip.com和Trip.com國際站同時秒殺,攜程門票再次表現穩定,預訂過程絲滑流暢,10秒內售罄。
以下是部分歷史秒殺活動峰值流量與日常峰值流量的對比數據:
數據顯示出活動的流量激增通常遠超系統日常處理的極限,如果沒有針對預訂交易系統進行優化,用戶可能會遇到各種問題,例如:
1) 頁面打開慢、卡頓、宕機:直接影響用戶購物體驗,系統會出現Redis或DB超負載,供應商接口不穩定等情況。
2) 付款后不能確認/退款:付款后,無法及時確認訂單狀態或進行退款操作,系統出現庫存超賣/少賣等情況。
要避免出現上述情況,就要求系統具備高度的可擴展性和靈活性,同時在架構、緩存、數據庫、流量控制等多方面進行全面優化。接下來我們通過具體場景來分析系統遇到的問題和應對策略,了解系統架構設計與演進過程。
三、系統架構設計與演進
整體而言,預訂交易系統的目標是:穩、準、快。
- 穩:確保系統穩定可靠,保障售賣流程無間斷。
- 準:實現數據一致性,確保履約準確無誤。
- 快:提供流暢的預訂體驗,實現快速確認。
在大流量高并發場景下,要達到這些目標就可以進行有針對性的改造升級,接下來展開闡述。
3.1 系統穩定性挑戰與應對策略
當系統遇到洪峰流量時,容易出現頁面打開慢、卡頓等問題,主要原因有以下幾點:
1) Redis超負載與緩存熱點。
2) 數據庫超負載。
3) 供應商系統不穩定。
接下來針對這3個常見問題,闡述相應的應對策略。
問題一:Redis超負載與緩存熱點
當Redis面臨負載問題時,可以使用水平擴容這種常規手段讓流量分攤到更多實例。然而擴容雖能降低大多數實例的CPU使用率,但在處理特定熱點數據時,各實例的CPU使用率仍然可能出現不均衡的情況,即緩存熱點問題;此外還會存在緩存大Key問題。
1) 緩存熱點問題
如下圖所示,node-1 節點存在2個熱點訪問,請求量遠高于其他節點。緩存熱點會導致實例負載不均衡,從而嚴重影響響應速度。
緩存熱點應對方案:熱點識別自動構建多級緩存
將單位時間內高頻訪問的Key,識別出來。例如:同一個Key,1秒內單機訪問10次。
如上圖所示,自動發現Hot keys或將指定的Key加入到本地緩存。
秒殺時:短暫的本地緩存可以減少Redis單實例熱點,對數據的一致性不會有較大影響。
優化效果:開啟多級緩存后,同一個緩存key訪問性能明顯提升,對應Redis訪問量也明顯降低(如下圖所示)。
2) 緩存大Key問題
緩存大key的危害主要包括:阻塞請求、內存占用大、阻塞網絡等。不僅會降低Redis的性能,還可能影響整個系統的穩定性(如下圖所示)。
通過memtier-benchmark工具在生產環境下壓測:200KB以上比10KB以內的性能慢3倍,吞吐能力也下降76%(如下圖所示)。
緩存大Key應對方案:
a)精簡緩存對象:去除緩存中的冗余字段。
b)壓縮緩存對象:采用壓縮比更高的壓縮方式,縮小緩存對象。
c)拆分大Key:若精簡和壓縮后還是過大,根據業務邏輯,將大Key拆分成多個小Key。(注意拆分后IO次數會增加,高負載下性能不一定會變好,需要根據壓測結果來評估最終性能)
d)長期治理:建立長期治理機制,定期掃描Redis中的大Key,每周跟進,將隱患在日常治理中消除。
優化效果:在大Key優化后,Redis查詢性能有較為明顯的提升(如下圖所示,緩存查詢耗時從300μs優化至100μs)。
問題二:數據庫超負載
系統中商品信息的變更往往伴隨著緩存失效的問題,尤其在高并發和秒殺場景下,大量請求可能直接穿透緩存層,對數據庫造成巨大壓力,甚至引發性能故障。
緩存更新策略優化:應對商品變更導致的數據庫壓力
1) 常見的緩存架構設計問題
監聽器收到消息后刪除相應的緩存Key。這種方式在一般情況下是有效的,但在高并發和大流量場景下,它存在幾個突出的問題:
a) 緩存擊穿:由于緩存的Key被立即刪除,大量請求在緩存未更新之前會直接訪問數據庫,導致數據庫壓力驟增。
b) 消息處理延遲:在高并發場景下,消息處理可能產生延遲,導致緩存更新不及時,進一步加劇數據庫壓力。
2) 緩存更新策略的優化
為了應對這些挑戰,采取了一系列優化措施,主要包括:
a) 緩存覆蓋更新策略:替代直接刪除緩存Key的做法,采用了緩存覆蓋更新策略。當商品信息發生變更時,系統不再刪除緩存Key,而是直接更新該Key對應的緩存值。避免了流量穿透到底層數據庫。
b) 消息聚合:針對商品變化消息量過大的問題,引入了消息聚合機制。將商品多次變化消息在一段時間窗口內合并成一個,減少消息處理的頻率。
c) 異步更新緩存:為了進一步降低對數據庫的實時壓力,采用了異步更新緩存的策略。當商品信息發生變更時,系統不會立即更新緩存,而是將更新任務放入一個異步隊列中,由后臺線程異步處理。
緩存更新策略變化如下圖所示:
問題三:供應商系統不穩定
供應商系統因大流量導致響應緩慢或被限流,影響整體系統的穩定性。
應對供應商系統不穩定性的技術策略優化
當供應商系統面臨大流量沖擊時,往往會出現響應緩慢甚至被限流的情況,這直接影響了我們自身系統的穩定性和用戶體驗。
供應商訂單對接問題
當與供應商進行訂單對接時,可能會遇到以下問題:
a)被供應商限流:在高并發場景下,供應商系統可能會對我們限流。這會導致我們的訂單提交受阻,影響業務流轉。
b)供應商系統不穩定:由于各種原因,供應商系統可能會出現不穩定的情況,導致訂單處理延遲或失敗。
為了緩解上述問題,我們采取以下技術策略:
1)削峰填谷/緩沖池:利用消息隊列作為訂單提交的緩沖池,將訂單信息先寫入隊列,再由后臺服務異步處理。這樣可以將訂單提交的高峰流量削平,減少對供應商系統的瞬時壓力。
2)禁售策略
? 自動禁售:建立對供應商系統的健康度監控機制,實時監測其響應速度、錯誤率等指標。一旦發現供應商系統出現不穩定或限流的情況,及時觸發禁售策略。
? 定期重試:對于因供應商系統問題而失敗的訂單,設定了一個重試機制,定期嘗試重新提交。同時,根據供應商系統的恢復情況,動態調整重試的頻率和次數。
優化效果:通過實施上述技術和策略優化,可以有效確保供應商系統能力不影響下單吞吐量(如下圖所示)。
上述的優化措施落地后能夠提升系統的穩定性,然而鑒于流量的不確定性,即使流量超過系統負載能力,系統也要正常運行,因此仍然需要有相應的流量控制策略。
流量控制策略優化:確保秒殺活動穩定運行
如下圖所示,不同頁面對應的流量和系統(承載能力)是不同的,需要控制好每個過程的流量,確保整體系統的穩定性。
以70萬人購買5000張票的秒殺活動為例,可采取以下限流策略:
1) SOA限流:接口與應用級限流
通過服務治理框架對服務接口進行限流(SOA限流),在秒殺/活動等場景會影響到其他商品的正常售賣。對此可針對秒殺活動的特殊需求,設計自定義的限流策略,如按秒殺商品限流、頁面級限流等,細化商品維度的流量控制。
2) 自定義限流:商品級限流
a)針對單個秒殺商品設置獨立的限流閾值,即使某個商品超負載,也不會影響整體系統的可用性。
b)同時,對于未知的秒殺突增流量,也可以支持熱點商品自動限流,與Redis 熱Key 發現類似,自動識別熱點訪問的商品,并添加到商品級限流中,從而確保整體系統的穩定運行。
如下圖所示,我們采用了商品維度的自定義限流策略,該策略將1秒內的請求流量劃分為10個獨立的100毫秒(可配置)滑動窗口。每個窗口都會平分一部分流量,以確保下游服務的并發量得到有效控制。這種方法不僅降低了下游服務的壓力,也為用戶提供更加均衡的流量分配。
結合商品級限流能力,控制進入每一個頁面的流量,形成多層次的限流防護體系,根據秒殺庫存預估售賣時長,控制進入到每一個頁面的流量比例,這樣也能夠大幅減少服務器資源投入。
優化效果:自定義限流可控制進入每一個頁面的流量,超負載也不影響整體的可用性,服務器資源的投入也可控。
本部分闡述了系統穩定性的挑戰及優化,包括Redis超負載與緩存熱點、數據庫超負載、供應商系統不穩定等。通過熱點識別自動構建多級緩存、緩存覆蓋更新策略、削峰填谷/緩沖池、自定義限流等多種技術策略,使得系統穩定性問題得到有效解決。
3.2 寫數據一致性挑戰與應對策略
下單過程中的庫存扣減的精確執行,這種數據一致性的實現效果會直接影響訂單是否能夠成功履約,而傳統關系型數據庫的并發更新存在顯著瓶頸,因此需要專項優化。
扣減庫存問題:性能瓶頸 – MySQL熱點行扣減庫存(行級鎖)。
技術策略:扣減庫存異步化,異步扣庫存主要分3步(見下圖):
1)初始化:秒殺商品設置好活動場次,將秒殺庫存同步至Redis。
2)扣庫存:活動開始時,先從Redis扣減庫存,在通過消息通知異步扣減DB庫存,削除DB更新同一行的峰值。
3)還庫存:如果有退訂,先判斷DB中是否有扣減記錄,如果有,則先退DB再退Redis;如果沒有,重試多次。
扣還庫存過程中也會存在超時等未知情況,此處細節過多不再展開。按照業務“可少買不超賣”的原則,即使在這個過程中數據可能存在短暫的延時,但能夠確保最終一致性。
優化效果:庫存扣減異步化,消除行級鎖瓶頸。現在系統能夠輕松支撐數十萬單/分鐘交易流量。
3.3 實現高可用的可持續性
系統是不斷演進的,如何保持并持續優化系統能力就成為新的課題。因此日常架構健康度持續治理、以及大型活動和節假日保障體系是實現高可用“可持續性”的關鍵。
3.3.1 架構健康度治理
基于架構健康度實現系統質量的量化管理,實現研發生命周期各個環節的跟蹤和優化,如下圖所示可細分為三部分:
a) 系統運行健康度:通過系統各個維度運行時的健康狀態和問題來反映系統質量。
b) 架構設計健康度:服務數量、調用關系的復雜度、循環依賴、調用層級過深等因素都會影響系統的穩定性和性能。
c) 工程化健康度:基于應用的工程質量和效率狀態,反應出開發的工程化水平。
3.3.2 大型活動和節假日保障體系
無論大型活動還是節假日,都需要提前準備好應急預案,做好壓測,提前保證系統的高可用。
四、總結
本文總結了攜程門票的預訂交易系統在承接秒殺活動中面臨的挑戰與應對策略。重點解決了讀熱點、寫瓶頸、強事務、流量控制等諸多細節問題,同時通過日常的架構健康度治理和制定專項的保障計劃,持續對系統進行優化,確保系統在高負載下依然能夠穩定運行,實現系統的持續高可用。