首頁>技術>

架構設計的典型案例——訂單系統

如圖5.10所示,訂單系統作為電商系統的“中軸線”,貫穿了整個電商系統的全部流程,電商系統中的核心繫統都是圍繞訂單構建的,訂單系統也是隨著電商行業的發展逐漸演變的。

圖5.10

訂單系統用於管理訂單型別和訂單狀態,收集商品、優惠、使用者、收貨資訊、支付資訊等一系列訂單實時資料,並進行庫存更新、訂單下發等一系列動作。訂單系統業務的基本模型涉及使用者、商品(庫存)、訂單、付款,訂單基本流程是下訂單→減庫存,下訂單和減庫存必須同時完成,否則,若下了訂單但是沒減庫存,就會導致超賣,使商家庫存不足,消費者下了訂單卻買不到東西,體驗不佳;若減了庫存但是沒有生成訂單,就會導致少賣,使商家庫存積壓或者反覆修改商品資訊,體驗也不佳。

圖5.11

1.訂單欄位

訂單欄位包含需要在訂單中記錄的資訊,主要用於其他系統如商品系統、物流系統、支付系統等的溝通,為下游系統提供資訊依據,如圖5.12所示。

圖5.12

2.訂單記錄資訊

訂單號作為訂單識別的標識,往往由一串數字組成,根據訂單的增加進行自增,也可以在設計訂單號時考慮訂單的加密設定(否則其他人透過訂單編號就能計算銷售量)。訂單號後續用作訂單的唯一標識,用於WMS(倉存管理系統)和TMS(運輸管理系統)時的訂單識別。

3.使用者資訊

使用者資訊指購買人的相關資訊,主要包括姓名、地址和手機號。

在 WMS(倉儲管理系統)和TMS(運輸管理系統)中,地址資訊會被用於區分割槽域和配送安排。

4.商品資訊

商品資訊一般包括商品的基本資訊和庫存,主要影響庫存更新和WMS生產。“金額”在邏輯上其實也屬於商品資訊的範疇,但由於其比較特殊,所以本節將其單獨拿出來講解。

其中,金額資訊用於記錄訂單在每個狀態節點的觸發時間。在訂單產生的商品資訊裡除了要記錄最終金額,還要記錄過程金額,比如商品分攤的優惠金額、支付金額、應付金額等。在後續的訂單結算、退換貨、財務等環節中都要用到。時間資訊用於記錄訂單每個狀態節點的觸發時間如建立時間、支付時間和退款時間等。

訂單流程

訂單流程指整個訂單從產生到完成的整個流程,包括正向流程和逆向流程。

1.正向流程

正向流程如圖5.13所示。

圖5.13

訂單流程涉及的環節非常多,有如下細節需要注意。

◎ 釋放庫存的場景:超時未支付、訂單自動取消。

◎ 如果選擇 COD(貨到付款),則支付環節相應地被轉移到訂單配送之後,在該過程中所有與款項相關的邏輯都變為只操作金額數字,不對結算和賬戶進行打款和退款操作。

◎ 金額需要分攤到子商品訂單,方便後期訂單的退款退貨。

◎ 訂單系統稽核的一個作用是對惡意使用者或者刷單情況進行處理。系統可根據白名單、黑名單、消費頻次、促銷品購買量等設計風控規則。如果後續會進入人工稽核環節,則可以在規則上適當放寬。

當觸發規則需要進行訂單退訂操作時,在設計上要考慮對使用者體驗的影響,在前臺文案上說明當前節點為稽核狀態或者等待接單狀態。

◎ 在O2O領域有催單的概念,傳統電商則透過關聯第三方物流資訊進行跟蹤。催單觸發一般會設定一定的時間間隔,在特定的時間內只觸發一次催單請求。

◎ 需要將預售等貨和移倉做成SOA服務,以便在交易頁面計算預計時間和預計到貨時間。移倉處理依賴倉庫的情況,也會涉及後續拆分和合幷包裹的邏輯。

◎ 在生產訂單時要先判斷報缺情況,如果出現報缺問題,則要考慮整單報缺、部分報缺、換貨或者換轉退的情況(庫存、倉促調撥和退款)。報缺情況分為系統報缺和實物報缺,這是相互承接但相對獨立的兩個環節。

◎ 電商系統要考慮 7 天無理由退貨的場景,即在訂單狀態完成後申請退貨。此時主要涉及的是金額上的計算及一些財務程式(如發票等)問題的處理。

2.逆向流程

逆向流程指因訂單取消、退貨及退款等情況引發的整個流程。逆、向流程的觸發主要有如下幾種情況,如圖5.14所示。

圖5.14

(1)使用者自主取消訂單(整單)。

(2)風控系統觸發取消訂單(整單)。

(3)在客服接到客戶投訴仲裁後觸發取消訂單(整單)。

(4)超時未支付取消訂單(整單)。

(5)換貨報缺轉為退單(整單、部分報缺)。

逆向流程觸發的關注點如下。

(1)訂單狀態(某一節點如訂單產生後不允許取消訂單)。

(2)當退單被商家拒絕後需要轉入客服仲裁環節。

(3)部分退款依然享有優惠活動,金額按照分攤進行退款。

3.訂單狀態

下面從訂單狀態的設計目的和存在價值去分析和理解它的設計機制,比如維度及維度的顆粒度大小。

正向流程和逆向流程的維度(各選取主要狀態)如下。

◎ 正向訂單:已鎖定、已確認、已付款、已發貨、已結算、已完成、已取消等。

◎ 正向預售訂單:預付款已付未確認、已確認未付尾款(變更)。

◎ 正向問題單:未確認、未鎖定、未發貨、部分付款、未付款等。

◎ 逆向退單:待結算、未收到貨、未入庫、質檢不透過、部分收貨、已取消、客戶已收貨等。

◎ 逆向換單:完成、已結算、客服已收貨等。

服務物件的維度如下。

◎ 顧客/使用者:待付款、待發貨、待收貨、待評價、買家已付款、交易成功或失敗、賣家已發貨、退款成功、交易關閉等。

◎ ERP 等其他互動系統:已鎖定、已確認、已分倉、已分配、已出庫、已收貨、已完成等。

◎ 客服:等待買家付款、待付款和待發貨的訂單、退款中的訂單、定金已付、買家已付款、賣家已發貨、交易成功、交易失敗、異常訂單。

4.訂單推送

當狀態機發生變化時,需要將對應的變化告知相關人員以便了解

當前訂單的情況,這就是訂單推送的作用。

訂單推送的觸發依賴於狀態機的變化,涉及的資訊有:

◎ 推送物件(使用者、物流人員、商家);

◎ 推送節點(狀態改變)。

訂單系統設計中的挑戰和實踐

下面從訂單系統需求的演變和訂單系統架構的演變這兩方面講講訂單系統設計中的挑戰和實踐。

1.訂單系統需求的演變

訂單系統需求的演變一般會經歷3個階段,如下所述。

第1階段,實現購買流程。

◎ 實現訂單的建立、發貨、確認等資訊閉環。

◎ 支援訂單稽核(在初期支援人工稽核即可)。

◎ 支援在使用者端顯示訂單相關資訊。

◎ 支援促銷金額的計算。

第2階段,提供服務。

◎ 提供訂單分散式服務。◎ 支援跨平臺交易單的生成(即在同一個大交易單內既有商家商品又有自營商品或者多個商家的商品)。

◎ 支援拆單、合單邏輯(配送單、支付單等)。

◎ 提供多樣的訂單推送服務來完善訂單的狀態。

第3階段,支援不同營銷手段下的訂單型別。當平臺發展到足夠大的規模時,提效、穩定就變成了一個重要的話題,平臺應該可以提供不同營銷場景下的訂單如團購、預購、秒殺等。

2.訂單系統架構的演變

隨著業務需求、使用者量及各種系統等因素的改變,訂單系統的架構也要隨之改變。下面講解訂單系統的三代演變及最佳實踐。

第1代訂單系統的架構:簡單粗暴如圖5.15所示,第1代訂單系統的架構簡單粗暴。

圖5.15

在第1代訂單系統的架構中,訂單狀態是在特定的伺服器上進行處理的,服務一旦出現問題,就會造成訂單的丟失,導致訂單流無法進行下去。

第1代訂單系統的缺點如下。◎ 服務單點。

◎ 資料庫單點。

第2代訂單系統的架構:無狀態非同步驅動

如圖5.16所示,第2代訂單系統的架構為無狀態非同步驅動。

圖5.16

在第2代訂單系統的架構中,應用伺服器不再保留訂單狀態,但給資料庫伺服器造成了高頻查詢壓力,資料庫相對比較脆弱。

第2代訂單系統二代的缺點為狀態掃描帶來的服務負載。

第3代訂單系統的架構:佇列模式

如圖5.17所示,第3代訂單系統的架構為佇列模式。

圖5.17

在第3代訂單系統的架構中,訂單的狀態的改變不再依靠高頻查詢資料庫來獲得,而是透過佇列模式來獲得,這很好地減輕了資料庫的壓力。但是在該系統中,System2 成了核心,該模組的維護會變得很複雜,這也是架構設計的關鍵:如果沒有完美的架構,則只能得到一個平衡的架構。

3.系統最佳化的重要實踐

實踐1,重試和補償。

◎ 多個機器重試不能同步,需要隨機跳躍(Jitter)和指數回退(Exponential Back-off)。

◎ 正在重試的服務也可能宕機,需要儲存狀態(State)。

實踐2,冪等性。

◎ 沒收到響應不見得失敗了。

◎ 自己響應了不見得別人以為自己成功了。

◎ 重試時必須帶上唯一的有意義的ID。

◎ 每一個服務的呼叫都必須是冪等的。

◎ 非只讀的服務必須儲存狀態。

實踐3,一致性實踐。

◎ 訂單系統有強一致性需求。

◎ 無單點故障的分散式系統的一致性是難以解決的問題。

◎ 已有Paxos演算法,現有開源系統(例如ZooKeeper)。◎ 可以使用常用的、成熟的關係資料庫來避免單點故障的發生。

◎ 雲端分散式無單點故障的系統。

實踐4,工作流(Workflow)。

◎ 可擴充套件性:無狀態的服務、分散式部署、分散式儲存、工作流狀態。

◎ 可靠性:定時器、重試、冪等性、強一致性的狀態。

◎ 可維護性:工作流的描述與執行Activity的描述相分離,支援非同步觸發。

◎ 支援版本和升級。

資料庫最佳化

下面從資料庫讀寫分離和資料庫分庫分表這兩方面講講如何進行資料庫最佳化。

1.資料庫讀寫分離

資料庫讀寫分離的基本原理是讓主資料庫處理事務性查詢,讓從資料庫處理Select查詢,當然主伺服器也可以提供查詢服務。讀寫分離的最大作用是降低伺服器的壓力。

資料庫讀寫分離的好處如下。

◎ 增加了冗餘。

◎ 增加了機器的處理能力。◎ 對於以讀操作為主的應用,使用讀寫分離是最好的場景,因為可以確保寫的伺服器壓力更小,而讀又可以接受時間上的延遲。

提高讀寫分離效能的主要原因如下。

◎ 物理伺服器增加、負荷增加。

◎ 主資料庫和從資料庫只負責各自的寫和讀,可很大程度地緩解X鎖和S鎖爭用。

◎ 從資料庫可配置MyISAM引擎,提升查詢效能及節約系統開銷。

◎ 從資料庫同步主資料庫的資料與主資料庫直接寫還是有區別的,從資料庫透過主資料庫傳送來的 binlog 恢復資料,但最重要的區別在於主資料庫向從資料庫傳送binlog是非同步的,從資料庫恢復資料也是非同步的。

◎ 讀寫分離適用於讀多寫少的場景,如果只有一臺伺服器,則當Select很多時,Update和Delete會被這些 Select訪問中的資料堵塞,等待 Select結束,併發效能不高。對於寫和讀比例相近的應用,應該部署雙主相互複製。

◎ 可以在從資料庫啟動時增加一些引數來提高其讀的效能,例如 --skip-innodb 、 --skip-bdb 、 --low-priority-updates 及 --delay-key-write=ALL。當然,這些設定也是需要根據具體的業務需求來做的,不一定能用上。

◎ 分攤讀取。假如我們有1個主資料庫和3個從資料庫,假設現在1分鐘內有10條寫入和150條讀取,那麼1個主資料庫和3個從資料庫相當於共計40條寫入,而讀取總數沒變,因此平均下來每臺伺服器都承擔了 10 條寫入和 50 條讀取(主資料庫不承擔讀取操作)。因此,雖然寫入請求數沒變,但是讀取請求數大大分攤了,提高了系統的效能。另外,在讀取請求被分攤後,又間接提高了寫入的效能。所以,總體效能提高了,說白了就是拿機器和頻寬換效能。

◎ MySQL 複製的另一大功能是增加冗餘和提高可用性,在一臺資料庫伺服器宕機後,能透過調整另一臺從資料庫伺服器來以最快的速度恢復服務,因此不能光看效能,也就是說有1個主資料庫和1個從資料庫也是可以保證資料庫穩定的。

實現方案如圖5.18所示。

圖5.18

2.資料庫分庫分表

不管採用哪種分庫分表框架或者平臺,其核心思路都是將原本儲存在單表中的過大資料進行拆分,將這些資料分散儲存到多個數據庫的多個表中,避免因為單表資料量太大給資料的訪問帶來讀寫效能的問題。所以,在分庫分表場景下最重要的一個原則就是將資料儘可能平均拆分到後端的資料庫中,如果拆分得不均勻,則還會產生資料訪問熱點,同樣存在熱點資料因為增長過快使資料庫單表資料量過大的問題。

而對於資料以什麼樣的緯度進行拆分,在很多場景下都是對業務資料的ID(在大部分場景下,此ID都是自增長的方式)做Hash取模(雜湊取模)的方式進行平均拆分的,這確實在很多場景下都是非常適用的,但並不是在所有場景下都是最優的選擇。也就是說,如何拆分資料並沒有所謂的金科玉律,更多地需要結合業務資料的結構和業務場景來決定。以電商訂單資料拆分為例,訂單是任何一個電商平臺都有的業務資料,每個平臺的使用者在提交訂單時都會在平臺後端生成訂單相關的資料,一般記錄一條訂單資料的資料庫表結構如圖5.19所示。

圖5.19

訂單資料主要由三張資料庫表組成,主訂單表對應的就是使用者的一個訂單,每提交一次訂單,都會生成主訂單表的一條資料。在某些情況下,使用者可能在一個訂單中選擇不同賣家的商品,每個賣家又會按照自己在該訂單中提供的商品計算相關的商品優惠(如滿100元減10元),以及按照不同的收貨地址設定不同的物流配送,所以會出現子訂單的相關概念,即一個主訂單會由多個子訂單組成,則真正對應到每個商品的具體訂單資訊被儲存在訂單詳情表中。

如果一個電商平臺的業務健康發展,則訂單資料容易因為單個數據庫表中的資料量過大而造成效能瓶頸,所以需要對其進行資料庫拆分。此時從理論上對訂單進行拆分是可以從兩方面進行的,一方面是透過訂單ID(一般為自增長ID)進行取模,即以訂單ID為分庫分表鍵,另一方面是透過買家的使用者ID進行雜湊取模,即以買家使用者ID為分庫分表鍵。

3.兩種方案的對比

如果透過訂單ID進行取模,比如按1024進行取模,則可以保證主訂單及相關子訂單的訂單詳情資料平均落入後端 1024 個數據庫表中,這在原則上很好地滿足了資料儘可能平均拆分的原則。

如果透過買家的使用者ID進行雜湊後取模,比如也按照1024進行取模,則在技術上也能保證將訂單資料拆分到後端的1024個數據庫表中,透過使用者ID進行雜湊後取模會出現問題,也就是說如果某些賣家的交易量非常大,那麼這些賣家的訂單資料量(特別是訂單詳情表的資料量)會比其他賣家多出不少,也就是說會出現資料不平均的現象,最終導致這些賣家的訂單資料所在的資料庫相對其他資料庫提前進入資料歸檔。

為了避免線上交易資料庫的資料量增大導致資料庫的效能出現問題,我們一般將3個月內的訂單資料儲存在線上交易資料庫中,將超過3個月的訂單歸檔到後端的專門歸檔資料庫中。

所以從對資料儘可能平均拆分這條原則來看,透過訂單 ID 進行取模看起來更能保證訂單資料的平均拆分,但我們暫時不要這麼快下結論,要根據不同的業務場景和最佳實踐多思考不同維度的優缺點。

小結

電商平臺的需求一直在變化,訂單系統的架構也會隨之變化,架構設計就是一個持續改進的過程。如果想將訂單系統做得更好,就需要更加深入系統的每一個環節,比如對容災、災備、分流、流控等都需要慢慢雕琢。在架構中沒有完美的架構,只有平衡的架構,不要追求單點的完美,要追求多點的平衡。

15
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Android第三方庫原始碼解析:OKHttp