出品 | 滴滴技術
做者 | 餘汶龍redis
前言:Fusion 是滴滴自研的分佈式 NoSQL 數據庫,徹底兼容 Redis 協議,支持超大規模數據持久化和高性能讀寫。在滴滴內部支撐了數百個業務,具備 PB 級別的數據存儲量,是使用最普遍的主存儲服務之一。在支持滴滴業務高速發展過程當中,積累了不少分佈式存儲領域的經驗,孵化了離線到在線的高速數據導入方案、NewSQL 方案、跨機房同步等,一路解決了 Redis 容量限制、 離線數據在線打通、數據庫擴展性差、異地多活容災等問題。數據庫
本文來自滴滴的技術專家、Fusion 的負責人餘汶龍在 2018 年北京 ArchSummit 全球架構師峯會上的演講內容,重點介紹了 Fusion 的核心設計以及架構演進過程。後端
內容框架服務器
海量存儲
FastLoad
NewSQL
跨機房多活網絡
誕生背景數據結構
業務 & 架構演進過程架構
滴滴出行成立於 2012 年,剛開始創業階段技術主要靠外包解決,沒太多技術沉澱;發展到了 2014 年,乘客司機和單量都有不錯的增加,咱們開始構建本身的系統架構,這個時候業務對於存儲的需求很單純,簡單用用 MySQL 基本能解決咱們的問題。負載均衡
到了 2015 年先後,咱們的業務線多了起來,專車快車等開始上線,這個時候咱們一方面作了中臺系統的重構,另外一方面感覺到了不小的存儲壓力,即業務數據量和請求量劇增;到了 2016 年,合併優步先後,日訂單量逼近 2000 萬,進一步挑戰咱們的存儲系統,因而咱們按照不一樣的業務,對存儲進行拆分,由於不一樣的業務對於存儲的需求是不同的,不一樣的業務對於吞吐、延遲、容量、數據請求大小等都有不一樣的需求,分庫分表也只是緩兵之計。框架
如何有效應對這些個性化需求呢?因而在這個時候,咱們就開始孵化滴滴本身的 NoSQL 數據庫 Fusion 了,用它來豐富咱們滴滴的存儲生態,爲業務提供更多的存儲選擇。運維
Fusion 是什麼?
前面咱們不斷提到 Fusion 的關鍵字,那麼是時候正式介紹下 Fusion。Fusion 是一個兼容 Redis 協議的分佈式 NoSQL 數據庫。定位介於 Redis 與 MySQL 之間的主存儲數據庫。怎麼理解這個定位呢?也就是性能方面咱們向 Redis 看齊,即低延遲;持久化能力方面咱們向 MySQL 看齊,即 MySQL 具有的多副本、高可用、ACID 事務,咱們都是支持的,同時定位爲服務打車訂單這樣的主流程在線業務。
它如何實現的呢?你們都知道 Redis 的數據是存放於內存中,雖然性能很好,可是容量極小,且每 GB 存儲成本很高(大概是咱們 Fusion 的 10 倍以上)。因而咱們就基於 SSD 磁盤實現了一套分佈式的存儲系統,在 SSD 磁盤上實現了 Redis 的數據結構,對外經過 proxy 屏蔽內部細節,讓用戶像訪問 Redis 同樣訪問 Fusion。當前咱們已經支持 StringHashBitmapSetSorted SetList 這些主流的 Redis 數據結構。
演進過程
咱們 Fusion 的發展總共經歷了 4 個階段,分別解決了 4 類業務問題,咱們接下來重點看下具體過程。
海量存儲
首先來看如何解決海量存儲的問題。
Redis 是一款很是優秀的內存數據庫,但它也有這樣一些已知問題存在:容量受限於內存、擴容遷移和大 key 過時、刪除過程是阻塞的、宕機恢復慢等問題。咱們 Fusion 設計之初,就避免了這些問題。具體是如何實現的呢?咱們從需求分析出發。
需求分析
Fusion 誕生初期,主要解決 2 個業務需求:
一是滴滴的歷史訂單,按照前面提到的每日千萬級別訂單量,很快就能達到幾百億的訂單,這麼龐大的數據量,存 MySQL 顯然是不夠靈活的,修改字段、修改索引都比較困難,存 Redis 就更加不可能,所以他們有新型存儲的需求;
二是地圖團隊的司機行程軌跡,每產生一條打車訂單就會產生一條司機行程軌跡,每一條行程軌跡由多個點組成,行程越長軌跡數據越大,這是一個比歷史訂單的數據量還要大的業務,存儲的困難可想而知。
所以,咱們對上述兩個業務的需求作了提煉和優先級排定:
知足了這些需求後,就誕生了存儲系統 Fusion 的雛形。
架構設計
軟件結構
下圖左邊是數據流部分,從下往上看,即 Fusion 是構建在 SSD 磁盤上的存儲服務,咱們引用優秀的存儲引擎 RocksDB 來作磁盤 IO 操做,而後在磁盤之上,咱們增長一層 cache 來提高性能,而後封裝一層網絡框架並支持 Redis RPC,就實現了單機版本的 Fusion 存儲節點,而後在單機的基礎上加上咱們的集羣路由管理,Fusion 的集羣就搭建好了,固然對外提供服務的時候,還有一層負載均衡。
下圖右邊是控制流部分,即咱們在 SaltStack 平臺基礎上,構建了用戶系統、運維繫統、統計、監控、計費等系統,方便用戶以及運維人員使用。
集羣架構
集羣架構上,咱們採用 hash 分片的方式來作數據 sharding。從上往下看,用戶經過 Redis 協議的客戶端(jedis、redigo、hiredis 等)就能夠訪問 Fusion,首先會通過 VIP 作負載均衡,而後轉發到具體 proxy,再由 proxy 轉發數據到後端 Fusion 的數據節點。
proxy 到後端數據節點的轉發,是根據請求的 key 計算 hash 值,而後對 slot 分片數取餘,獲得一個固定的 slotid,每一個 slotid 會固定的映射到一個存儲節點,以此解決數據路由問題。
此外,咱們還作了存儲生態的打通。支持 Hadoop、MySQL、Redis 的數據同步到 Fusion,也支持 Fusion 數據同步到 MQ,供下游消費。
小結
接下來就對 Fusion 作個小結,拿 Redis 來作個簡單對比。
FastLoad
咱們演進過程當中,解決的第二個問題是,離線數據到在線系統的快速打通。所以咱們作了一個叫 FastLoad 的系統。
需求分析
首先,FastLoad 誕生初期主要支持兩個業務:標籤平臺和特徵平臺。標籤平臺是指對每一個乘客和司機,都打上 N 個標籤,而後後續的打車流程會依賴這部分標籤,好比優惠券的發放;而後特徵平臺呢,會收集建立各種特徵,對每一個對象用某個特徵庫作一次判斷,便可肯定某種行爲。接下來咱們對需求進行提取。
寫入確定是不行的。剛開始業務方也確實是這麼玩的,直接經過 Hadoop 任務調用 Redis SDK,而後一條條的寫入
Fusion,通常是天天凌晨開始寫數據,等到早高峯 8 點時大量讀取。可是這種方法實踐下來,常常致使 Fusion
各種超時,在早高峯打車已經來臨 時還在寫凌晨的數據,很是影響穩定性。所以第 3 個需求是必須快速更新。
架構設計
知足上述需求後,就誕生了咱們的 FastLoad 系統。接下來就來看下咱們的架構是如何設計的。咱們給用戶提供兩種接入方式:控制檯和 OpenAPI。用戶經過任一一種方式提交 FastLoad 任務時,都會在咱們的 FastLoad 服務器上,建立一個 DTS 任務,該任務會在 Hadoop 配置中心註冊一個調度任務(週期性或一次性,由用戶決定),而後 FastLoad 服務器根據用戶上傳的數據存儲路徑或 Hive 表(咱們支持的數據源有:HDFS 上的 JSON 文件和 Hive 結構的數據),按照用戶提交的拼 key 方式,咱們啓動 map/reduce 任務直接構造 Fusion 底層存儲在文件系統上的文件 SST,並把它們構造好相互之間的排序,避免重複,構造好後通知 Fusion 存儲節點,下載 SST 文件,而後 load 到 Fusion 數據庫中。此後,用戶就能夠經過 Redis-Client 訪問咱們幫它加載的數據了。
小結
總結一下咱們的 FastLoad 一站式 DTS 平臺,有以下優點:
NewSQL
在演進過程的第 3 個階段,咱們主要是針對 MySQL 的。你們都知道 MySQL 的擴展性比較差,面對百億級存儲,有幾個問題,一個是數據存不下,一個是擴展不靈活,好比修改字段、修改索引等。接着就來討論下,咱們是如何解決這類問題的。
需求分析
一樣的,咱們先來分析下業務的需求是什麼?簡單理解下,咱們認爲有 3 點剛需:
背景問題
如何實現 shema 到 key/value 的轉換?
前面的介紹咱們知道,Fusion 是支持 Redis 協議的,那麼 schema 轉換成 key/value,就能夠用 Redis 的 hash 結構來實現,下圖咱們就以 student 表爲例,轉換了 2 行數據。
如何作主鍵查詢呢?
下面的圖片給出了一個例子,即查詢 ID 爲 1 的學生的所有信息或年齡。
如何實現二級索引呢?
咱們仍是以 student 表爲例,分別構建以下 agesex 索引,其編碼規則以下可見。
如何作非主鍵查詢和範圍查詢呢?
在上圖構建好索引後,就很容易實現下面的兩個例子,即查詢年齡在某個範圍的學生,和查詢某種性別的全部學生。
背景問題
架構設計上分紅接入層和數據存儲層,在接入層(DISE)咱們提供控制檯來管理用戶的字段,用戶能夠在這裏定義本身的 schema、字段、索引,並作相應的修改。而後用戶經過咱們提供的類 SQL 的 SDK 接入,由咱們的 SchemaServer 作 schema 轉換,接着插入數據到存儲層。而後數據存儲層吐出 binlog,由 IndexServer 異步消費 binlog 並構建索引。查詢時候,用戶的請求經由 SDK 到達 SchemaServer,SchemaServer 先查詢索引服務器,拿到對應的主鍵信息,而後根據命中的主鍵查詢詳細信息,最後返回給用戶。
小結
NewSQL 解決的問題是針對 MySQL 的特殊場景的,咱們就拿 MySQL 來跟 Fusion 作個對比,能夠看到 Fusion 只是在部分場景上解決了 MySQL 的容量限制以及擴展問題,但仍是有不少場景並不能支持的。
跨機房多活建設
最後一個演進咱們講的是如何支持業務的跨機房容災問題。
背景介紹
滴滴多活的業務架構以下圖,能夠看到用戶層接入層和業務層都是無狀態的,所以如圖中的白色虛線所描述的,他們的請求能夠在兩個機房間來回路由,而不影響業務請求的正確性。那麼是如何作到這一點的呢?必然得有一個地方維護着狀態的一致性,才能讓業務自由切換。所以跨機房多活容災最複雜的部分就在底層數據同步層,這裏的數據同步涉及到不少中間件,咱們這裏只關心 Fusion 的跨機房多活。
架構設計
下圖是 Fusion 的跨機房同步架構,不依賴任何外部中間件,也不依賴內部 proxy。當用戶數據經過 A 機房寫入時,落地到某個存儲節點上,該存儲節點會 cache 一份對端節點的路由表,並異步的將剛纔寫入的數據轉發到對端集羣。
咱們這裏的轉發採用了兩個異步特性:1. 跟用戶寫入主流程徹底異步,即不影響用戶正常請求;2. A 機房到 B 機房的數據同步,採用了異步、批量、應答的方式高效同步。既保證了用戶請求主機房的吞吐和延遲,也大幅下降了備機房數據讀取的延遲。
小結
到此總結下咱們的多活方案:
總結&展望
總結
在伴隨滴滴業務發展的過程當中,Fusion 經歷了 4 個發展階段,咱們堅持」好東西是用出來「,所以在每一個階段,都儘可能避免」過分設計「,只解決特定的業務問題。這給了咱們不少認真打磨產品的時間和精力,讓咱們贏得了用戶口碑。
展望
經過前面的分享咱們知道,Fusion 雖然能作的事情不少,但不能作的事情更多,因此咱們的目標是持續發展持續演進,把解決業務問題當作己任。將來咱們將往分佈式數據庫方向前進,解決更多的業務問題。
END