
講師介紹
胡顯波,58到家技術經理/58速運後端架構總負責人。14年7月加入58到家,先後負責58到家APP、58小時工、58美甲等,見證了58到家飛速發展。14年11月負責58速運整體業務,帶領團隊小夥伴支撐了速運業務日訂單從0~50W的飛速增長。
今天很榮幸給大家介紹58速運從艱苦創業到成為同城貨運作業領頭人的整個系統演進過程。簡單來說我們的業務是做同城貨運,比如您去買一個大型家具,自己的家用車肯定是裝不下的,這時你可能需要找路邊的小型面包車或者金杯車來幫你搬運。一般來講,很容易遇到黑車,而且價格不标準,我們做的這個行業就是将這種傳統的黑車行業進行線上化,在産品形态上可了解為滴滴打車的計程車版。
本次分享内容主要分為4個部分:創業之初、高速發展、智能時代、總結。
<b>一、創業之初-快速疊代試錯</b>
速運在2014年是作為58集團下20多個孵化業務中的其中之一,那個時期基本是平均三個星期一個業務孵化上線,當時有20多個業務孵化同時進行。這個時間我們不斷的試錯,不斷去尋找58同城新的增長點。
從上圖中的大家可以看到,我們所有的服務都基于在一個資料庫來運作,這個系統之間隻需要通過一些簡單的tag标記就可以區分開業務,系統疊代非常快。新孵化的業務,增加一些簡單的業務邏輯就能實作這個産品的快速上線,我們在兩周内實作了速運使用者、商家的APP以及的後端的産品上線。
派單-石器時代
這時的系統架構是非常簡單的,我們稱之為“石器時代”,當時所有的訂單排程的邏輯放在一個Jar包,然後通過MQTT服務将訂單推送到司機的APP上。當時的訂單排程(也是我們最初級的訂單排程方案)是一個訂單搜尋附近的司機,然後由近到遠的距離将訂單推送出去,司機搶單後即中單。因為在創業階段,我們需要吸引客戶、司機,每單都會有補貼。
痛點:
系統不穩定,一個慢SQL,全業務受影響
這裡舉個非常普遍的例子,其他業務線小夥伴在上線時,不小心寫了一個慢SQL,一個慢SQL就會把資料庫的所有連接配接占滿,導緻所有的業務全部挂掉了,當時聽到的最多的回報是:什麼情況,怎麼你們又挂了。
多業務并存,訂單表索引多,性能下降
當時有很多個業務在同時孵化,多業務并存,每一個業務都會根據它自己的業務需求去在訂單表中建立索引,結果索引越來越多,整體的性能也越來越差。
訂單字段備援,新增和修改字段非常痛苦
每個業務都有特殊的業務字段,單标資料量已經到達了千萬級,每增加一個字段和修改一個字段,都需要耗費很長的時間,而且會造成鎖庫導緻系統異常。
業務增長迅猛,資料庫已成瓶頸
58速運整體的訂單增長非常迅速,在成立三個月以後,每天的單已達到了1萬+,系統性能已成為瓶頸。
針對以上痛點,我們做了第一次的技術引進——遷庫、叢集拆分。
<b>第一次技術演進</b>
<b>遷庫、叢集解耦</b>
為什麼要遷庫?誰痛誰知道!不想受到其他業務小夥伴的影響,就要做到解耦。
一個最簡單的方案就是停服,把所有的服務停掉,然後把資料庫抽離出來,相對來講這是成本最簡單的。但是停服會産生的影響:
淩晨時間業務仍然有訂單,會影響到使用者通路。
需要給使用者發公告。
停服遷移如果失敗,無法向業務方解釋,會喪失信任。
我們采用的方案:将訂單表單獨地拆離出來,放在單獨的資料庫裡,兩個資料庫之間使用雙向同步。雙向同步需要解決時問題:
主鍵沖突:速運的訂單會标記一個比較特殊的标記ID(如80開頭标記為速運,其他業務都是10開頭的),與其它的業務線區分開發,這樣就可以保證它在雙向同步時不會出現主鍵沖突的問題。
更新覆寫:update的操作在同步的過程中因為時間差的問題可能存在寫覆寫的情況,我們采用訂單日志的記錄,遷庫完成後做資料的校驗。
經過多次的遷移,将原有的資料庫按照業務劃分成了訂單庫、結算庫、配置庫和軌迹庫等,每個資料庫會根據業務量容量的大小來配置資料庫實體機的核心、記憶體,減少成本。
<b>二、高速發展:穩定高效</b>
存在問題:
補貼大戰,大量無效補貼,營運成本高
2015年我們進入了高速發展的階段,市場上出現了藍犀牛、1号貨的、雲鳥的等多個強勁的競争對手。各方都是争分奪秒,一個系統、功能,我需要抓緊把它給疊代上來,誰也不能比誰落後。另外就是補貼大戰,各大競争對手投放大量的訂單補貼(高達30元+),使得整體營運成本呈現水高船漲的趨勢。
快速疊代多人維護一套工程,效率差,BUG頻發
最開始創業時團隊隻有幾個人,工程都集中在幾個叢集中,後面擴大到30多個人時,大家都集中在這些叢集上去開發,平均每天都要進行多次上線,遇到了個最核心、最痛點的問題,代碼合并,合并代碼就意味着出錯的幾率大大提升,當時BUG率很高。
業務高速發展,資料量急速增長
我們在2015年時,訂單增長了好幾倍,同時每個訂單大概會推送給50多個司機,這個資料量級,資料量告訴的增長。
營運分析需求越來越複雜
另外營運需要對現在的市場和使用者進行分析,整體的營運需求分析逐漸複雜。
這時我們進行了第二次技術演進,我們稱之為“進行了奔跑中的火車換輪子”,我們進行了服務化解耦;緩存、分庫分表,提升系統性能;接入大資料平台,進行複雜需求的分析。
<b>第二次技術演進</b>
<b>奔跑中的火車換輪子</b>
派單-鐵器時代
我們将所有的系統都按服務子產品進行了拆分,比如說結算、充值、推送、司機任務等,現在大概已有20+個服務,每個服務都有獨立的資料庫,有獨立的負責人。這樣就可以做到我自己的代碼我自己來寫,别人都不允許去插手。
此外我們進行了推送的多通道化,從上圖可以看到,我們針對每個司機選取了兩種推送通道,同時我們也建議大家在做推送消息時采取這種方案。拿小米的手機來說,“小米”推送通道的到達率是最高的,但小米的通道在華為的手機上,到達率不如“個推”的推送到達率高。我們就會根據司機的機型來選取一個到達率最高的三方通道。同時在設計上不能有單點,假如說小米的通道出現了問題,那我們的服務就不可用了,司機接收不到訂單,使用者的需求就沒法得到滿足。是以我們還有一個自研管道TCP通道,這個TCP通道除了和我們三方通道做一個雙通道保活外,它還可以做一些資料的上傳。
這時的訂單排程,被稱為探索階段,初期的距離推送效果有限,誰搶到誰就中單,司機的服務品質我們沒有辦法去評判,補貼也是大衆化的。是以我們自己研究了一個按象限推送的方法:1、首先我先推送一個很短的距離,比如說我先把一公裡以内的所有司機都推送一遍,這時我是不給補貼的,當推完一公裡以後沒有人搶,或者是搶的人非常的少,我會按象限去推。2、在第一個象限,我給一塊錢補貼,如果沒人搶,第二個象限給兩塊錢補貼,第三個象限給三塊錢,這樣逐漸地去增加。最後當司機搶了單,我們會根據司機的好評、完成率這些方面選擇一個最優質的司機。
分庫分表
前面提到資料庫性能已經成為瓶頸了,是以這裡以一個使用者服務給大家講一下我們的分庫分表是怎麼做的。
業務初期我們一個庫可以完成支撐所有的通路;
随着資料量的增長,我們可以做了一些讀寫的分離,把一些讀取SQL放在從庫上,但這裡給大家一個建議——訂單狀态的讀取盡量不要在從庫上讀,網絡一抖動,你的訂單狀态就很可能會出現不一緻情況;
加上從庫,當表的資料量達到千萬級,查詢的性能依然會下降,這樣我們就需要去做水準拆分和垂直拆分。水準拆分比較簡單,大家也容易了解,而垂直拆分就是比如說我把一個使用者10個最常用的屬性放到一個組表裡,把不常用的屬性放到另外一張表裡面去,這樣可以減少I/O的操作,也可以提高整體的産品性能。
資料庫水準拆分以後,再給拆分後的庫增加從庫。
在這裡水準拆分要重點提一下,就是如果資源允許,水準拆分還是建議分庫。資料庫的性能瓶頸也是會受到硬體裝置和網絡IO的影響,如果通路量的持續增加,資料庫還是會成為瓶頸。
我們的水準拆分有兩種方法:
範圍法:使用者ID在1K萬以下的放到一個庫,1K萬~2KW以上的放到另外一個庫,這樣切分簡單,擴容也友善,但是會存在資料庫之間的負載不均勻。
哈希法:根據使用者ID進行哈希運算,切分簡單,整體負載比較均衡的,平滑遷移可能需要我們去解決的難點。
拆分後的問題:
部分查詢變慢了:非patition key查詢,需要周遊全部庫
做完水準拆分以後,我們遇到了一個新的問題,實用patition key水準拆分,非patition key查詢需要掃庫,性能反而變慢了。
營運需求無法實作:各種次元統計,沒辦法聯表查詢
營運小夥伴原來在單庫的時候,因為複雜SQL跑的特别慢,導緻無法統計特别,分完庫以後,他連Join都用不了,更無法查詢統計了。
問題分析-“任何脫離業務架構的設計都在耍流氓”
我們拿資料庫的Binlog日志看了一下,根據使用者ID的通路大概是占99%,根據使用者姓名、手機号、Email的這些屬性的查詢大概隻有在1%的量。
營運會根據年齡、性别、頭像、登入時間、注冊時間這些複雜的資料去做統計和分析。
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速運已經成為行業的領頭者了,如何使用更少的補貼擷取最大化的收益?
平台補貼是不是真的起到了作用,然後我們到底需要補多少錢才能幫助使用者完成訂單,
如何去盡量滿足使用者的需求。每個新使用者進入平台是有成本的,一個使用者的成本在幾十甚至到一百塊左右,如何滿足使用者的需求,讓使用者持續的留在平台中。
平台的司機良莠不齊,司機的收益應如何配置設定?
<b>第三次技術演進:戰斧項目</b>
我們進行了第三次的技術引進,我們稱之為是戰斧項目,項目的定義:精準、高效。我們做了以下優化:
政策服務的細化
智能模型的接入
智能的分流架構
<b>三、智能時代:效率、精準</b>
智能模型訓練
上圖為智能模型訓練圖,首先我們會将訂單資訊、使用者資訊、司機資訊、客司關系資訊、訂單總體推送、司機接單等場景資訊統一上傳到大資料平台,通過這種歸一化&分桶、XGBoost、特征組合、獨熱編碼等将這些資料分析為特征資料。
針對分析出來特征資料,我們需要對它進行訓練,如:訂單價格、訂單距離等特征在整個訂單派單中起到的權重。因為特征很多,計算出來的權重可能并不是一個完美的解,隻能說是近優、最優的一個解法,通過不斷地疊代優化,最終訓練出來最終的模型。
訂單-模型運用
訂單模型的運用:
下單階段:在使用者下單時,我們會采用這種使用者訂單定價的模型,觀察這個訂單所在的商圈的運力飽和度,如果司機少,而訂單需求多,我們會進行一個訂單的調價,
推送階段:系統推送的過程中,會根據司機的接單意願來撈取。有的司機喜歡高價格訂單,有的司機喜歡短程訂單,有的司機喜歡去中關村等。我們會根據訂單與司機意願的比對程度進行優先推送的排序。
搶單階段:先預估這個訂單的接單人數,計算出來訂單的價值,如果訂單的價值高(價格高、地點好)、那麼這個訂單不會發放補貼了,同時會扣取司機的一些積分或優先搶單次數等。如果訂單價值比較低(價格低、偏遠地區),會給這個訂單适當地增加補貼,來確定訂單的完成。
指派階段:當司機搶完單以後,我們會根據所有司機曆史完成訂單的資料,取司機的品質,來決定哪個司機中單,保證訂單盡可能完成。
訂單完成階段:訂單完成了以後預測這個使用者的流失機率,如果可能流失,會送一些券或者其他權益吸引使用者留在平台。
派單-智能時代
上圖在智能派單時代的系統架構圖。使用者在下完單以後,訂單會進入到我們整體的政策系統,它包含推送系統、補貼系統、價格系統、任務系統等,然後通過特征比對系統,計算出一個最優的訂單排程解,将這個訂單推送到司機的單隊列引擎和訂單的排序政策引擎,最終通過我們的推送服務将訂單推送給司機。
政策分流+監測
智能系統需要有不同的算法線上上實驗,當我們一些新算法研發完成以後,肯定不能用100%的流量線上上進行驗證算法的可行性,如果有問題,會對線上業務産生影響。我們一般取5%或10%的流量線上上驗證。一般根據使用者手機号、裝置碼、使用者屬性等,以及取模、集合等方式。驗證線上算法驗證時,如何實時的監測算法的效果,避免錯誤算法對線上業務造成影響?
如上圖所示,使用者在APP的每個步驟、運用了哪個算法,我們都會将使用者的ID、采用的算法ID通過日志上報的報到統計平台。業務監控平台會實時進行監控,對于出現異常的算法就自動關閉分流。
特征計算
特征資料中有40多萬個特征,每個訂單需要推送給很多個司機,需要進行進行上萬次的運算,需要在幾十毫秒内給出計算結果,如何保證計算的高性能呢?我們采用的是這種階段性事件驅動的計算方式來最大化提高并行計算的能力。
如圖所示,這是我們的計算鍊,裡面包含多個Stage,包含準備階段、轉化階段、取數階段和計算階段,每一個階段都有自己獨立的線程池,根據每個階段的特征設定核心線程數,同時整個計算鍊做到了可插拔的形式,友善業務調整。
利器-監控平台
監控可以說是整個架構演進過程中非常重要的部分。
再牛逼的算法,也需要穩定的系統來支撐;
業務出現異常,我們肯定要第一時間知曉的;
提高問題排查效率,就是在挽救損失。
立體化監控
目前已經做到的監控包含:關鍵字、接口、流量、端口,JVM、CPU、線程、緩存、DB所有的監控等等,同時還有服務治理,當服務節點發生異常,實時切換。
業務化的名額監控,管道轉化率、管道取消率、管道推送數量、異常訂單數量等等,如果出現異常,第一時間預警。
調用跟蹤系統
調用跟蹤系統,很多網際網路公司都已經在使用,調用跟蹤系統目的是需要看到的是APP發起的每個請求在整個Service後端走過的所有過程,效果下圖所示,可以監控到每一步所調用的服務和耗時。
<b>四、總結</b>
不同的階段采用不同的架構,技術的重點跟随業務轉變。
訂單的推送通道,建議使用雙通道,保證推送的到達率。
資料庫的水準拆分,在資源允許的情況下,強烈建議分庫。
算法線上分流驗證必須要有實時的監控和自動流量切換。
監控很重要,第一時間發現問題,減少影響
Q&A
Q1:58速運在分庫時用的是什麼中間件?
A1:分庫看業務不同的需要,有自研的,也有使用第三方的。看自身的需要,中間件不一定是最好的,但它能夠給你很多借鑒。
Q2:剛才您說訂單推送多元化,是按照城市的範圍來推送的還是按照指定哪個區域來推送的?比如說你想要做山東省的業務,就指定地推這樣?
A2:不是,首先像這種車輛的排程,你肯定不能離得特别遠,如果你那個訂單和司機的距離超過了5公裡,基本上這個訂單成交的機率是非常地低的。我們是以訂單的起始點,去搜尋附近的司機,這個沒有區域的限制。
Q3:老師您好,我想問一下您提到了幾次架構的演進,因為整個的産品疊代過程當中,整個業務需求一直持續地在演進,你的技術架構也在演進,那麼整個的研發體系是專門有一個團隊在做這一塊的技術架構演進,還是跟着你的業務團隊一起在做這個架構演進?
A2:肯定是随着業務的發展來進行架構演進,如何平衡業務需求的實作和架構的演進是架構師的職責所在。個人的幾點建議:
人是要不斷地招聘,如果人手連業務需求都忙不過來,肯定是沒有時間來做技術優化的。
在業務需求實作的過程中穿插一些技術優化的項目,帶領團隊的小夥伴實作技術成長。
建立技術圖書館,讓大家自我學習。
團隊内部技術分享交流,參加技術大會等手段,提升團隊的技術能力。
和業務的小夥伴溝通,我們需要做一些技術優化,做這些技術優化可以給我們帶來哪些收益(提高開發效率、提高營運效率、減少BUG等),溝通和協調這些軟實力也是架構師需要具備的重要技能。
公司不同的階段,需要采用不同的應對方案,創業初期,生存最重要,架構設計的再好,業務發展不起來也無法起到作用。
原文釋出時間為:2018-01-08
本文作者:胡顯波@DBAPlus