日訂單峯值破40萬!58速運訂單調度系統架構大解密

講師介紹前端

胡顯波,58到家技術經理/58速運後端架構總負責人。14年7月加入58到家,前後負責58到家APP、58小時工、58美甲等,見證了58到家飛速發展。14年11月負責58速運總體業務,帶領團隊小夥伴支撐了速運業務日訂單從0~50W的飛速增加。算法

今天很榮幸給你們介紹58速運從艱苦創業到成爲同城貨運行業領頭人的整個系統演進過程。簡單來講咱們的業務是作同城貨運,好比您去買一個大型傢俱,本身的家用車確定是裝不下的,這時你可能須要找路邊的小型麪包車或者金盃車來幫你搬運。通常來說,很容易遇到黑車,並且價格不標準,咱們作的這個行業就是將這種傳統的黑車行業進行線上化,在產品形態上可理解爲滴滴打車的出租車版。數據庫

本次分享內容主要分爲4個部分:創業之初、高速發展、智能時代、總結。後端

1、創業之初-快速迭代試錯

58集團

速運在2014年是做爲58集團下20多個孵化業務中的其中之一,那個時期基本是平均三個星期一個業務孵化上線,當時有20多個業務孵化同時進行。這個時間咱們不斷的試錯,不斷去尋找58同城新的增加點。緩存

從上圖中的你們能夠看到,咱們全部的服務都基於在一個數據庫來運行,這個系統之間只須要經過一些簡單的tag標記就能夠區分開業務,系統迭代很是快。新孵化的業務,增長一些簡單的業務邏輯就能實現這個產品的快速上線,咱們在兩週內實現了速運用戶、商家的APP以及的後端的產品上線。網絡

派單-石器時代架構

數據庫

這時的系統架構是很是簡單的,咱們稱之爲「石器時代」,當時全部的訂單調度的邏輯放在一個Jar包,而後經過MQTT服務將訂單推送到司機的APP上。當時的訂單調度(也是咱們最初級的訂單調度方案)是一個訂單搜索附近的司機,而後由近到遠的距離將訂單推送出去,司機搶單後即中單。由於在創業階段,咱們須要吸引客戶、司機,每單都會有補貼。框架

痛點:性能

  • 系統不穩定,一個慢SQL,全業務受影響

這裏舉個很是廣泛的例子,其餘業務線小夥伴在上線時,不當心寫了一個慢SQL,一個慢SQL就會把數據庫的全部鏈接佔滿,致使全部的業務所有掛掉了,當時聽到的最多的反饋是:什麼狀況,怎麼大家又掛了。學習

  • 多業務並存,訂單表索引多,性能降低

當時有不少個業務在同時孵化,多業務並存,每個業務都會根據它本身的業務需求去在訂單表中創建索引,結果索引愈來愈多,總體的性能也愈來愈差。

  • 訂單字段冗餘,新增和修改字段很是痛苦

每一個業務都有特殊的業務字段,單標數據量已經到達了千萬級,每增長一個字段和修改一個字段,都須要耗費很長的時間,並且會形成鎖庫致使系統異常。

  • 業務增加迅猛,數據庫已成瓶頸

58速運總體的訂單增加很是迅速,在成立三個月之後,天天的單已達到了1萬+,系統性能已成爲瓶頸。

針對以上痛點,咱們作了第一次的技術引進——遷庫、集羣拆分。

第一次技術演進遷庫、集羣解耦

爲何要遷庫?誰痛誰知道!不想受到其餘業務小夥伴的影響,就要作到解耦。

一個最簡單的方案就是停服,把全部的服務停掉,而後把數據庫抽離出來,相對來說這是成本最簡單的。可是停服會產生的影響:

  1. 凌晨時間業務仍然有訂單,會影響到用戶訪問。
  2. 須要給用戶發公告。
  3. 停服遷移若是失敗,沒法向業務方解釋,會喪失信任。

們採用的方案:將訂單表單獨地拆離出來,放在單獨的數據庫裏,兩個數據庫之間使用雙向同步。雙向同步須要解決時問題:

  1. 主鍵衝突:速運的訂單會標記一個比較特殊的標記ID(如80開頭標記爲速運,其餘業務都是10開頭的),與其它的業務線區分開發,這樣就能夠保證它在雙向同步時不會出現主鍵衝突的問題。
  2. 更新覆蓋:update的操做在同步的過程當中由於時間差的問題可能存在寫覆蓋的狀況,咱們採用訂單日誌的記錄,遷庫完成後作數據的校驗。

通過屢次的遷移,將原有的數據庫按照業務劃分紅了訂單庫、結算庫、配置庫和軌跡庫等,每一個數據庫會根據業務量容量的大小來配置數據庫物理機的內核、內存,減小成本。

2、高速發展:穩定高效

存在問題:

  • 補貼大戰,大量無效補貼,運營成本高

2015年咱們進入了高速發展的階段,市場上出現了藍犀牛、1號貨的、雲鳥的等多個強勁的競爭對手。各方都是爭分奪秒,一個系統、功能,我須要抓緊把它給迭代上來,誰也不能比誰落後。另外就是補貼大戰,各大競爭對手投放大量的訂單補貼(高達30元+),使得總體運營成本呈現水高船漲的趨勢。

  • 快速迭代多人維護一套工程,效率差,BUG頻發

最開始創業時團隊只有幾我的,工程都集中在幾個集羣中,後面擴大到30多我的時,你們都集中在這些集羣上去開發,平均天天都要進行屢次上線,遇到了個最核心、最痛點的問題,代碼合併,合併代碼就意味着出錯的概率大大提高,當時BUG率很高。

  • 業務高速發展,數據量急速增加

咱們在2015年時,訂單增加了好幾倍,同時每一個訂單大概會推送給50多個司機,這個數據量級,數據量告訴的增加。

  • 運營分析需求愈來愈複雜

另外運營須要對如今的市場和用戶進行分析,總體的運營需求分析逐漸複雜。

這時咱們進行了第二次技術演進,咱們稱之爲「進行了奔跑中的火車換輪子」,咱們進行了服務化解耦;緩存、分庫分表,提高系統性能;接入大數據平臺,進行復雜需求的分析。

第二次技術演進奔跑中的火車換輪子

派單-鐵器時代

咱們將全部的系統都按服務模塊進行了拆分,好比說結算、充值、推送、司機任務等,如今大概已有20+個服務,每一個服務都有獨立的數據庫,有獨立的負責人。這樣就能夠作到我本身的代碼我本身來寫,別人都不容許去插手。

此外咱們進行了推送的多通道化,從上圖能夠看到,咱們針對每一個司機選取了兩種推送通道,同時咱們也建議你們在作推送消息時採起這種方案。拿小米的手機來講,「小米」推送通道的到達率是最高的,但小米的通道在華爲的手機上,到達率不如「個推」的推送到達率高。咱們就會根據司機的機型來選取一個到達率最高的三方通道。同時在設計上不能有單點,假如說小米的通道出現了問題,那咱們的服務就不可用了,司機接收不到訂單,用戶的需求就無法獲得知足。因此咱們還有一個自研渠道TCP通道,這個TCP通道除了和咱們三方通道作一個雙通道保活外,它還能夠作一些數據的上傳。

這時的訂單調度,被稱爲探索階段,初期的距離推送效果有限,誰搶到誰就中單,司機的服務質量咱們沒有辦法去評判,補貼也是大衆化的。因此咱們本身研究了一個按象限推送的方法:一、首先我先推送一個很短的距離,好比說我先把一千米之內的全部司機都推送一遍,這時我是不給補貼的,當推完一千米之後沒有人搶,或者是搶的人很是的少,我會按象限去推。二、在第一個象限,我給一塊錢補貼,若是沒人搶,第二個象限給兩塊錢補貼,第三個象限給三塊錢,這樣逐步地去增長。最後當司機搶了單,咱們會根據司機的好評、完成率這些方面選擇一個最優質的司機。

分庫分表

前面提到數據庫性能已經成爲瓶頸了,因此這裏以一個用戶服務給你們講一下咱們的分庫分表是怎麼作的。

  1. 業務初期咱們一個庫能夠完成支撐全部的訪問;
  2. 隨着數據量的增加,咱們能夠作了一些讀寫的分離,把一些讀取SQL放在從庫上,但這裏給你們一個建議——訂單狀態的讀取儘可能不要在從庫上讀,網絡一抖動,你的訂單狀態就極可能會出現不一致狀況;
  3. 加上從庫,當表的數據量達到千萬級,查詢的性能依然會降低,這樣咱們就須要去作水平拆分和垂直拆分。水平拆分比較簡單,你們也容易理解,而垂直拆分就是好比說我把一個用戶10個最經常使用的屬性放到一個組表裏,把不經常使用的屬性放到另一張表裏面去,這樣能夠減小I/O的操做,也能夠提升總體的產品性能。
  4. 數據庫水平拆分之後,再給拆分後的庫增長從庫。

在這裏水平拆分要重點提一下,就是若是資源容許,水平拆分仍是建議分庫。數據庫的性能瓶頸也是會受到硬件設備和網絡IO的影響,若是訪問量的持續增長,數據庫仍是會成爲瓶頸。

數據庫

咱們的水平拆分有兩種方法:

  1. 範圍法:用戶ID在1K萬如下的放到一個庫,1K萬~2KW以上的放到另一個庫,這樣切分簡單,擴容也方便,可是會存在數據庫之間的負載不均勻。
  2. 哈希法:根據用戶ID進行哈希運算,切分簡單,總體負載比較均衡的,平滑遷移可能須要咱們去解決的難點。

拆分後的問題:

  • 部分查詢變慢了:非patition key查詢,須要遍歷所有庫

作完水平拆分之後,咱們遇到了一個新的問題,實用patition key水平拆分,非patition key查詢須要掃庫,性能反而變慢了。

  • 運營需求沒法實現:各類維度統計,沒辦法聯表查詢

運營小夥伴原來在單庫的時候,由於複雜SQL跑的特別慢,致使沒法統計特別,分完庫之後,他連Join都用不了,更沒法查詢統計了。

問題分析-「任何脫離業務架構的設計都在耍流氓」

  1. 咱們拿數據庫的Binlog日誌看了一下,根據用戶ID的訪問大概是佔99%,根據用戶姓名、手機號、Email的這些屬性的查詢大概只有在1%的量。
  2. 運營會根據年齡、性別、頭像、登陸時間、註冊時間這些複雜的數據去作統計和分析。

索引

1、前端解決方案

  • 索引表法:非patition key與uid創建索引表

拿非Patition key和uid作一個索引表,這樣我直接經過這個表和Patition key進來後先去找一下uid,這樣就能夠找到這個uid在哪一個庫,可是增長了一次數據庫的查詢。

  • 緩存映射法:非patition key與uid映射關係放入緩存,緩存命中率高

咱們把Patition key與uid的映射關係放在緩存裏面去,只會第一次比較慢,後面都會從緩存中取,並且這個緩存基本上不用淘汰。

  • 非patition key生成uid

根據Patition key生成一個uid,這個須要必定的生成技巧,同時這個可能有主鍵衝突的風險。

  • 基因法

根據非Patition key的其中部分基因生成一個字段,以下圖:

2、運營側需求解決方案

  • 冗餘後臺庫:經過MQ/Canal實時同步到後臺庫

經過MQ或者是Canal讀取MySQL的binlog,將幾個前臺的數據庫實時地同步到後臺庫裏去,後臺庫不對前臺業務提供服務,僅供運營側查詢。注意這個後臺庫是千萬不能用於現場生產的,由於運營會在上面作一些複雜的慢查詢,數據庫的響應會很是慢。

引擎

  • 外置搜索引擎:ES/Solr/XXXX

接入外鍵索引,如ES/Solr提供搜索服務。

  • 大數據平臺

使用大數據平臺,經過MySQL的binlog和日誌上報,將數據讀取到大數據平臺進行實時地分析,供運營查詢。

反思

到了2016年,競爭對手基本上已經被消滅了,58速運已經成爲行業的領頭者了,如何使用更少的補貼獲取最大化的收益?

  1. 平臺補貼是否是真的起到了做用,而後咱們到底須要補多少錢才能幫助用戶完成訂單,
  2. 如何去儘可能知足用戶的需求。每一個新用戶進入平臺是有成本的,一個用戶的成本在幾十甚至到一百塊左右,如何知足用戶的需求,讓用戶持續的留在平臺中。
  3. 平臺的司機參差不齊,司機的收益應如何分配?

第三次技術演進:戰斧項目

咱們進行了第三次的技術引進,咱們稱之爲是戰斧項目,項目的定義:精準、高效。咱們作了如下優化:

  1. 策略服務的細化
  2. 智能模型的接入
  3. 智能的分流框架

3、智能時代:效率、精準

智能模型訓練

智能時代

上圖爲智能模型訓練圖,首先咱們會將訂單信息、用戶信息、司機信息、客司關係信息、訂單整體推送、司機接單等場景信息統一上傳到大數據平臺,經過這種歸一化&分桶、XGBoost、特徵組合、獨熱編碼等將這些數據分析爲特徵數據。

針對分析出來特徵數據,咱們須要對它進行訓練,如:訂單價格、訂單距離等特徵在整個訂單派單中起到的權重。由於特徵不少,計算出來的權重可能並非一個完美的解,只能說是近優、最優的一個解法,經過不斷地迭代優化,最終訓練出來最終的模型。

訂單-模型運用

訂單模型

訂單模型的運用:

  1. 下單階段:在用戶下單時,咱們會採用這種用戶訂單訂價的模型,觀察這個訂單所在的商圈的運力飽和度,若是司機少,而訂單需求多,咱們會進行一個訂單的調價,
  2. 推送階段:系統推送的過程當中,會根據司機的接單意願來撈取。有的司機喜歡高價格訂單,有的司機喜歡短程訂單,有的司機喜歡去中關村等。咱們會根據訂單與司機意願的匹配程度進行優先推送的排序。
  3. 搶單階段:先預估這個訂單的接單人數,計算出來訂單的價值,若是訂單的價值高(價格高、地點好)、那麼這個訂單不會發放補貼了,同時會扣取司機的一些積分或優先搶單次數等。若是訂單價值比較低(價格低、偏遠地區),會給這個訂單適當地增長補貼,來確保訂單的完成。
  4. 指派階段:當司機搶完單之後,咱們會根據全部司機歷史完成訂單的數據,取司機的質量,來決定哪一個司機中單,保證訂單儘量完成。
  5. 訂單完成階段:訂單完成了之後預測這個用戶的流失機率,若是可能流失,會送一些券或者其餘權益吸引用戶留在平臺。

派單-智能時代

智能派單

上圖在智能派單時代的系統架構圖。用戶在下完單之後,訂單會進入到咱們總體的策略系統,它包含推送系統、補貼系統、價格系統、任務系統等,而後經過特徵匹配系統,計算出一個最優的訂單調度解,將這個訂單推送到司機的單隊列引擎和訂單的排序策略引擎,最終經過咱們的推送服務將訂單推送給司機。

策略分流+監測

智能系統須要有不一樣的算法在線上實驗,當咱們一些新算法研發完成之後,確定不能用100%的流量在線上進行驗證算法的可行性,若是有問題,會對線上業務產生影響。咱們通常取5%或10%的流量在線上驗證。通常根據用戶手機號、設備碼、用戶屬性等,以及取模、集合等方式。驗證線上算法驗證時,如何實時的監測算法的效果,避免錯誤算法對線上業務形成影響?

監控

如上圖所示,用戶在APP的每一個步驟、運用了哪一個算法,咱們都會將用戶的ID、採用的算法ID經過日誌上報的報到統計平臺。業務監控平臺會實時進行監控,對於出現異常的算法就自動關閉分流。

特徵計算

特徵數據中有40多萬個特徵,每一個訂單須要推送給不少個司機,須要進行進行上萬次的運算,須要在幾十毫秒內給出計算結果,如何保證計算的高性能呢?咱們採用的是這種階段性事件驅動的計算方式來最大化提升並行計算的能力。

如圖所示,這是咱們的計算鏈,裏面包含多個Stage,包含準備階段、轉化階段、取數階段和計算階段,每個階段都有本身獨立的線程池,根據每一個階段的特徵設置核心線程數,同時整個計算鏈作到了可插拔的形式,方便業務調整。

利器-監控平臺

監控能夠說是整個架構演進過程當中很是重要的部分。

  1. 再牛逼的算法,也須要穩定的系統來支撐;
  2. 業務出現異常,咱們確定要第一時間知曉的;
  3. 提升問題排查效率,就是在挽救損失。

立體化監控

立體監控

目前已經作到的監控包含:關鍵字、接口、流量、端口,JVM、CPU、線程、緩存、DB全部的監控等等,同時還有服務治理,當服務節點發生異常,實時切換。

業務化的指標監控,渠道轉化率、渠道取消率、渠道推送數量、異常訂單數量等等,若是出現異常,第一時間預警。

調用跟蹤系統

調用跟蹤系統,不少互聯網公司都已經在使用,調用跟蹤系統目的是須要看到的是APP發起的每一個請求在整個Service後端走過的全部過程,效果下圖所示,能夠監控到每一步所調用的服務和耗時。

4、總結

  1. 不一樣的階段採用不一樣的架構,技術的重點跟隨業務轉變。
  2. 訂單的推送通道,建議使用雙通道,保證推送的到達率。
  3. 數據庫的水平拆分,在資源容許的狀況下,強烈建議分庫。
  4. 算法線上分流驗證必需要有實時的監控和自動流量切換。
  5. 監控很重要,第一時間發現問題,減小影響

Q&A

Q1:58速運在分庫時用的是什麼中間件?

A1:分庫看業務不一樣的須要,有自研的,也有使用第三方的。看自身的須要,中間件不必定是最好的,但它可以給你不少借鑑。

 

Q2:剛纔您說訂單推送多元化,是按照城市的範圍來推送的仍是按照指定哪一個區域來推送的?好比說你想要作山東省的業務,就指定地推這樣?

A2:不是,首先像這種車輛的調度,你確定不能離得特別遠,若是你那個訂單和司機的距離超過了5千米,基本上這個訂單成交的機率是很是地低的。咱們是以訂單的起始點,去搜索附近的司機,這個沒有區域的限制。

 

Q3:老師您好,我想問一下您提到了幾回架構的演進,由於整個的產品迭代過程中,整個業務需求一直持續地在演進,你的技術架構也在演進,那麼整個的研發體系是專門有一個團隊在作這一塊的技術架構演進,仍是跟着你的業務團隊一塊兒在作這個架構演進?

A2:確定是隨着業務的發展來進行架構演進,如何平衡業務需求的實現和架構的演進是架構師的職責所在。我的的幾點建議:

  1. 人是要不斷地招聘,若是人手連業務需求都忙不過來,確定是沒有時間來作技術優化的。
  2. 在業務需求實現的過程當中穿插一些技術優化的項目,帶領團隊的小夥伴實現技術成長。
  3. 創建技術圖書館,讓你們自我學習。
  4. 團隊內部技術分享交流,參加技術大會等手段,提高團隊的技術能力。
  5. 和業務的小夥伴溝通,咱們須要作一些技術優化,作這些技術優化能夠給咱們帶來哪些收益(提升開發效率、提升運營效率、減小BUG等),溝通和協調這些軟實力也是架構師須要具有的重要技能。
  6. 公司不一樣的階段,須要採用不一樣的應對方案,創業初期,生存最重要,架構設計的再好,業務發展不起來也沒法起到做用。
相關文章
相關標籤/搜索