分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

滴滴技術發表於2019-05-06

出品 | 滴滴技術

作者 | 餘汶龍

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

前言:

Fusion 是滴滴自研的分散式 NoSQL 資料庫,完全相容 Redis 協議,支援超大規模資料持久化和高效能讀寫。在滴滴內部支撐了數百個業務,具有 PB 級別的資料儲存量,是使用最廣泛的主儲存服務之一。在支援滴滴業務高速發展過程中,積累了很多分散式儲存領域的經驗,孵化了離線到線上的高速資料匯入方案、NewSQL 方案、跨機房同步等,一路解決了 Redis 容量限制、 離線資料線上打通、資料庫擴充套件性差、異地多活容災等問題。

本文來自滴滴的技術專家、Fusion 的負責人餘汶龍在 2018 年北京 ArchSummit 全球架構師峰會上的演講內容,重點介紹了 Fusion 的核心設計以及架構演進過程。

內容框架

  • 誕生背景:滴滴業務發展簡介

  • 演進過程:如何滿足業務需求

    海量儲存

    FastLoad

    NewSQL

    跨機房多活

  • 總結 & 展望

誕生背景

  • 業務 & 架構演進過程

滴滴出行成立於 2012 年,剛開始創業階段技術主要靠外包解決,沒太多技術沉澱;發展到了 2014 年,乘客司機和單量都有不錯的增長,我們開始構建自己的系統架構,這個時候業務對於儲存的需求很單純,簡單用用 MySQL 基本能解決我們的問題。

到了 2015 年前後,我們的業務線多了起來,專車快車等開始上線,這個時候我們一方面做了中臺系統的重構,另一方面感受到了不小的儲存壓力,即業務資料量和請求量劇增;到了 2016 年,合併優步前後,日訂單量逼近 2000 萬,進一步挑戰我們的儲存系統,於是我們按照不同的業務,對儲存進行拆分,因為不同的業務對於儲存的需求是不一樣的,不同的業務對於吞吐、延遲、容量、資料請求大小等都有不同的需求,分庫分表也只是緩兵之計。

如何有效應對這些個性化需求呢?於是在這個時候,我們就開始孵化滴滴自己的 NoSQL 資料庫 Fusion 了,用它來豐富我們滴滴的儲存生態,為業務提供更多的儲存選擇。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • Fusion 是什麼?

前面我們不斷提到 Fusion 的關鍵字,那麼是時候正式介紹下 Fusion。Fusion 是一個相容 Redis 協議的分散式 NoSQL 資料庫。定位介於 Redis 與 MySQL 之間的主儲存資料庫。怎麼理解這個定位呢?也就是效能方面我們向 Redis 看齊,即低延遲;持久化能力方面我們向 MySQL 看齊,即 MySQL 具備的多副本、高可用、ACID 事務,我們都是支援的,同時定位為服務叫車訂單這樣的主流程線上業務。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

它如何實現的呢?大家都知道 Redis 的資料是存放於記憶體中,雖然效能很好,但是容量極小,且每 GB 儲存成本很高(大概是我們 Fusion 的 10 倍以上)。於是我們就基於 SSD 磁碟實現了一套分散式的儲存系統,在 SSD 磁碟上實現了 Redis 的資料結構,對外通過 proxy 遮蔽內部細節,讓使用者像訪問 Redis 一樣訪問 Fusion。當前我們已經支援 String\Hash\Bitmap\Set\Sorted Set\List 這些主流的 Redis 資料結構。

演進過程

我們 Fusion 的發展總共經歷了 4 個階段,分別解決了 4 類業務問題,我們接下來重點看下具體過程。

  • 海量儲存

首先來看如何解決海量儲存的問題。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

Redis 是一款非常優秀的記憶體資料庫,但它也有這樣一些已知問題存在:容量受限於記憶體、擴容遷移和大 key 過期、刪除過程是阻塞的、當機恢復慢等問題。我們 Fusion 設計之初,就避免了這些問題。具體是如何實現的呢?我們從需求分析出發。

 需求分析

Fusion 誕生初期,主要解決 2 個業務需求:

一是滴滴的歷史訂單,按照前面提到的每日千萬級別訂單量,很快就能達到幾百億的訂單,這麼龐大的資料量,存 MySQL 顯然是不夠靈活的,修改欄位、修改索引都比較困難,存 Redis 就更加不可能,因此他們有新型儲存的需求;

二是地圖團隊的司機行程軌跡,每產生一條叫車訂單就會產生一條司機行程軌跡,每一條行程軌跡由多個點組成,行程越長軌跡資料越大,這是一個比歷史訂單的資料量還要大的業務,儲存的困難可想而知。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

因此,我們對上述兩個業務的需求做了提煉和優先順序排定:

  1. 剛需是海量儲存。

  2. 具備基本的線上故障處理能力。

  3. 穩定性很重要!

  4. 效能要足夠好,以司機行程軌跡為例,每天 300 億級別寫入,他們對效能的追求當然是越高越好。

  5. 接入要求簡單,這裡我們選擇了 Redis 協議。

  6. 打通其他儲存系統。

滿足了這些需求後,就誕生了儲存系統 Fusion 的雛形。

  • 架構設計

· 軟體結構

下圖左邊是資料流部分,從下往上看,即 Fusion 是構建在 SSD 磁碟上的儲存服務,我們引用優秀的儲存引擎 RocksDB 來做磁碟 IO 操作,然後在磁碟之上,我們增加一層 cache 來提升效能,然後封裝一層網路框架並支援 Redis RPC,就實現了單機版本的 Fusion 儲存節點,然後在單機的基礎上加上我們的叢集路由管理,Fusion 的叢集就搭建好了,當然對外提供服務的時候,還有一層負載均衡。

下圖右邊是控制流部分,即我們在 SaltStack 平臺基礎上,構建了使用者系統、運維繫統、統計、監控、計費等系統,方便使用者以及運維人員使用。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

· 叢集架構

叢集架構上,我們採用 hash 分片的方式來做資料 sharding。從上往下看,使用者通過 Redis 協議的客戶端(jedis、redigo、hiredis 等)就可以訪問 Fusion,首先會經過 VIP 做負載均衡,然後轉發到具體 proxy,再由 proxy 轉發資料到後端 Fusion 的資料節點。

proxy 到後端資料節點的轉發,是根據請求的 key 計算 hash 值,然後對 slot 分片數取餘,得到一個固定的 slotid,每個 slotid 會固定的對映到一個儲存節點,以此解決資料路由問題。

此外,我們還做了儲存生態的打通。支援 Hadoop、MySQL、Redis 的資料同步到 Fusion,也支援 Fusion 資料同步到 MQ,供下游消費。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

小結

接下來就對 Fusion 做個小結,拿 Redis 來做個簡單對比。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

FastLoad

我們演進過程中,解決的第二個問題是,離線資料到線上系統的快速打通。因此我們做了一個叫 FastLoad 的系統。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 需求分析

首先,FastLoad 誕生初期主要支援兩個業務:標籤平臺和特徵平臺。標籤平臺是指對每個乘客和司機,都打上 N 個標籤,然後後續的叫車流程會依賴這部分標籤,比如優惠券的發放;然後特徵平臺呢,會收集建立各類特徵,對每個物件用某個特徵庫做一次判斷,即可確定某種行為。接下來我們對需求進行提取。

  1. 高效能。由於這部分資料是在離線計算平臺 Hadoop 上加工完成的,業務很容易想到就近存放在 Hive 上,但 Hive 的查詢效能實在不能滿足線上查詢的高吞吐、低延遲要求。因此對於新的儲存系統,他們第一個要求就是效能!

  2. 定時更新。像特徵資料,一般只需要小時級別甚至天級別的更新,所以業務需要有快捷的定時更新功能。

  3. 快速更新。特徵資料還有一個特點,就是資料量特別大,以乘客特徵為例,動輒上億條資料,約 TB 級別資料量。這麼大的資料量通過 SDK 寫入肯定是不行的。剛開始業務方也確實是這麼玩的,直接通過 Hadoop 任務呼叫 Redis SDK,然後一條條的寫入 Fusion,一般是每天凌晨開始寫資料,等到早高峰 8 點時大量讀取。但是這種方法實踐下來,經常導致 Fusion 各類超時,在早高峰叫車已經來臨

    時還在寫凌晨的資料,非常影響穩定性。因此第 3 個需求是必須快速更新。

  4. 穩定性。這個是毋容置疑的。

  5. 多表隔離。有些業務有很多類特徵資料,他們有隔離儲存的需求,也有分類更新、分類查詢的需求,因此需要多表來支援邏輯到物理的隔離。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 架構設計

滿足上述需求後,就誕生了我們的 FastLoad 系統。接下來就來看下我們的架構是如何設計的。我們給使用者提供兩種接入方式:控制檯和 OpenAPI。使用者通過任一一種方式提交 FastLoad 任務時,都會在我們的 FastLoad 伺服器上,建立一個 DTS 任務,該任務會在 Hadoop 配置中心註冊一個排程任務(週期性或一次性,由使用者決定),然後 FastLoad 伺服器根據使用者上傳的資料儲存路徑或 Hive 表(我們支援的資料來源有:HDFS 上的 JSON 檔案和 Hive 結構的資料),按照使用者提交的拼 key 方式,我們啟動 map/reduce 任務直接構造 Fusion 底層儲存在檔案系統上的檔案 SST,並把它們構造好相互之間的排序,避免重複,構造好後通知 Fusion 儲存節點,下載 SST 檔案,然後 load 到 Fusion 資料庫中。此後,使用者就可以通過 Redis-Client 訪問我們幫它載入的資料了。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 小結

總結一下我們的 FastLoad 一站式 DTS 平臺,有如下優勢:

  1. 減少 N 次網路互動。相比呼叫 Redis SDK 的方式寫入,我們減少非常多的網路互動,傳輸的是壓縮格式檔案,節省了網路頻寬。

  2. 對使用者請求 0 影響。我們利用 map/reduce 的計算能力,做了 SST 的全域性排序,讓 SST 進入 Fusion 的時候,不經由 L0,直接到達最終 level,避免了 LSM 的 compact 影響,因此對使用者可以說沒有影響。

  3. 接入簡單,使用者 0 感知細節。使用者既不需要關心 Hadoop 使用、任務排程,也不需要自己寫 Redis SDK 的程式碼,只需要告訴我們,在什麼時間點需要什麼樣的資料即可!

  4. 提供了 OpenAPI,方便使用者的自動化流程打通。

  5. 提供全量覆蓋和增量匯入兩種方式。分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

NewSQL

在演進過程的第 3 個階段,我們主要是針對 MySQL 的。大家都知道 MySQL 的擴充套件性比較差,面對百億級儲存,有幾個問題,一個是資料存不下,一個是擴充套件不靈活,比如修改欄位、修改索引等。接著就來討論下,我們是如何解決這類問題的。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 需求分析

同樣的,我們先來分析下業務的需求是什麼?簡單理解下,我們認為有 3 點剛需:

  1. 輕鬆改欄位。即需要足夠的擴充套件性。

  2. 儲存不限量。即需要一個容量儘可能大的儲存。

  3. 省成本。既然需要存 MySQL 都存不下的資料,那麼成本一定要考慮清楚。

至於事務、穩定性、高效能、二級索引,我們認為都是基本需求。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 背景問題

· 如何實現 shema 到 key/value 的轉換?

前面的介紹我們知道,Fusion 是支援 Redis 協議的,那麼 schema 轉換成 key/value,就可以用 Redis 的 hash 結構來實現,下圖我們就以 student 表為例,轉換了 2 行資料。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

· 如何做主鍵查詢呢?

下面的圖片給出了一個例子,即查詢 ID 為 1 的學生的全部資訊或年齡。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

· 如何實現二級索引呢?

我們還是以 student 表為例,分別構建如下 age\sex 索引,其編碼規則如下可見。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

· 如何做非主鍵查詢和範圍查詢呢?

在上圖構建好索引後,就很容易實現下面的兩個例子,即查詢年齡在某個範圍的學生,和查詢某種性別的所有學生。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

· 背景問題

架構設計上分成接入層和資料儲存層,在接入層(DISE)我們提供控制檯來管理使用者的欄位,使用者可以在這裡定義自己的 schema、欄位、索引,並做相應的修改。然後使用者通過我們提供的類 SQL 的 SDK 接入,由我們的 SchemaServer 做 schema 轉換,接著插入資料到儲存層。然後資料儲存層吐出 binlog,由 IndexServer 非同步消費 binlog 並構建索引。查詢時候,使用者的請求經由 SDK 到達 SchemaServer,SchemaServer 先查詢索引伺服器,拿到對應的主鍵資訊,然後根據命中的主鍵查詢詳細資訊,最後返回給使用者。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 小結

NewSQL 解決的問題是針對 MySQL 的特殊場景的,我們就拿 MySQL 來跟 Fusion 做個對比,可以看到 Fusion 只是在部分場景上解決了 MySQL 的容量限制以及擴充套件問題,但還是有很多場景並不能支援的。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

跨機房多活建設

最後一個演進我們講的是如何支援業務的跨機房容災問題。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 背景介紹

滴滴多活的業務架構如下圖,可以看到使用者層接入層和業務層都是無狀態的,因此如圖中的白色虛線所描述的,他們的請求可以在兩個機房間來回路由,而不影響業務請求的正確性。那麼是如何做到這一點的呢?必然得有一個地方維護著狀態的一致性,才能讓業務自由切換。因此跨機房多活容災最複雜的部分就在底層資料同步層,這裡的資料同步涉及到很多中介軟體,我們這裡只關心 Fusion 的跨機房多活。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 架構設計

下圖是 Fusion 的跨機房同步架構,不依賴任何外部中介軟體,也不依賴內部 proxy。當使用者資料通過 A 機房寫入時,落地到某個儲存節點上,該儲存節點會 cache 一份對端節點的路由表,並非同步的將剛才寫入的資料轉發到對端叢集。

我們這裡的轉發採用了兩個非同步特性:1. 跟使用者寫入主流程完全非同步,即不影響使用者正常請求;2. A 機房到 B 機房的資料同步,採用了非同步、批量、應答的方式高效同步。既保證了使用者請求主機房的吞吐和延遲,也大幅降低了備機房資料讀取的延遲。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 小結

到此總結下我們的多活方案:

  1. 非同步資料複製。在追求效能的同時,放棄了一段時間的不一致。如果在資料未達成一致的時候,主機房當機,備機房的資料將缺失,但這個缺失不會是永久,等到主機房恢復後,我們會把這部分資料自動補齊到備機房,這個過程我們會根據時間戳去重。

  2. 自適應感知叢集狀態變更,比如切主、擴容等。在執行過程中,兩個機房的叢集難免會發生各類路由變化,我們在設計時考慮到了這一點,針對路由變化,我們會及時更新路由表,以把資料同步到正確的節點上。

  3. 資料可靠同步。我們的資料同步是依賴滑動視窗的應答機制,因此實現了一種可靠的資料同步。

  4. 支援雙寫,解決秒級衝突。由第一點提到,在某些場景是存在雙寫的,如果雙寫的是不同 key,自然不需要解決衝突,如果雙寫的是針對同一個 key,那麼我們會根據時間戳做衝突檢測。

  5. 自動資料補償。也就是在發生主機房當機後,寫入備機房的增量資料,可以自動的補償到主機房;原先滯留在主機房的資料,在主機房恢復後,也可以補償到備機房。即可以達到最終一致性。

    分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

總結與展望

  • 總結

在伴隨滴滴業務發展的過程中,Fusion 經歷了 4 個發展階段,我們堅持”好東西是用出來“,因此在每個階段,都儘量避免”過度設計“,只解決特定的業務問題。這給了我們很多認真打磨產品的時間和精力,讓我們贏得了使用者口碑。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 展望

通過前面的分享我們知道,Fusion 雖然能做的事情很多,但不能做的事情更多,所以我們的目標是持續發展持續演進,把解決業務問題當做己任。未來我們將往分散式資料庫方向前進,解決更多的業務問題。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路

  • 余文瀧,滴滴出行技術專家,2016年加入滴滴,從零開始構建滴滴自研的分散式 NoSQL 資料庫 Fusion。

分享 | 滴滴分散式NoSQL資料庫Fusion的演進之路



相關文章