<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          專車數(shù)據(jù)層「架構(gòu)進化」往事

          共 5261字,需瀏覽 11分鐘

           ·

          2022-05-10 14:42

          很多年前,讀了子柳老師的《淘寶技術(shù)這十年》。這本書成為了我的架構(gòu)啟蒙書,書中的一句話像種子一樣深埋在我的腦海里:“好的架構(gòu)是進化來的,不是設(shè)計來的”。

          2015年,我加入神州專車訂單研發(fā)團隊,親歷了專車數(shù)據(jù)層「架構(gòu)進化」的過程。這次工作經(jīng)歷對我而言非常有啟發(fā)性,也讓我經(jīng)常感慨:“好的架構(gòu)果然是一點點進化來的”。

          1 單數(shù)據(jù)庫架構(gòu)

          產(chǎn)品初期,技術(shù)團隊的核心目標是:“快速實現(xiàn)產(chǎn)品需求,盡早對外提供服務(wù)”。

          彼時的專車服務(wù)都連同一個 SQLServer 數(shù)據(jù)庫,服務(wù)層已經(jīng)按照業(yè)務(wù)領(lǐng)域做了一定程度的拆分。

          這種架構(gòu)非常簡單,團隊可以分開協(xié)作,效率也極高。隨著專車訂單量的不斷增長,早晚高峰期,用戶需要打車的時候,點擊下單后經(jīng)常無響應(yīng)。

          系統(tǒng)層面來看:

          1. 數(shù)據(jù)庫瓶頸顯現(xiàn)。頻繁的磁盤操作導(dǎo)致數(shù)據(jù)庫服務(wù)器 IO 消耗增加,同時多表關(guān)聯(lián),排序,分組,非索引字段條件查詢也會讓 cpu 飆升,最終都會導(dǎo)致數(shù)據(jù)庫連接數(shù)激增;
          2. 網(wǎng)關(guān)大規(guī)模超時。在高并發(fā)場景下,大量請求直接操作數(shù)據(jù)庫,數(shù)據(jù)庫連接資源不夠用,大量請求處于阻塞狀態(tài)。

          2 ?SQL優(yōu)化和讀寫分離

          為了緩解主數(shù)據(jù)庫的壓力,很容易就想到的策略:SQL優(yōu)化。通過性能監(jiān)控平臺和 DBA 同學(xué)協(xié)作分析出業(yè)務(wù)慢 SQL ,整理出優(yōu)化方案:

          1. 合理添加索引;
          2. 減少多表 JOIN 關(guān)聯(lián),通過程序組裝,減少數(shù)據(jù)庫讀壓力;
          3. 減少大事務(wù),盡快釋放數(shù)據(jù)庫連接。

          另外一個策略是:讀寫分離。

          讀寫分離的基本原理是讓主數(shù)據(jù)庫處理事務(wù)性增、改、刪操作( INSERT、UPDATE、DELETE),而從數(shù)據(jù)庫處理 SELECT 查詢操作。

          專車架構(gòu)團隊提供的框架中,支持讀寫分離,于是數(shù)據(jù)層架構(gòu)進化為如下圖:

          讀寫分離可以減少主庫寫壓力,同時讀從庫可水平擴展。當然,讀寫分離依然有局限性:

          1. 讀寫分離可能面臨主從延遲的問題,訂單服務(wù)載客流程中對實時性要求較高,因為擔(dān)心延遲問題,大量操作依然使用主庫查詢;
          2. 讀寫分離可以緩解讀壓力,但是寫操作的壓力隨著業(yè)務(wù)爆發(fā)式的增長并沒有很有效的緩解。

          3 ?業(yè)務(wù)領(lǐng)域分庫

          雖然應(yīng)用層面做了優(yōu)化,數(shù)據(jù)層也做了讀寫分離,但主庫的壓力依然很大。接下來,大家不約而同的想到了業(yè)務(wù)領(lǐng)域分庫,也就是:將數(shù)據(jù)庫按業(yè)務(wù)領(lǐng)域拆分成不同的業(yè)務(wù)數(shù)據(jù)庫,每個系統(tǒng)僅訪問對應(yīng)業(yè)務(wù)的數(shù)據(jù)庫。

          業(yè)務(wù)領(lǐng)域分庫可以緩解核心訂單庫的性能壓力,同時也減少系統(tǒng)間的相互影響,提升了系統(tǒng)整體穩(wěn)定性。

          隨之而來的問題是:原來單一數(shù)據(jù)庫時,簡單的使用 JOIN 就可以滿足需求,但拆分后的業(yè)務(wù)數(shù)據(jù)庫在不同的實例上,就不能跨庫使用 JOIN了,因此需要對系統(tǒng)邊界重新梳理,業(yè)務(wù)系統(tǒng)也需要重構(gòu)

          重構(gòu)重點包含兩個部分:

          1. 原來需要 JOIN 關(guān)聯(lián)的查詢修改成 RPC 調(diào)用,程序中組裝數(shù)據(jù) ;
          2. 業(yè)務(wù)表適當冗余字段,通過消息隊列或者異構(gòu)工具同步。

          4 緩存和MQ

          專車服務(wù)中,訂單服務(wù)是并發(fā)量和請求量最高,也是業(yè)務(wù)中最核心的服務(wù)。雖然通過業(yè)務(wù)領(lǐng)域分庫,SQL 優(yōu)化提升了不少系統(tǒng)性能,但訂單數(shù)據(jù)庫的寫壓力依然很大,系統(tǒng)的瓶頸依然很明顯。

          于是,訂單服務(wù)引入了 緩存? MQ?。

          乘客在用戶端點擊立即叫車,訂單服務(wù)創(chuàng)建訂單,首先保存到數(shù)據(jù)庫后,然后將訂單信息同步保存到緩存中。

          在訂單的載客生命周期里,訂單的修改操作先修改緩存,然后發(fā)送消息到 MetaQ?,訂單落盤服務(wù)消費消息,并判斷訂單信息是否正常(比如有無亂序),若訂單數(shù)據(jù)無誤,則存儲到數(shù)據(jù)庫中。

          核心邏輯有兩點:

          1. 緩存集群中存儲最近七天訂單詳情信息,大量訂單讀請求直接從緩存獲??;
          2. 在訂單的載客生命周期里,寫操作先修改緩存,通過消息隊列異步落盤,這樣消息隊列可以起到消峰的作用,同樣可以降低數(shù)據(jù)庫的壓力。

          這次優(yōu)化提升了訂單服務(wù)的整體性能,也為后來訂單服務(wù)庫分庫分表以及異構(gòu)打下了堅實的基礎(chǔ)。

          5 從 SQLServer 到 MySQL

          業(yè)務(wù)依然在爆炸增長,每天幾十萬訂單,訂單表數(shù)據(jù)量很快將過億,數(shù)據(jù)庫天花板遲早會觸及。

          訂單分庫分表已成為技術(shù)團隊的共識。業(yè)界很多分庫分表方案都是基于 MySQL 數(shù)據(jù)庫,專車技術(shù)管理層決定先將訂單庫整體先從 SQLServer 遷移到 MySQL 。

          遷移之前,準備工作很重要 :

          1. SQLServer 和 MySQL 兩種數(shù)據(jù)庫語法有一些差異,訂單服務(wù)必須要適配 MySQL 語法。
          2. 訂單 order_id 是主鍵自增,但在分布式場景中并不合適,需要將訂單 id 調(diào)整為分布式模式。

          當準備工作完成后,才開始遷移。

          遷移過程分兩部分:歷史全量數(shù)據(jù)遷移 增量數(shù)據(jù)遷移。

          歷史數(shù)據(jù)全量遷移主要是 DBA 同學(xué)通過工具將訂單庫同步到獨立的 MySQL 數(shù)據(jù)庫。

          增量數(shù)據(jù)遷移:因為 SQLServer 無 binlog 日志概念,不能使用 maxwell 和 canal 等類似解決方案。訂單團隊重構(gòu)了訂單服務(wù)代碼,每次訂單寫操作的時候,會發(fā)送一條 MQ 消息到 MetaQ 。為了確保遷移的可靠性,還需要將新庫的數(shù)據(jù)同步到舊庫,也就是需要做到雙向同步

          遷移流程:

          1. 首先訂單服務(wù)(SQLServer版)發(fā)送訂單變更消息到 MetaQ ,此時并不開啟「舊庫消息消費」,讓消息先堆積在 MetaQ 里;
          2. 然后開始遷移歷史全量數(shù)據(jù),當全量遷移完成后,再開啟「舊庫消息消費」,這樣新訂單庫就可以和舊訂單庫數(shù)據(jù)保持同步了;
          3. 開啟「新庫消息消費」,然后部署訂單服務(wù)( MySQL 版),此時訂單服務(wù)有兩個版本同時運行,檢測數(shù)據(jù)無誤后,逐步增加新訂單服務(wù)流量,直到老訂單服務(wù)完全下線。

          6 自研分庫分表組件

          業(yè)界分庫分表一般有 proxy 和 client 兩種流派。

          ▍ proxy模式

          代理層分片方案業(yè)界有 Mycat ,cobar 等 。

          它的優(yōu)點:應(yīng)用零改動,和語言無關(guān),可以通過連接共享減少連接數(shù)消耗。缺點:因為是代理層,存在額外的時延。

          ▍ client模式

          應(yīng)用層分片方案業(yè)界有 sharding-jdbcTDDL 等。

          它的優(yōu)點:直連數(shù)據(jù)庫,額外開銷小,實現(xiàn)簡單,輕量級中間件。缺點:無法減少連接數(shù)消耗,有一定的侵入性,多數(shù)只支持Java語言。

          神州架構(gòu)團隊選擇自研分庫分表組件,采用了 client 模式 ,組件命名:SDDL

          訂單服務(wù)需要引入是 SDDL 的 jar 包,在配置中心配置 數(shù)據(jù)源信息?sharding key??,路由規(guī)則 等,訂單服務(wù)只需要配置一個 datasourceId?即可。

          7 分庫分表策略

          7.1 乘客維度

          專車訂單數(shù)據(jù)庫的查詢主維度是:乘客,乘客端按乘客 user_id 和 訂單 order_id 查詢頻率最高,我們選擇 user_id 做為 sharding key?,相同用戶的訂單數(shù)據(jù)存儲到同一個數(shù)據(jù)庫中。

          分庫分表組件 SDDL 和阿里開源的數(shù)據(jù)庫中間件 cobar 路由算法非常類似的。

          為了便于思維擴展,先簡單介紹下 cobar 的分片算法。

          假設(shè)現(xiàn)在需要將訂單表平均拆分到4個分庫 shard0 ,shard1 ,shard2 ,shard3 。首先將 [0-1023] 平均分為4個區(qū)段:[0-255],[256-511],[512-767],[768-1023],然后對字符串(或子串,由用戶自定義)做 hash, hash 結(jié)果對1024取模,最終得出的結(jié)果 slot?落入哪個區(qū)段,便路由到哪個分庫。

          cobar 的默認路由算法 ,可以和 雪花算法?天然融合在一起, 訂單 order_id?使用雪花算法,我們可以將 slot 的值保存在 10位工作機器ID?里。

          通過訂單 order_id 可以反查出 slot , 就可以定位該用戶的訂單數(shù)據(jù)存儲在哪個分區(qū)里。

          Integer?getWorkerId(Long?orderId)?{
          ?Long?workerId?=?(orderId?>>?12)?&?0x03ff;
          ?return?workerId.intValue();
          }

          專車 SDDL 分片算法和 cobar 差異點在于:

          1. cobar 支持最大分片數(shù)是1024,而 SDDL 最大支持分庫數(shù)1024*8=8192,同樣分四個訂單庫,每個分片的 slot 區(qū)間范圍是2048 ;
          1. 因為要支持8192個分片,雪花算法要做一點微調(diào),雪花算法的10位工作機器修改成13位工作機器,時間戳也調(diào)整為:38位時間戳(由某個時間點開始的毫秒數(shù))。

          7.2 ?司機維度

          雖然解決了主維度乘客分庫分表問題,但專車還有另外一個查詢維度,在司機客戶端,司機需要查詢分配給他的訂單信息。

          我們已經(jīng)按照乘客 user_id 作為 sharding key ,若按照司機 driver_id 查詢訂單的話,需要廣播到每一個分庫并聚合返回,基于此,技術(shù)團隊選擇將乘客維度的訂單數(shù)據(jù)異構(gòu)到以司機維度的數(shù)據(jù)庫里。

          司機維度的分庫分表策略和乘客維度邏輯是一樣的,只不過 sharding key 變成了司機 driver_id 。

          異構(gòu)神器 canal 解析乘客維度四個分庫的 binlog ,通過 SDDL 寫入到司機維度的四個分庫里。

          這里大家可能有個疑問:雖然可以異構(gòu)將訂單同步到司機維度的分庫里,畢竟有些許延遲,如何保證司機在司機端查詢到最新的訂單數(shù)據(jù)呢 ?

          緩存和MQ這一小節(jié)里提到:緩存集群中存儲最近七天訂單詳情信息,大量訂單讀請求直接從緩存獲取。訂單服務(wù)會緩存司機和當前訂單的映射,這樣司機端的大量請求就可以直接緩存中獲取,而司機端查詢訂單列表的頻率沒有那么高,異構(gòu)復(fù)制延遲在10毫秒到30毫秒之間,在業(yè)務(wù)上是完全可以接受的。

          7.3 ?運營維度

          專車管理后臺,運營人員經(jīng)常需要查詢訂單信息,查詢條件會比較復(fù)雜,專車技術(shù)團隊采用的做法是:訂單數(shù)據(jù)落盤在乘客維度的訂單分庫之后,通過 canal 把數(shù)據(jù)同步到Elastic Search。

          7.4 小表廣播

          業(yè)務(wù)中有一些配置表,存儲重要的配置,讀多寫少。在實際業(yè)務(wù)查詢中,很多業(yè)務(wù)表會和配置表進行聯(lián)合數(shù)據(jù)查詢。但在數(shù)據(jù)庫水平拆分后,配置表是無法拆分的。

          小表廣播的原理是:將小表的所有數(shù)據(jù)(包括增量更新)自動廣播(即復(fù)制)到大表的機器上。這樣,原來的分布式 JOIN 查詢就變成單機本地查詢,從而大大提高了效率。

          專車場景下,小表廣播是非常實用的需求。比如:城市表是非常重要的配置表,數(shù)據(jù)量非常小,但訂單服務(wù),派單服務(wù),用戶服務(wù)都依賴這張表。

          通過 canal 將基礎(chǔ)配置數(shù)據(jù)庫城市表同步到訂單數(shù)據(jù)庫,派單數(shù)據(jù)庫,用戶數(shù)據(jù)庫。

          8 平滑遷移

          分庫分表組件 SDDL?研發(fā)完成,并在生產(chǎn)環(huán)境得到一定程度的驗證后,訂單服務(wù)從單庫 MySQL 模式遷移到分庫分表模式條件已經(jīng)成熟。

          遷移思路其實和從 SQLServer 到 MySQL 非常類似。

          整體遷移流程:

          1. DBA 同學(xué)準備乘客維度的四個分庫,司機維度的四個分庫 ,每個分庫都是最近某個時間點的全量數(shù)據(jù);
          2. 八個分庫都是全量數(shù)據(jù),需要按照分庫分表規(guī)則刪除八個分庫的冗余數(shù)據(jù) ;
          3. 開啟正向同步,舊訂單數(shù)據(jù)按照分庫分表策略落盤到乘客維度的分庫,通過 canal 將乘客維度分庫訂單數(shù)據(jù)異構(gòu)復(fù)制到司機維度的分庫中;
          4. 開啟反向同步,修改訂單應(yīng)用的數(shù)據(jù)源配置,重啟訂單服務(wù),訂單服務(wù)新創(chuàng)建的訂單會落盤到乘客維度的分庫,通過 canal 將乘客維度分庫訂單數(shù)據(jù)異構(gòu)到全量訂單庫以及司機維度的數(shù)據(jù)庫;
          5. 驗證數(shù)據(jù)無誤后,逐步更新訂單服務(wù)的數(shù)據(jù)源配置,完成整體遷移。

          9 數(shù)據(jù)交換平臺

          專車訂單已完成分庫分表 , 很多細節(jié)都值得復(fù)盤:

          1. 全量歷史數(shù)據(jù)遷移需要 DBA 介入 ,技術(shù)團隊沒有成熟的工具或者產(chǎn)品輕松完成;
          2. 增量數(shù)據(jù)遷移通過 canal 來實現(xiàn)。隨著專車業(yè)務(wù)的爆發(fā)增長,數(shù)據(jù)庫鏡像,實時索引構(gòu)建,分庫異構(gòu)等需求越來越多,雖然canal 非常優(yōu)秀,但它還是有瑕疵,比如缺失任務(wù)控制臺,數(shù)據(jù)源管理能力,任務(wù)級別的監(jiān)控和報警,操作審計等功能。

          面對這些問題,架構(gòu)團隊的目標是打造一個平臺,滿足各種異構(gòu)數(shù)據(jù)源之間的實時增量同步和離線全量同步,支撐公司業(yè)務(wù)的快速發(fā)展。

          基于這個目標,架構(gòu)團隊自研了 dataLink 用于增量數(shù)據(jù)同步,深度定制了阿里開源的 dataX 用于全量數(shù)據(jù)同步。

          10 寫到最后

          專車架構(gòu)進化之路并非一帆風(fēng)順,也有波折和起伏,但一步一個腳印,專車的技術(shù)儲備越來越深厚。

          2017年,瑞幸咖啡在神州優(yōu)車集團內(nèi)部孵化,專車的這些技術(shù)儲備大大提升了瑞幸咖啡技術(shù)團隊的研發(fā)效率,并支撐業(yè)務(wù)的快速發(fā)展。比如瑞幸咖啡的訂單數(shù)據(jù)庫最開始規(guī)劃的時候,就分別按照用戶維度,門店維度各拆分了8個數(shù)據(jù)庫實例,分庫分表組件 SDDL 數(shù)據(jù)交換平臺都起到了關(guān)鍵的作用 。

          好了,這篇文字就寫到這里了。我們下期見。


          往期推薦:

          如何實現(xiàn)一個任務(wù)調(diào)度系統(tǒng)

          后端工程師的「跨域」之旅

          品味Spring Cache設(shè)計之美

          我與消息隊列的八年情緣

          如果我的文章對你有所幫助,還請幫忙點贊、在看、轉(zhuǎn)發(fā)一下,你的支持會激勵我輸出更高質(zhì)量的文章,非常感謝!


          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  奇米影视7777久久精品 | 色婷婷五月综合 | 精品二区三区影院AV | 天天操人人色 | 日本韩国亚洲天堂网 |