溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

發(fā)布時間:2020-08-15 04:29:03 來源:ITPUB博客 閱讀:491 作者:云編 欄目:軟件技術(shù)

  本文根據(jù)封宇在2018年10月18日【第十屆中國系統(tǒng)架構(gòu)師大會(SACC2018)】現(xiàn)場演講內(nèi)容整理而成。

   講師介紹:

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  封宇,瓜子二手車高級技術(shù)專家,中國計算機(jī)學(xué)會專業(yè)會員。2017年2月入職瓜子二手車,主要負(fù)責(zé)瓜子即時消息解決方案及相關(guān)系統(tǒng)研發(fā)工作。在瓜子期間,主持自研消息系統(tǒng)用于支持瓜子內(nèi)效工具呱呱,滿足瓜子兩萬多員工移動辦公需求;作為項目經(jīng)理,負(fù)責(zé)瓜子服務(wù)在線化項目,該項目對瓜子二手車交易模式及流程帶來深遠(yuǎn)影響。

  在入職瓜子二手車之前,封宇曾供職于58同城、58到家、華北計算技術(shù)研究所,參與到家消息系統(tǒng)、58爬蟲系統(tǒng)以及多個國家級軍工科研項目的架構(gòu)及研發(fā)工作。在消息系統(tǒng)、后端架構(gòu)、存儲架構(gòu)等方面有豐富經(jīng)驗。

   本文摘要:

  瓜子業(yè)務(wù)重線下,用戶網(wǎng)上看車、預(yù)約到店、成交等許多環(huán)節(jié)都發(fā)生在線下。瓜子IM智能客服系統(tǒng)的目的是要把這些線下的活動搬到線上,對線下行為進(jìn)行追溯,積累相關(guān)數(shù)據(jù)。系統(tǒng)連接用戶、客服、電銷、銷售、AI機(jī)器人、業(yè)務(wù)后臺等多個角色及應(yīng)用,覆蓋網(wǎng)上咨詢、瀏覽、預(yù)約看車、到店體驗、后服、投訴等眾多環(huán)節(jié),各個角色間通過可直接操作的卡片傳遞業(yè)務(wù)。

  例如,用戶有買車意向時,電銷或AI機(jī)器人會及時給用戶推送預(yù)約看車的卡片,用戶只需選擇時間即可完成預(yù)約操作。整個系統(tǒng)邏輯復(fù)雜,及時性、可靠性要求高,涉及IM消息、業(yè)務(wù)卡片、各種實時統(tǒng)計。此次演講,從數(shù)據(jù)架構(gòu)層面講解系統(tǒng)遇到的挑戰(zhàn)及解決辦法。

   分享大綱:

  一、項目背景

  二、系統(tǒng)架構(gòu)

  三、存儲架構(gòu)

   演講正文:

  今天分享的題目是“瓜子IM智能客服數(shù)據(jù)架構(gòu)設(shè)計”,這個系統(tǒng)和旺旺比較像。簡單說一下分享的幾大部分內(nèi)容:第一部分項目背景,項目背景需要稍微講一下,有助于大家理解;第二部分是系統(tǒng)架構(gòu),為什么要簡單講一下系統(tǒng)架構(gòu)呢?因為不講業(yè)務(wù)的架構(gòu)都是耍流氓,所以說我們講存儲,都要知道系統(tǒng)是怎么回事;第三部分重點(diǎn)講一下存儲,在這塊我們講分享一下我們的實踐經(jīng)驗,以及演進(jìn)過程,更多的是采到的一些坑,我們怎么解決的。

   項目背景

  比較熟悉的都知道瓜子二手車沒有中間商賺差價,其實瓜子在中間要做很多事情。首先二手車很難,它不是一個標(biāo)品,我們要去收車,收車都是在社會上去收,通過網(wǎng)站收,你要驗車。我們要把它放到我們的網(wǎng)站上,有些車要收到我們的場地,有些車可能是在用戶家里樓下停著。如果一個用戶來買車,在網(wǎng)上點(diǎn)了之后,你可能下單要去看這個車,我們的銷售要去跟到線下去陪你看車選車試駕,還有一系列的復(fù)檢等等,有很多的事情。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  現(xiàn)在瓜子這塊我們做的還不夠好,為什么不夠好?我們站在瓜子內(nèi)部的角度來看這個事情,有很多的行為發(fā)生在線下,我們很難防就會帶來很多問題。比如飛單,有些去跟別的企業(yè)串,這個車就沒有在平臺上賣,這些問題都很難解決。第二個就是銷售到底跟用戶在做什么,他沒準(zhǔn)罵那些用戶或者什么的,回頭企業(yè)發(fā)現(xiàn)的投訴很難查。那我們這個項目的一個重要的問題,就要解決一個線上化,就是把這些線下的行為搬到線上,我們利用通訊,在IM過程當(dāng)中傳遞一些業(yè)務(wù),這樣把整個的一些業(yè)務(wù)線上化固化下來。

  第二個是電商化,我比較喜歡用京東,我覺得他的物流和客服都很好,瓜子也是希望做成電商,還有一個就降本增效快一點(diǎn),如果現(xiàn)在你用瓜子,你會發(fā)現(xiàn)你到網(wǎng)上去瀏覽,就會有電銷人員給你打電話,這個是很煩的,對用戶的體驗很不好,再一個對瓜子來說成本也很高,我們也養(yǎng)了很多的電銷,所以說我們就啟動了這樣一個項目來解決這些問題。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  剛才提到了很復(fù)雜,整個系統(tǒng)串聯(lián)了很多角色,有用戶、銷售、電銷、評估師,還有AI和機(jī)器人。做系統(tǒng)要善于抽象,我們先有一個基于通訊的即時通訊系統(tǒng)。第二我們是要把通訊系統(tǒng)集成到業(yè)務(wù),或者叫把這些業(yè)務(wù)搬到通訊系統(tǒng)上面來,核心就是這樣子。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  這是截了幾個圖,我們看第一個圖,我們上邊就有一個車,下邊有個預(yù)約,用戶事實上是可以直接在聊天界面里面去點(diǎn)預(yù)約了。這個就是我說的跟一般的客服不一樣的,它可以做一些叫做導(dǎo)購或者叫營銷也好,直接通過這樣一個途徑,因為瓜子的獲客成本很高,每個訪問瓜子的用戶我們都希望及時跟他溝通,這個圖有助于大家理解。

   系統(tǒng)架構(gòu)

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  整個系統(tǒng)不是一個單一的系統(tǒng),它結(jié)合了一些業(yè)務(wù),我們可以把它拆分成這么幾個層次。最上面是一個端,那端首先就是瓜子的APP,當(dāng)然還有毛豆的,現(xiàn)在瓜子的業(yè)務(wù)有好幾個,除此之外有些員工用的,比如說電銷的、客服的、售后的、金融的、我們可能都是一些APP或者是一些桌面系統(tǒng),這是我們的客戶端。

  第二是一個路由層,我們要打通這些業(yè)務(wù)。要讓這些業(yè)務(wù)在這個系統(tǒng)里邊及時的傳遞,所謂傳遞剛才前面有個圖案,比如說你想約車了,那客服就給你或者電銷就給你發(fā)一個約車的卡片,你就可以直接選時間約,這是傳遞業(yè)務(wù)。再往下邊是一些業(yè)務(wù)層,那就是原來瓜子有很多有什么業(yè)務(wù)就涉及什么業(yè)務(wù),最底下是一個存儲。這次后邊主要講的就是站在存儲層的角度來看整個系統(tǒng),重點(diǎn)會講存儲層怎么對路由層進(jìn)行支持。

   存儲架構(gòu)

  存儲這塊會講大概幾個點(diǎn),包括數(shù)據(jù)庫的拆分,消息怎么存,消息里邊也會特別提一下群的模型,大規(guī)模的群是比較麻煩的。還有一些存儲邏輯以及業(yè)務(wù)怎么在上邊run起來。最后是統(tǒng)計分析,實時計算這樣一些設(shè)備。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  這個圖是現(xiàn)在的一個數(shù)據(jù)庫的圖,我們看著這些就是數(shù)據(jù)庫已經(jīng)分得很好了,比如通訊的數(shù)據(jù)庫,有調(diào)度的有卡片有分析。我在這里介紹一下調(diào)度這個新出現(xiàn)的名詞,它是干嘛?就是你在這個系統(tǒng)里邊點(diǎn)開一個車也好,點(diǎn)開的人也好,聊天時用戶看到的是一個瓜子的客服,后邊瓜子內(nèi)部實際上是一個瓜子的客戶團(tuán)隊非常大的團(tuán)隊來支持你,所以說到底你跟哪個客服聊天,是有一些策略的。我們感覺這樣一個拆分實際上是順理成章的,但是事實上根本就不是。我舉個例子,2015年大概是京東內(nèi)部有一個分享,劉強(qiáng)東分享流露出來了,說有一個二手車企業(yè)一年還是一個月,我忘了,賣了兩輛車出去,估值就到了2億美金。簡直不敢相信。

  我分享這個例子并不是說話有什么不對,當(dāng)然我相信他也不是說的瓜子,因為瓜子A輪它不止這個數(shù),我想說最初企業(yè)是很小的,業(yè)務(wù)量是很小的,我們根本就不可能是這樣一個數(shù)據(jù)庫的結(jié)構(gòu),就跟沈老師說的一樣,其實它就是一個庫也沒有什么IM,沒有什么調(diào)度,這些卡片可能就是一個賣車的一個數(shù)據(jù)庫。

  我是去年的2月份進(jìn)入瓜子的,快兩年了,那時候瓜子的業(yè)務(wù)量非常小,具體我也不知道,就是一個數(shù)據(jù)庫,一個數(shù)據(jù)庫其實是非常好的,因為很多人來了之后就說要拆庫,一個數(shù)據(jù)庫的好處是寫業(yè)務(wù)很快,十幾個人快速的就把系統(tǒng)就搭起來了。我們事實上庫拆成這么幾個也經(jīng)歷了一個過程,最初是做IM只有一個IM的庫,后來有了調(diào)度加了一個庫,再后來有什么卡片,有分析逐步得往外擴(kuò)。這個庫它其實是有一定的成本,如果你拆分得不好,你會去做很多接口,比如說你像關(guān)聯(lián)查一下,發(fā)現(xiàn)不是我團(tuán)隊的庫也要做各種各樣接口,產(chǎn)生了分布式的事物的一致性的問題,都產(chǎn)生了。所以說數(shù)據(jù)庫的拆分,尤其垂直拆分,實際上是隨著你不同的階段,你選擇不同的拆分方式,以后隨著系統(tǒng)的擴(kuò)大瓜子業(yè)務(wù)擴(kuò)大這個系統(tǒng)它會拆的更多數(shù)據(jù)庫,但拆的更多,對你的運(yùn)維監(jiān)控這些團(tuán)隊的挑戰(zhàn)都會帶來一些成本,也會帶來一些挑戰(zhàn)。我們現(xiàn)在把它拆成了這樣一個庫,各司其職。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  下面就重點(diǎn)說一下消息,這塊我們怎么存?我們看一下左邊這個圖,左邊這個圖是一般來說很容易理解的消息,怎么存的方式?以前桌面系統(tǒng)經(jīng)常這么干,比如說A要給B發(fā)一個消息,他怎么發(fā)?他就說A用戶端,A這個端我發(fā)一個消息,如果B在線,我就把消息直接發(fā)給他,他給我一個確認(rèn),這個過程就存儲好就結(jié)束了。其實我服務(wù)當(dāng)中不需要存這個消息,如果是A發(fā)給一個C這個C不在線怎么辦?我們也有策略,A把這個消息發(fā)給了C,C如果沒有確認(rèn)說我收到這個消息,我就把這個下邊的第二步,我就把它存到一個離線的數(shù)據(jù)庫里邊等著你C什么時候上線,你就把這個消息拉回去,這個過程就完結(jié)了,這個消息我就給送到了,所以說這個時候的存儲非常簡單,我就一個離線庫,存一下某個人的消息就好了。

  但是這種模式其實是有很多問題的,真正使用的時候,很多產(chǎn)品現(xiàn)在是移動端的手機(jī)端,網(wǎng)絡(luò)首先是不穩(wěn)定,長期處于一個C的狀態(tài),如果你都去監(jiān)控它的狀態(tài),送沒送到,再存儲性能會很差。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  第二個現(xiàn)在的端有很多,有桌面的,有手機(jī)的,有APP端,還有PAD端,有好幾個端,如果都用這種模式,需要為每個端都這里判斷去看傳輸數(shù)據(jù)其實也是很困難的。我們就變了一個方式,第1步來了消息,我們就把消息存到存儲庫,你只要發(fā)消息我就先給你存下來,第2步,同時我還存到一個同步庫里邊。這兩個庫要稍微解釋一下,存儲和同步庫分別來做什么?存儲庫比較好理解,你什么時候都能從庫里邊還原你的消息,把它讀回去,比如說你換了手機(jī),你都可以把消息拉回來。這個同步庫是什么意思?同步庫就是說你沒有換手機(jī),也沒有重新裝系統(tǒng),就是你可能有一段時間離線,離線起來之后,就說我比如假設(shè)一個消息序列,1到100發(fā)給你了,就A發(fā)給C,1到100了。結(jié)果但是C從第70號消息的時候,他就離線了,他就不在線。這樣子,C這個端上線后,第四步把70號消息傳給這個服務(wù)端,說我有70消息同步庫就知道,把70到100的消息發(fā)給C。這樣子消息就是可以送達(dá)這個端,這兩個概念稍微是會有一點(diǎn)模糊,但是沒有關(guān)系,后邊我會接著展開來講。

  也就說一個消息,我們會存一個消息同步庫和一個存儲庫,這個實際上是一個消息同步庫,它是一個模型,我下一張PPT應(yīng)該會講用什么東西來存它。

  如果我們把它理解成一個郵件,你很好理解。我們的郵件,有一個收件箱,同步庫就像一個收件箱,不管是誰發(fā)給你的郵件,群發(fā)地也好,單發(fā)的也好,反正我都給你放一份,在收件箱里面放一份,A把這個郵件放進(jìn)去了消息,你從另一個要取出來,你不管用這個手機(jī)也好用你的蘋果系統(tǒng)筆記本或者windows本也好,也都要去收這個郵件,收的過程就是什么?比如說剛才說第一蘋果系統(tǒng)筆記本,B1說我之前收了前兩封B游標(biāo),它本地有一個消息最大的,說我在2號消息,我收到了,那他把2號消息傳給服務(wù)端,服務(wù)端就說好,后邊2到20號消息都可以收走了,這樣子可以保證這個消息不重不漏地送給客戶端去。

  B2說我之前這個端其實收了十封了,我就從11分開始收,B3說就收過一封,我就從第二封郵件開始收就好了,這樣子就解決了,消息就送過去了。這里有幾個問題,我們同步的過程就是理論上是可以了,但是有幾個問題,第一個就是擴(kuò)散寫擴(kuò)散讀,在這塊跟存儲很相關(guān),擴(kuò)散寫和擴(kuò)散讀有很多討論。舉個例子是什么地方產(chǎn)生的?比如我如果是一個單聊,我們兩個人聊天沒有問題,我肯定把消息都寫給你了。但是事實上很多時候我們是在一個群里邊聊天,給我們銷售,我們的評估師或者機(jī)器人,他們都在里邊聊,用戶也在里邊聊,這個消息我是為每人寫一份,還是我為整個會話也就是這個群,我只寫只存一份,你們都來讀。一個會話的消息,或者說你自己手里的收件箱,我先說結(jié)論,我們是在消息的同步庫里邊采用的擴(kuò)散寫,后邊還有一個存儲庫,存儲庫里邊我們是采用的擴(kuò)散讀的方式,在同步庫里邊,我們每人都寫了一份,這樣子讀的時候很方便。而在存儲的時候,由于我們的存儲速度慢一些,我們是只寫了一份數(shù)據(jù),這一個會話只有一條消息。

  第二個點(diǎn)是事實上在同步過程中,我們遇到了一些問題,有很多的策略需要我們考慮。就是一個消息TimeLine,有時候不同的端有不同的同步策略,比如說有些場景下,我們要求它的每一個端都收到這一條消息,那就是我們的通知,在公司發(fā)的優(yōu)惠券什么的,每個端都要送達(dá)。有些場景人他是希望說你的手機(jī)收了,那你打開桌面,我們就不再給你送這個消息了,按照剛才這一個同步模型,有一些困難的每個端可能都會去搜這個消息,所以說我們就準(zhǔn)備了三個這樣的存儲的號。

  那比如說這個圖上的B1B2B3,當(dāng)前消息的我們另外存在一個最大消息的號。第三個號你所有的里邊搜的消息最靠前的一塊就說比較像B2這樣通過這三個位置的組合,我們可以確定你收取消息的位置或者一個策略,這是這個模型,我們存儲采用什么?我們采用了Redis cluster,我們用了SortedSet結(jié)構(gòu)。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  我專門把它提出來了,因為我們其實在這還踩過一個坑,我要存這個消息了,怎么都得知道這個結(jié)構(gòu)的效率,我們查了一下SortedSet效率還可以,就說它是一個ln這樣子一個效率,所以說在同步庫,如果我們每個用戶只存一部分消息,它的性能是非常高的。這個結(jié)構(gòu)本質(zhì)上是個跳表,跳表結(jié)構(gòu)其實很復(fù)雜,我想在這會上講清楚很難,最后放了兩個圖,就是一本書。

  我們知道這些結(jié)構(gòu),比如像二叉樹或者一些紅黑樹,檢索都有比較好的索引策略,跳表也類似。它比較類似什么,就像我們比如說一本書上有一千頁,我想翻到856頁怎么翻?其實我們有一種方法去前面去找索引去定位什么東西,還有我大概翻到800頁,逐步修正。跳表結(jié)構(gòu)本質(zhì)上比較像翻書,我覺得是翻到一個大的頁,先翻800頁,翻到850,在逐漸翻到860頁。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  下面分享一下這塊我們遇到了一個什么問題!我們SortedSet存儲,存消息,而我們存消息為了全局一致性,用了一個思路。這個算法我們消息是一個長整型,就是上班卡,我先講的是沒關(guān)系,先講下面精度丟失的問題,我們的消息是一個長整型。這個場景下總共是64位,所以說snowflake這個算法,它的前面第一位不用,它其實表示正整數(shù)它是有意義的。用41表示一個時間區(qū)間,這里面產(chǎn)生一些ID代表了大概有六七十年或者三四十年,反正是肯定是夠用了。

  中間十位是一個工作機(jī)編號,他可以支持1024個臺機(jī)器,我們現(xiàn)階段用不了這么多機(jī)器,最后的12位是一個毫秒內(nèi)的一個序號,所以說構(gòu)成了我們消息的ID因此消息是很長的一串,算下來得18位的整數(shù)。

  放到SortedSet里邊之后,我們后來就發(fā)現(xiàn)一些問題,發(fā)現(xiàn)這個時間靠的近的消息,我們區(qū)分不出來它的先后順序。就這深挖下去,發(fā)現(xiàn)SortedSet它十個字實際上是個double類型的,下邊這個圖是double類型的描述,它的精度只有52位,上邊長整型它是有63位的精度,這里邊就有11位的差距。所以說在那個毫?xí)r間很接近的消息,它的精度丟失,我們檢索拉取的時候,這些順序就出了問題。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  因此我們采用了一個策略,也可以借鑒一下,根據(jù)我們的當(dāng)時的負(fù)載量以及機(jī)器數(shù),這個最終保證了我們幾乎遇不到這種精度丟失的問題,就把精度主動的轉(zhuǎn)換降低了。這個case上說明就是我們選擇存儲的時候,數(shù)據(jù)類型很重要,你得根據(jù)你的業(yè)務(wù)類型看一下。

  我們還在這同步的時候遇到一些問題,就是這個問題更多出現(xiàn)在我們內(nèi)部的一個工具,我們有很多的人數(shù)比較大的群,因為我們的消息像一個收件箱,他的大小是有限制的,有些大群它瘋狂的刷消息,那這樣子這個群里邊可能就有成百上千上萬的消息,因為我們收件箱大小有限制,我們就會把之前更早的消息淘汰掉,導(dǎo)致一些單聊比較重要的消息就丟失了,這個是我們的遇到的問題,后邊的PPT會有解決方案。

  第二個問題就是還有一些web端,我們web端,其實本地的緩存是很難用的,這個就是我們用戶一打開之后,它有多少未讀數(shù),只能先通過我們把消息拉回去算一下,新拉到多少消息,才能計算出它有多少未讀數(shù)。這個實際上對我們也是一個挑戰(zhàn),很不友好。

  接下來我們講一下存儲這塊,我們存儲消息要落庫了,我們怎么存消息?我們剛才提到了,是按照每個會話你看到的每個人跟你聊天的一個維度來存儲。我們也是采用了分庫的策略,分庫比較簡單。這舉個例子,事實上這個庫不止這么多個,我們把一個他的消息繪畫的ID除以四,取它的模來確定它到底放到哪個庫里邊,剛才提到了我們很多ID是用snowflake算法來生成的,我們有個方法來防止它的生存不均,看一下。這是一個我們防止它的ID分布不均的一個方案。我們看到最后有一個12位的序列號,如果你不加任何干預(yù),他每次都從零開始,事實上當(dāng)你并發(fā)比較小的時候,你會發(fā)現(xiàn)它后邊都是零,就最后幾位都是你這樣子,如果都是零,你用是你用固定的取模的算法,他就絕對是不平均了。

  比如說你的數(shù)據(jù)庫不夠了,你到時要擴(kuò)容的時候你就發(fā)現(xiàn)很困難,你不知道以前的數(shù)據(jù),你只能把以前的數(shù)據(jù)全部翻出來,簡直是災(zāi)難,所以說我們需要人為的干預(yù),我們就是用取模的方式,把分庫進(jìn)行特殊的處理,加上一些分庫的行為,在這個里邊我們用了一個ID生成的時間,給他一個最后八位的一個遮罩,他在128個數(shù)據(jù)庫的時候,它分布會很平均。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  說一下怎么擴(kuò)容。剛才之前提到的最開始業(yè)務(wù)量很少,但是前幾天瓜子一天已經(jīng)賣出1萬輛的車了,所以說這個量現(xiàn)在我們是逐步的會在起來,當(dāng)成交1萬輛,用戶量是非常大的。我們怎么擴(kuò)容,這就是擴(kuò)容的基本方法。我們最初有db0123這樣幾個庫,我們看一下左邊有就是這個圖的左邊,一個msg:chatid=100和msg:chatid=104,以前chatid除以四的時候,100和104這兩個數(shù)據(jù),這兩個數(shù)據(jù)它都會并重db0這個庫。我們分庫的時候怎么做?第一步我們把db0123這樣的庫同時進(jìn)項,我就要主從同步,反正在搞db4567,db0和db4數(shù)據(jù)一樣的db1和db5數(shù)據(jù)也一樣,相對應(yīng)的一樣。我們把分庫策略改成除以八求余,之后的結(jié)果就出現(xiàn)什么?我們就發(fā)現(xiàn)按照新的分庫策略,chatid104還在db0里面,chatid100它由于除了之后他就到db4了,這樣子我們就相當(dāng)于是從四個庫直接就變成了八個庫,之后的過程就是分庫規(guī)則上線之后,我們再由DBA把不屬于庫的其他數(shù)據(jù)給刪掉,這個庫的擴(kuò)容就搞定了,所以說業(yè)務(wù)可以接著跑,但是這樣子是不是就解決問題了?其實沒有簡單,遠(yuǎn)遠(yuǎn)沒有,因為你像我們的數(shù)據(jù)庫前面的數(shù)據(jù)庫是MySQL,為了安全,他有一重兩重可能還有擴(kuò)庫,簡直這個數(shù)據(jù)庫越來越多,它運(yùn)維和DBA他就不干了,說你這庫越來越多,我怎么維護(hù),這個受不了了。

  所以說還有一個就是這種關(guān)系型數(shù)據(jù)庫,它可以支持像事務(wù)有好多事務(wù)關(guān)聯(lián)性查詢這些非常豐富的邏輯,但事實上我們一個消息都最多的一個數(shù)據(jù)量的應(yīng)用,它用不上這么復(fù)雜,它是很簡單的,我要么根據(jù)消息ID查某一條消息,要么根據(jù)一個消息要檢索這個范圍之間的消息,真的用不上這么復(fù)雜的一些邏輯,所以說存儲用關(guān)系型數(shù)據(jù)庫并不是說特別適合,那我們就去研究了。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  我們首先一查發(fā)現(xiàn)有一個時序型的數(shù)據(jù)庫是一個OpenTSDB,他的應(yīng)用場景跟這個業(yè)務(wù)很相似。OpenTSDB它內(nèi)部是用Hbase來實現(xiàn)的,我們覺得Hbase就很好。我們?yōu)槭裁催x擇Hbase?因為有團(tuán)隊維護(hù),非?,F(xiàn)實。選用了Hbase之后,這個接下來如果用過Hbase的同學(xué)就知道,除了我們要分片,這些做好了之后,最關(guān)鍵的是要設(shè)計Rowkey設(shè)計是需要結(jié)合業(yè)務(wù),而且需要設(shè)計得非常精妙,我們的Rowkey結(jié)果就是一個會話chatid ID,Chatid可以分散region,msgid 時間有序用于范圍檢索。

  而如果我們用這個你去咨詢,你會經(jīng)常的一個場景,我打開了看到的最新的消息,我往下劃一劃才是加載更老的消息,這個結(jié)構(gòu)正好一來了之后,檢索你最新的消息,你往下滑的時候,我們就接著去查后邊的消息,這樣子非常快,而如果當(dāng)?shù)厥裁炊紱]有,你重新新裝一個非常方便,你直接來Hbase里邊查詢最新的Rowkey你就找到你最新的消息了,這個就解決了。

  還有一點(diǎn)就是region,就像分庫一樣,Hbase做的比較好,它可以自己幫你維護(hù)這個分片,但是我們不建議這么搞自己維護(hù)分片,當(dāng)你像這種消息的數(shù)據(jù)它存儲量是很小的,它很小會導(dǎo)致默認(rèn)給你一個region,但是這樣一個讀寫瓶頸就來了,所以說我們需要提前規(guī)劃我們分庫的region.

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  下面是一些群,群有一些特殊的地方,在我們的二手車APP上,這種大規(guī)模的群比較小,但是我想分享的是我們在內(nèi)部通訊里邊群遇到的一些問題也帶上來,就一起把它跟大家交流一下。

  第一個就是剛才提到的減少存儲量。這個是下面的存儲庫,比如有很多群,可能有2000多人有,如果我發(fā)一條消息就存2000份,那簡直是災(zāi)難,所以說我們只能存一份,因此我們看這個圖就是左邊的藍(lán)色之后,我們只存了一份,標(biāo)明了這個消息ID,標(biāo)明了這是哪個會話或者是哪個群的。

  因為存了一份,第二個問題就帶來了,如果今天加群的人,昨天加群的人其實看到的消息應(yīng)該是不一樣的。正常業(yè)務(wù)是這樣,有時候你還可以看到最近多少條的邏輯怎么實現(xiàn),就是我們在再給它擴(kuò)展一個數(shù)據(jù)庫表,這個表是關(guān)系型數(shù)據(jù)庫里的,記錄上群的號碼,記錄上這個人的ID,記錄上他加群的時間,加群的時間我們可以通過一個函數(shù)把它運(yùn)算。所以說msg ID的策略很重要,我們經(jīng)過加群時間,由于它是一個時間的函數(shù),我們可以跟這個加群的時間進(jìn)行一個映射關(guān)系,這樣子我通過加群時間能夠大概定位到他從哪條消息可以檢索,如果你需要去做策略,也可以說上面看多少條,下邊看多少條都可以做。第三個就是有一個會話排序的問題,這種對話的場景里邊,我們可以看到會有很多的會話,所以說這是一個策略的選擇。

  第一種做法,你可以為每個人建一個會話,他每有一條消息,你就把他的最后時間更新一下,這個過程就能滿足會話的排序,但事實上我們能不能這么做?我覺得我們不能這么做,因為有些時候消息很多,而且有些時候用戶很大,我們發(fā)一條消息。有一千個人要去更新他的狀態(tài),不管你用多少都是扛不住的,所以會話的策略,我們也是在緩存轉(zhuǎn)存會話的最后一條消息量。當(dāng)用戶要來拉取他的會話列表,或者更新他的會話列表的時候,由服務(wù)器端給他預(yù)算好了之后返回給他,我們用的時候正常情況下與客戶端它本地是可以收到消息,如果你在線他是自己知道調(diào)整這個數(shù)據(jù)的。拉取會話的行為,當(dāng)它發(fā)生離線了再次打開,這個時候需要更新一下,如果這個頻率比較低這樣一個取舍,我們的存儲模型也就出來了,所以說其他很多業(yè)務(wù)都是在發(fā)生的時候我們就跟蹤她的狀態(tài),而這個會話排序我們是在比如說讀取的時候我們才可以建立這個過程。

  后邊的已讀未讀,這個點(diǎn)不再細(xì)講,沒有什么特征。我們知道緩存里為每條消息都建了一個存儲結(jié)構(gòu),說這條消息哪些人已讀哪些未讀,在比較短的時間把它淘汰。消息撤回這塊提一下,之前有個小同學(xué)這么干,這個消息怎么撤回?在關(guān)系型數(shù)據(jù)庫里邊,這個消息要撤回,我在表里邊把這條消息標(biāo)記上,這條消息是撤回來的,這個做法有沒有問題?一點(diǎn)都沒有問題。

  之后他又來了個需求,說我就想看一下這些沒有撤回的消息拿出來怎么辦?這個同學(xué)也是剛畢業(yè)沒多久,就調(diào)整,就想到了建索引,他就把索引建好了,就可以這么去拉取數(shù)據(jù),結(jié)果跑一段時間數(shù)據(jù)庫報警了。這不行,怎么回事?因為撤回的消息跟正常沒撤回的消息比例是失衡的非常小一間隔索引,所以毫無意義,而且還消耗了寫消息的性能,因此我們撤回消息后來兩種做法,第一是把它從這個消息庫里邊刪掉,挪到一個撤回的消息表里邊,這是顯而易見的。還有一種做法就是我們也打標(biāo)記,但是不做索引,我也不支持你過濾接受,而我是無差別的拉出來之后在存儲的邏輯層那邊把它過濾掉,這樣子做。

  下邊有提到了,我們講存儲結(jié)構(gòu)不光是一個簡單的一個數(shù)據(jù)庫這樣一個簡單的概念,它其實在db到業(yè)務(wù)之間還會有一些叫做約定也好,規(guī)范也好,或者降低復(fù)雜度也好,因為你直接讓業(yè)務(wù)去處理它是不好的,所以我們有存儲的邏輯,這樣邏輯層做一些基本的邏輯。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  這里跟大家分享一下,瓜子它APP的地位還不夠高,我第一次用的時候一點(diǎn)它要登陸,因此我們要做一些匿名的策略,我們希望匿名的狀態(tài)下你已經(jīng)能建立溝通了,如果你覺得可以我們再接著聊,賣車也好,買車也好,所以說匿名就對我們這個業(yè)務(wù)帶來一個挑戰(zhàn),匿名的時候,我們可能給他分了一個ID,他聊著聊著覺得可以了,它就登錄了,登錄了之后,他實名的時候,他實名有可能是新創(chuàng)建的一個,也可能他之前就登過,但是由于忘了,或者是時間久了過期了,這個時候他在這一次的業(yè)務(wù)過程當(dāng)中,他就兩個ID,如果一直讓它成為兩個ID其實對后邊的電銷人員是很郁悶的,說我們開始跟我聊了一下,過會變了個人其實是一個人,前面的業(yè)務(wù)也中斷了,所以說我們對這個消息層面我們就進(jìn)行了一個Merge,這個我們并沒有說你實名,我們就把你的數(shù)據(jù)給搬家,按照這個實名的就是匿名有一個時間序列,實名是不是也有一個,我們并沒有這么搞,我們還是兩個,而是在存儲中間的一個層次進(jìn)行拉取的過程,在需要Merge的時候,我們在存儲邏輯上給他Merge.但是匿名到實名遠(yuǎn)沒有簡單,只是一個延伸,事實上你這個消息里面的匿名很好做,但是你的業(yè)務(wù)匿名到實名很難,還有我們經(jīng)常遇到這個問題,機(jī)器人給他發(fā)了一個東西,匿名狀態(tài),后來他登陸了,他一打開,拉回去了之后,這個消息還在他那里,他變成實名了。他進(jìn)行操作,這個時候業(yè)務(wù)的匿名到實名其實是更難的,如果有做這樣想法的,提前想好,更多的是業(yè)務(wù)層面的理論都是。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  這個是消息的最后一部分了,實際上還會遇到一些問題,事實上消息同步是非常復(fù)雜的一個事情,我們后來越做越覺得它復(fù)雜。這個有些人會出現(xiàn)一個什么情況,比如說我用A手機(jī)收了幾條消息,我在B手機(jī)上又收了幾條消息,過一段時間我在A手機(jī)上又來收幾條消息。

  你看剛才那種模型就會導(dǎo)致中間出現(xiàn)很多斷層,中間出現(xiàn)了很多,就像Client右邊這個圖,就345的消息他并沒有收到,但是服務(wù)端其實是所有消息都有的。這時候我們做了一些策略,我們?yōu)槊總€消息嚴(yán)格的編號,msg index:1,2,3,每個消息嚴(yán)格的編號。如果是這樣子,客戶端知道了之后,他就知道我原來少了345這三個號對不對?我就可以到服務(wù)端去說,我缺345這幾個消息,你給我解索出來。有沒有這么容易?客戶端可能覺得這個是很容易的,但是到了服務(wù)端事情不是這么回事,345是你自己編的一個號,而我們的消息之前說了snow?ake這樣一個唯一的編號,那你拿著345并不能找到你到底是哪個消息ID,所以說我是不是服務(wù)端要用哪個消息建立這么一個索引,還是應(yīng)該是一個編號到msg ID索引?可以做,但是存儲量工作量非常的大,那我們怎么干?我們在邏輯層里邊做了一些事情,服務(wù)端每次返回客戶端的消息,我們把這個消息把它做成一個鏈表的結(jié)構(gòu),當(dāng)你來拉取,因為是反向消息好多是吧?拉去2號消息的時候,我就說,這個我告訴你,你的下一條消息是msg7,你拉取,也可以一段一段拉取沒關(guān)系。你拉到msg6的時候,我告訴你說你的消息msg5,我客戶端說原來少這個消息5,這樣子客戶端可以通過這個消息ID到服務(wù)端來檢索消息,由于是消息ID不管我們是OK也好,或者我們的關(guān)系型數(shù)據(jù)庫是基于索引也好,都很容易做,現(xiàn)成的,也不需要再維護(hù)其他的索引關(guān)系。所以說這也是一個策略的點(diǎn)。這種斷層我們就解決了。

  但是還有問題,有時候消息非常多,如果你一次都把這些就是我們剛才說的同步庫的消息收過去,過程其實是很慢的,尤其在深度用戶的時候這個方案不好。有一種做法,你把這個消息壓縮一下送過去,簡直傳遞。但是其實還是不好,客戶端要渲染,要計算數(shù)量很慢,這就是剛才提到的擴(kuò)散讀和擴(kuò)散寫的問題,最早有一個,所以說后來更好的辦法是說同步庫里邊也并不是去同步的具體的消息,你可以去做這個用戶有哪些變更的會話,這么一個會話,它有多少未接收的數(shù)據(jù),記錄好這個數(shù)字有多少未讀。這樣客戶端可以把這些數(shù)據(jù)拉到本地,你看到了有多少未讀的會話之后,你點(diǎn)進(jìn)去的時候,你再照這個存儲庫里邊反向的通過這個來拉取你的消息,再加上我們剛才說的中間空蕩的一個補(bǔ)齊的策略,一個列表補(bǔ)齊的策略,這樣的體驗非常好。

  所以說我們就解決了我們這個項目,我們就解決了消息的問題。后邊我們看一下消息解決了還沒有完,我們要推廣這個項目,我們要落地,需要做業(yè)務(wù),因為只是傳一個消息沒有意義,對觀眾來說我們就在做業(yè)務(wù)了,我們承載業(yè)務(wù)的就是叫我們的業(yè)務(wù)卡片,最初那個圖里邊我們看到的那些傳過去可以直接操作的這個東西,應(yīng)該我們還申請了專利,當(dāng)時去查了一下沒人這么搞的,但是由于沒人這么搞,其實我們在實施過程中遇到一些問題,下面我們看一下這個圖。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  這個圖右邊有一個卡片的代理,右圖有個綠色的卡片代理是我們對這個業(yè)務(wù)設(shè)置的一個特殊的東西,我們在推廣的時候遇到很多問題,我們這些業(yè)務(wù)原有的業(yè)務(wù)部門,他們都做得有接口是現(xiàn)成的,由于你把它搬到了IM交互里面有幾種方法:第一你們?nèi)拷o我改一下,這個是很困難,有些業(yè)務(wù)說他不愿意,我們就設(shè)計了這么一個代理的產(chǎn)出的卡片的所有響應(yīng)試點(diǎn),先打到我們的一個代理模塊,代理模塊再去適配你原有的業(yè)務(wù)邏輯,這樣子代理模塊,知道用戶操作行為到底是什么樣,成沒成功,成功了或者沒成功,他再通過調(diào)度,通過他們的通道通知相關(guān)業(yè)務(wù)的各方結(jié)果,這是一種策略。

  同時它要高可靠,比如說我們預(yù)約看車就相當(dāng)于下班了,就相當(dāng)于這個是很重要的業(yè)務(wù)。你這個不行,鏈條太長了,風(fēng)險太高,寧可我們加點(diǎn)東西都可以,那就是左邊這個邏輯,這業(yè)務(wù)服務(wù)愿意說我自己改一下,可控性我得自己把控,不能說因為通訊有問題,我的業(yè)務(wù)就不跑了。那就是他改一下,來觸發(fā)調(diào)度的一些邏輯。這個是我們在整個推廣的過程當(dāng)中最重要的一個策略,實際上也是探索出來的,因為不光是一個技術(shù)問題,還是個組織結(jié)構(gòu)問題。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  簡單提一下調(diào)度問題,因為不是重點(diǎn),你怎么知道到底是哪個客戶來服務(wù)你?我們提出了一個場景的概念,就是你每次從各種入口進(jìn)到對話界面的時候,這些入口我們是有狀態(tài)的。A入口B入口,比如你約車還是砍價什么之類的,我們它先把這個場景到調(diào)度去注冊一下,說我從這兒進(jìn)來,同時調(diào)度會有一些大數(shù)據(jù)來支撐,原來你是誰誰,你就從這個場景進(jìn)來,我們認(rèn)為你可能是要干什么事情。這樣給他返回場景里,他再次跟通道間發(fā)生關(guān)系的時候,帶上這個場景,我們這個調(diào)度就知道把你推給具體的誰,是銷售也好,機(jī)器人也好,是我們具體的解決投訴的客服或者解決什么電銷的客服了。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  接下來再提一下分析統(tǒng)計,像瓜子的規(guī)模已經(jīng)比較大了,我們現(xiàn)在有超過一千個研發(fā)團(tuán)隊,但是在大數(shù)據(jù)這塊投入也比較多,但是事實上現(xiàn)在公司都是數(shù)據(jù)驅(qū)動化,這個團(tuán)隊的力量依然是很有限的,它支持各種各樣的業(yè)務(wù)線,非常吃力的還是很忙,所以我們在分析統(tǒng)計有兩塊,第一塊是T+1的分析是離線的,還有一塊是實時的一個分析。

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  我們這個項目于對實時統(tǒng)計的要求很高,比如及時回復(fù)率這些各種各樣的統(tǒng)計要求實時的監(jiān)控報警,怎么做呢?這是我們整個系統(tǒng)另一個角度的一個架構(gòu),和我們一些跟消息相關(guān)的或跟一些業(yè)務(wù)調(diào)度相關(guān)的,我們都走了一個kafka,就是通道的以及送給客服的一些銷售評估師等等,他們業(yè)務(wù)線的這些邏輯,我們通過kafka傳遞一些比較多的數(shù)據(jù)。我們在想能不能用借用kafka來簡單的實現(xiàn)技術(shù),事實上是可以,這概念是一個流式數(shù)據(jù)庫,我們最初的結(jié)構(gòu)就是圖左邊這一塊,整個系統(tǒng)中間走的消息都通過了一個kafka.

瓜子二手車封宇:瓜子IM智能客服系統(tǒng)數(shù)據(jù)架構(gòu)設(shè)計

  我們可以保證業(yè)務(wù)在上面跑,其次kafka它緩存的這段數(shù)據(jù),我們是可以對他進(jìn)行流式計算,我們整體的架構(gòu)是上圖這樣。

  最后我簡單重復(fù)一下:我們的過程,第一我們這個系統(tǒng)通過數(shù)據(jù)層面展示了一個通訊,就是即時通訊的這樣一個系統(tǒng),大概是怎么做的,數(shù)據(jù)庫怎么存的;第二是把我們通訊的能力應(yīng)用到業(yè)務(wù)系統(tǒng),我們解決了技術(shù)上或者組織上遇到了一些什么困難;第三是我們找一個比較簡單的方法,處理我們的一些離線計算,當(dāng)然他做T+1也是可以的,謝謝大家。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI