滴滴自研分布式 NoSQL 数据库 Fusion 的演进之路
https://www.infoq.cn/article/hcPzw431w*JIdelpZ6iO
本文來(lái)自滴滴的技術(shù)專家、Fusion 的負(fù)責(zé)人余汶龍?jiān)?2018 年北京 ArchSummit 全球架構(gòu)師峰會(huì)上的演講內(nèi)容,重點(diǎn)介紹了 Fusion 的核心設(shè)計(jì)以及架構(gòu)演進(jìn)過(guò)程。
為了講清楚滴滴自研存儲(chǔ) Fusion 的演進(jìn)過(guò)程,本文將分成 3 個(gè)部分:
-
誕生背景:滴滴業(yè)務(wù)發(fā)展簡(jiǎn)介
-
演進(jìn)過(guò)程:如何滿足業(yè)務(wù)需求
-
海量存儲(chǔ)
-
FastLoad
-
NewSQL
-
跨機(jī)房多活
-
-
總結(jié) & 展望
誕生背景
業(yè)務(wù) & 架構(gòu)演進(jìn)過(guò)程
滴滴公司成立于 2012 年,剛開始創(chuàng)業(yè)階段技術(shù)主要靠外包解決,沒(méi)太多技術(shù)沉淀;發(fā)展到了 2014 年,乘客司機(jī)和單量都有不錯(cuò)的增長(zhǎng),我們開始構(gòu)建自己的系統(tǒng)架構(gòu),這個(gè)時(shí)候業(yè)務(wù)對(duì)于存儲(chǔ)的需求很單純,簡(jiǎn)單用用 MySQL 基本能解決我們的問(wèn)題;到了 2015 年前后,我們的業(yè)務(wù)線多了起來(lái),專車快車等開始上線,這個(gè)時(shí)候我們一方面做了中臺(tái)系統(tǒng)的重構(gòu),另一方面感受到了不小的存儲(chǔ)壓力,即業(yè)務(wù)數(shù)據(jù)量和請(qǐng)求量劇增;到了 2016 年,合并優(yōu)步前后,日訂單量逼近 2000 萬(wàn),進(jìn)一步挑戰(zhàn)我們的存儲(chǔ)系統(tǒng),于是我們按照不同的業(yè)務(wù),對(duì)存儲(chǔ)進(jìn)行拆分,因?yàn)椴煌臉I(yè)務(wù)對(duì)于存儲(chǔ)的需求是不一樣的,不同的業(yè)務(wù)對(duì)于吞吐、延遲、容量、數(shù)據(jù)請(qǐng)求大小等都有不同的需求,分庫(kù)分表也只是緩兵之計(jì)。
如何有效應(yīng)對(duì)這些個(gè)性化需求呢?于是在這個(gè)時(shí)候,我們就開始孵化滴滴自己的 NoSQL 數(shù)據(jù)庫(kù) Fusion 了,用它來(lái)豐富我們滴滴的存儲(chǔ)生態(tài),為業(yè)務(wù)提供更多的存儲(chǔ)選擇。
Fusion 是什么?
前面我們不斷提到 Fusion 的關(guān)鍵字,那么是時(shí)候正式介紹下 Fusion。Fusion 是一個(gè)兼容 Redis 協(xié)議的分布式 NoSQL 數(shù)據(jù)庫(kù)。定位介于 Redis 與 MySQL 之間的主存儲(chǔ)數(shù)據(jù)庫(kù)。怎么理解這個(gè)定位呢?也就是性能方面我們向 Redis 看齊,即低延遲;持久化能力方面我們向 MySQL 看齊,即 MySQL 具備的多副本、高可用、ACID 事務(wù),我們都是支持的,同時(shí)定位為服務(wù)打車訂單這樣的主流程在線業(yè)務(wù)。
它如何實(shí)現(xiàn)的呢?大家都知道 Redis 的數(shù)據(jù)是存放于內(nèi)存中,雖然性能很好,但是容量極小,且每 GB 存儲(chǔ)成本很高(大概是我們 Fusion 的 10 倍以上)。于是我們就基于 SSD 磁盤實(shí)現(xiàn)了一套分布式的存儲(chǔ)系統(tǒng),在 SSD 磁盤上實(shí)現(xiàn)了 Redis 的數(shù)據(jù)結(jié)構(gòu),對(duì)外通過(guò) proxy 屏蔽內(nèi)部細(xì)節(jié),讓用戶像訪問(wèn) Redis 一樣訪問(wèn) Fusion。當(dāng)前我們已經(jīng)支持 String\Hash\Bitmap\Set\Sorted Set\List 這些主流的 Redis 數(shù)據(jù)結(jié)構(gòu)。
演進(jìn)過(guò)程
我們 Fusion 的發(fā)展總共經(jīng)歷了 4 個(gè)階段,分別解決了 4 類業(yè)務(wù)問(wèn)題,我們接下來(lái)重點(diǎn)看下具體過(guò)程。
海量存儲(chǔ)
首先來(lái)看如何解決海量存儲(chǔ)的問(wèn)題。
Redis 是一款非常優(yōu)秀的內(nèi)存數(shù)據(jù)庫(kù),但它也有這樣一些已知問(wèn)題存在:容量受限于內(nèi)存、擴(kuò)容遷移和大 key 過(guò)期、刪除過(guò)程是阻塞的、宕機(jī)恢復(fù)慢等問(wèn)題。我們 Fusion 設(shè)計(jì)之初,就避免了這些問(wèn)題。具體是如何實(shí)現(xiàn)的呢?我們從需求分析出發(fā)。
需求分析
Fusion 誕生初期,主要解決 2 個(gè)業(yè)務(wù)需求:
一是滴滴的歷史訂單,按照前面提到的每日千萬(wàn)級(jí)別訂單量,很快就能達(dá)到幾百億的訂單,這么龐大的數(shù)據(jù)量,存 MySQL 顯然是不夠靈活的,修改字段、修改索引都比較困難,存 Redis 就更加不可能,因此他們有新型存儲(chǔ)的需求;
二是地圖團(tuán)隊(duì)的司機(jī)行程軌跡,每產(chǎn)生一條打車訂單就會(huì)產(chǎn)生一條司機(jī)行程軌跡,每一條行程軌跡由多個(gè)點(diǎn)組成,行程越長(zhǎng)軌跡數(shù)據(jù)越大,這是一個(gè)比歷史訂單的數(shù)據(jù)量還要大的業(yè)務(wù),存儲(chǔ)的困難可想而知。
因此,我們對(duì)上述兩個(gè)業(yè)務(wù)的需求做了提煉和優(yōu)先級(jí)排定:
剛需是海量存儲(chǔ)。
具備基本的在線故障處理能力。
穩(wěn)定性很重要!
性能要足夠好,以司機(jī)行程軌跡為例,每天 300 億級(jí)別寫入,他們對(duì)性能的追求當(dāng)然是越高越好。
接入要求簡(jiǎn)單,這里我們選擇了 Redis 協(xié)議。
打通其他存儲(chǔ)系統(tǒng)。
滿足了這些需求后,就誕生了存儲(chǔ)系統(tǒng) Fusion 的雛形。
架構(gòu)設(shè)計(jì)
軟件結(jié)構(gòu)
下圖左邊是數(shù)據(jù)流部分,從下往上看,即 Fusion 是構(gòu)建在 SSD 磁盤上的存儲(chǔ)服務(wù),我們引用優(yōu)秀的存儲(chǔ)引擎 RocksDB 來(lái)做磁盤 IO 操作,然后在磁盤之上,我們?cè)黾右粚?cache 來(lái)提升性能,然后封裝一層網(wǎng)絡(luò)框架并支持 Redis RPC,就實(shí)現(xiàn)了單機(jī)版本的 Fusion 存儲(chǔ)節(jié)點(diǎn),然后在單機(jī)的基礎(chǔ)上加上我們的集群路由管理,Fusion 的集群就搭建好了,當(dāng)然對(duì)外提供服務(wù)的時(shí)候,還有一層負(fù)載均衡。
下圖右邊是控制流部分,即我們?cè)?SaltStack 平臺(tái)基礎(chǔ)上,構(gòu)建了用戶系統(tǒng)、運(yùn)維系統(tǒng)、統(tǒng)計(jì)、監(jiān)控、計(jì)費(fèi)等系統(tǒng),方便用戶以及運(yùn)維人員使用。
集群架構(gòu)
集群架構(gòu)上,我們采用 hash 分片的方式來(lái)做數(shù)據(jù) sharding。從上往下看,用戶通過(guò) Redis 協(xié)議的客戶端(jedis、redigo、hiredis 等)就可以訪問(wèn) Fusion,首先會(huì)經(jīng)過(guò) VIP 做負(fù)載均衡,然后轉(zhuǎn)發(fā)到具體 proxy,再由 proxy 轉(zhuǎn)發(fā)數(shù)據(jù)到后端 Fusion 的數(shù)據(jù)節(jié)點(diǎn)。proxy 到后端數(shù)據(jù)節(jié)點(diǎn)的轉(zhuǎn)發(fā),是根據(jù)請(qǐng)求的 key 計(jì)算 hash 值,然后對(duì) slot 分片數(shù)取余,得到一個(gè)固定的 slotid,每個(gè) slotid 會(huì)固定的映射到一個(gè)存儲(chǔ)節(jié)點(diǎn),以此解決數(shù)據(jù)路由問(wèn)題。
此外,我們還做了存儲(chǔ)生態(tài)的打通。支持 Hadoop、MySQL、Redis 的數(shù)據(jù)同步到 Fusion,也支持 Fusion 數(shù)據(jù)同步到 MQ,供下游消費(fèi)。
小結(jié)
接下來(lái)就對(duì) Fusion 做個(gè)小結(jié),拿 Redis 來(lái)做個(gè)簡(jiǎn)單對(duì)比。
FastLoad
我們演進(jìn)過(guò)程中,解決的第二個(gè)問(wèn)題是,離線數(shù)據(jù)到在線系統(tǒng)的快速打通。因此我們做了一個(gè)叫 FastLoad 的系統(tǒng)。
需求分析
首先,FastLoad 誕生初期主要支持兩個(gè)業(yè)務(wù):標(biāo)簽平臺(tái)和特征平臺(tái)。標(biāo)簽平臺(tái)是指對(duì)每個(gè)乘客和司機(jī),都打上 N 個(gè)標(biāo)簽,然后后續(xù)的打車流程會(huì)依賴這部分標(biāo)簽,比如優(yōu)惠券的發(fā)放;然后特征平臺(tái)呢,會(huì)收集創(chuàng)建各類特征,對(duì)每個(gè)對(duì)象用某個(gè)特征庫(kù)做一次判斷,即可確定某種行為。接下來(lái)我們對(duì)需求進(jìn)行提取。
高性能。由于這部分?jǐn)?shù)據(jù)是在離線計(jì)算平臺(tái) Hadoop 上加工完成的,業(yè)務(wù)很容易想到就近存放在 Hive 上,但 Hive 的查詢性能實(shí)在不能滿足在線查詢的高吞吐、低延遲要求。因此對(duì)于新的存儲(chǔ)系統(tǒng),他們第一個(gè)要求就是性能!
定時(shí)更新。像特征數(shù)據(jù),一般只需要小時(shí)級(jí)別甚至天級(jí)別的更新,所以業(yè)務(wù)需要有快捷的定時(shí)更新功能。
快速更新。特征數(shù)據(jù)還有一個(gè)特點(diǎn),就是數(shù)據(jù)量特別大,以乘客特征為例,動(dòng)輒上億條數(shù)據(jù),約 TB 級(jí)別數(shù)據(jù)量。這么大的數(shù)據(jù)量通過(guò) SDK 寫入肯定是不行的。剛開始業(yè)務(wù)方也確實(shí)是這么玩的,直接通過(guò) Hadoop 任務(wù)調(diào)用 Redis SDK,然后一條條的寫入 Fusion,一般是每天凌晨開始寫數(shù)據(jù),等到早高峰 8 點(diǎn)時(shí)大量讀取。但是這種方法實(shí)踐下來(lái),經(jīng)常導(dǎo)致 Fusion 各類超時(shí),在早高峰打車已經(jīng)來(lái)臨時(shí)還在寫凌晨的數(shù)據(jù),非常影響穩(wěn)定性。因此第 3 個(gè)需求是必須快速更新。
穩(wěn)定性。這個(gè)是毋容置疑的。
多表隔離。有些業(yè)務(wù)有很多類特征數(shù)據(jù),他們有隔離存儲(chǔ)的需求,也有分類更新、分類查找的需求,因此需要多表來(lái)支持邏輯到物理的隔離。
架構(gòu)設(shè)計(jì)
滿足上述需求后,就誕生了我們的 FastLoad 系統(tǒng)。接下來(lái)就來(lái)看下我們的架構(gòu)是如何設(shè)計(jì)的。我們給用戶提供兩種接入方式:控制臺(tái)和 OpenAPI。用戶通過(guò)任一一種方式提交 FastLoad 任務(wù)時(shí),都會(huì)在我們的 FastLoad 服務(wù)器上,創(chuàng)建一個(gè) DTS 任務(wù),該任務(wù)會(huì)在 Hadoop 配置中心注冊(cè)一個(gè)調(diào)度任務(wù)(周期性或一次性,由用戶決定),然后 FastLoad 服務(wù)器根據(jù)用戶上傳的數(shù)據(jù)存儲(chǔ)路徑或 Hive 表(我們支持的數(shù)據(jù)源有:HDFS 上的 JSON 文件和 Hive 結(jié)構(gòu)的數(shù)據(jù)),按照用戶提交的拼 key 方式,我們啟動(dòng) map/reduce 任務(wù)直接構(gòu)造 Fusion 底層存儲(chǔ)在文件系統(tǒng)上的文件 SST,并把它們構(gòu)造好相互之間的排序,避免重復(fù),構(gòu)造好后通知 Fusion 存儲(chǔ)節(jié)點(diǎn),下載 SST 文件,然后 load 到 Fusion 數(shù)據(jù)庫(kù)中。此后,用戶就可以通過(guò) Redis-Client 訪問(wèn)我們幫它加載的數(shù)據(jù)了。
小結(jié)
總結(jié)一下我們的 FastLoad 一站式 DTS 平臺(tái),有如下優(yōu)勢(shì):
減少 N 次網(wǎng)絡(luò)交互。相比調(diào)用 Redis SDK 的方式寫入,我們減少非常多的網(wǎng)絡(luò)交互,傳輸?shù)氖菈嚎s格式文件,節(jié)省了網(wǎng)絡(luò)帶寬。
對(duì)用戶請(qǐng)求 0 影響。我們利用 map/reduce 的計(jì)算能力,做了 SST 的全局排序,讓 SST 進(jìn)入 Fusion 的時(shí)候,不經(jīng)由 L0,直接到達(dá)最終 level,避免了 LSM 的 compact 影響,因此對(duì)用戶可以說(shuō)沒(méi)有影響。
接入簡(jiǎn)單,用戶 0 感知細(xì)節(jié)。用戶既不需要關(guān)心 Hadoop 使用、任務(wù)調(diào)度,也不需要自己寫 Redis SDK 的代碼,只需要告訴我們,在什么時(shí)間點(diǎn)需要什么樣的數(shù)據(jù)即可!
提供了 OpenAPI,方便用戶的自動(dòng)化流程打通。
提供全量覆蓋和增量導(dǎo)入兩種方式。
NewSQL
在演進(jìn)過(guò)程的第 3 個(gè)階段,我們主要是針對(duì) MySQL 的。大家都知道 MySQL 的擴(kuò)展性比較差,面對(duì)百億級(jí)存儲(chǔ),有幾個(gè)問(wèn)題,一個(gè)是數(shù)據(jù)存不下,一個(gè)是擴(kuò)展不靈活,比如修改字段、修改索引等。接著就來(lái)討論下,我們是如何解決這類問(wèn)題的。
需求分析
同樣的,我們先來(lái)分析下業(yè)務(wù)的需求是什么?簡(jiǎn)單理解下,我們認(rèn)為有 3 點(diǎn)剛需:
輕松改字段。即需要足夠的擴(kuò)展性。
存儲(chǔ)不限量。即需要一個(gè)容量盡可能大的存儲(chǔ)。
省成本。既然需要存 MySQL 都存不下的數(shù)據(jù),那么成本一定要考慮清楚。
至于事務(wù)、穩(wěn)定性、高性能、二級(jí)索引,我們認(rèn)為都是基本需求。
背景問(wèn)題
如何實(shí)現(xiàn) shema 到 key/value 的轉(zhuǎn)換?
前面的介紹我們知道,Fusion 是支持 Redis 協(xié)議的,那么 schema 轉(zhuǎn)換成 key/value,就可以用 Redis 的 hash 結(jié)構(gòu)來(lái)實(shí)現(xiàn),下圖我們就以 student 表為例,轉(zhuǎn)換了 2 行數(shù)據(jù)。
如何做主鍵查詢呢?
下面的圖片給出了一個(gè)例子,即查詢 ID 為 1 的學(xué)生的全部信息或年齡。
如何實(shí)現(xiàn)二級(jí)索引呢?
我們還是以 student 表為例,分別構(gòu)建如下 age\sex 索引,其編碼規(guī)則如下可見。
如何做非主鍵查詢和范圍查詢呢?
在上圖構(gòu)建好索引后,就很容易實(shí)現(xiàn)下面的兩個(gè)例子,即查詢年齡在某個(gè)范圍的學(xué)生,和查詢某種性別的所有學(xué)生。
架構(gòu)設(shè)計(jì)
架構(gòu)設(shè)計(jì)上分成接入層和數(shù)據(jù)存儲(chǔ)層,在接入層(DISE)我們提供控制臺(tái)來(lái)管理用戶的字段,用戶可以在這里定義自己的 schema、字段、索引,并做相應(yīng)的修改。然后用戶通過(guò)我們提供的類 SQL 的 SDK 接入,由我們的 SchemaServer 做 schema 轉(zhuǎn)換,接著插入數(shù)據(jù)到存儲(chǔ)層。然后數(shù)據(jù)存儲(chǔ)層吐出 binlog,由 IndexServer 異步消費(fèi) binlog 并構(gòu)建索引。查詢時(shí)候,用戶的請(qǐng)求經(jīng)由 SDK 到達(dá) SchemaServer,SchemaServer 先查詢索引服務(wù)器,拿到對(duì)應(yīng)的主鍵信息,然后根據(jù)命中的主鍵查詢?cè)敿?xì)信息,最后返回給用戶。
小結(jié)
NewSQL 解決的問(wèn)題是針對(duì) MySQL 的特殊場(chǎng)景的,我們就拿 MySQL 來(lái)跟 Fusion 做個(gè)對(duì)比,可以看到 Fusion 只是在部分場(chǎng)景上解決了 MySQL 的容量限制以及擴(kuò)展問(wèn)題,但還是有很多場(chǎng)景并不能支持的。
跨機(jī)房多活建設(shè)
最后一個(gè)演進(jìn)我們講的是如何支持業(yè)務(wù)的跨機(jī)房容災(zāi)問(wèn)題。
背景介紹
滴滴多活的業(yè)務(wù)架構(gòu)如下圖,可以看到用戶層接入層和業(yè)務(wù)層都是無(wú)狀態(tài)的,因此如圖中的白色虛線所描述的,他們的請(qǐng)求可以在兩個(gè)機(jī)房間來(lái)回路由,而不影響業(yè)務(wù)請(qǐng)求的正確性。那么是如何做到這一點(diǎn)的呢?必然得有一個(gè)地方維護(hù)著狀態(tài)的一致性,才能讓業(yè)務(wù)自由切換。因此跨機(jī)房多活容災(zāi)最復(fù)雜的部分就在底層數(shù)據(jù)同步層,這里的數(shù)據(jù)同步涉及到很多中間件,我們這里只關(guān)心 Fusion 的跨機(jī)房多活。
架構(gòu)設(shè)計(jì)
下圖是 Fusion 的跨機(jī)房同步架構(gòu),不依賴任何外部中間件,也不依賴內(nèi)部 proxy。當(dāng)用戶數(shù)據(jù)通過(guò) A 機(jī)房寫入時(shí),落地到某個(gè)存儲(chǔ)節(jié)點(diǎn)上,該存儲(chǔ)節(jié)點(diǎn)會(huì) cache 一份對(duì)端節(jié)點(diǎn)的路由表,并異步的將剛才寫入的數(shù)據(jù)轉(zhuǎn)發(fā)到對(duì)端集群。
我們這里的轉(zhuǎn)發(fā)采用了兩個(gè)異步特性:1. 跟用戶寫入主流程完全異步,即不影響用戶正常請(qǐng)求;2. A 機(jī)房到 B 機(jī)房的數(shù)據(jù)同步,采用了異步、批量、應(yīng)答的方式高效同步。既保證了用戶請(qǐng)求主機(jī)房的吞吐和延遲,也大幅降低了備機(jī)房數(shù)據(jù)讀取的延遲。
小結(jié)
到此總結(jié)下我們的多活方案:
異步數(shù)據(jù)復(fù)制。在追求性能的同時(shí),放棄了一段時(shí)間的不一致。如果在數(shù)據(jù)未達(dá)成一致的時(shí)候,主機(jī)房宕機(jī),備機(jī)房的數(shù)據(jù)將缺失,但這個(gè)缺失不會(huì)是永久,等到主機(jī)房恢復(fù)后,我們會(huì)把這部分?jǐn)?shù)據(jù)自動(dòng)補(bǔ)齊到備機(jī)房,這個(gè)過(guò)程我們會(huì)根據(jù)時(shí)間戳去重。
自適應(yīng)感知集群狀態(tài)變更,比如切主、擴(kuò)容等。在運(yùn)行過(guò)程中,兩個(gè)機(jī)房的集群難免會(huì)發(fā)生各類路由變化,我們?cè)谠O(shè)計(jì)時(shí)考慮到了這一點(diǎn),針對(duì)路由變化,我們會(huì)及時(shí)更新路由表,以把數(shù)據(jù)同步到正確的節(jié)點(diǎn)上。
數(shù)據(jù)可靠同步。我們的數(shù)據(jù)同步是依賴滑動(dòng)窗口的應(yīng)答機(jī)制,因此實(shí)現(xiàn)了一種可靠的數(shù)據(jù)同步。
支持雙寫,解決秒級(jí)沖突。由第一點(diǎn)提到,在某些場(chǎng)景是存在雙寫的,如果雙寫的是不同 key,自然不需要解決沖突,如果雙寫的是針對(duì)同一個(gè) key,那么我們會(huì)根據(jù)時(shí)間戳做沖突檢測(cè)。
自動(dòng)數(shù)據(jù)補(bǔ)償。也就是在發(fā)生主機(jī)房宕機(jī)后,寫入備機(jī)房的增量數(shù)據(jù),可以自動(dòng)的補(bǔ)償?shù)街鳈C(jī)房;原先滯留在主機(jī)房的數(shù)據(jù),在主機(jī)房恢復(fù)后,也可以補(bǔ)償?shù)絺錂C(jī)房。即可以達(dá)到最終一致性。
總結(jié) & 展望
總結(jié)
在伴隨滴滴業(yè)務(wù)發(fā)展的過(guò)程中,Fusion 經(jīng)歷了 4 個(gè)發(fā)展階段,我們堅(jiān)持”好東西是用出來(lái)“,因此在每個(gè)階段,都盡量避免”過(guò)度設(shè)計(jì)“,只解決特定的業(yè)務(wù)問(wèn)題。這給了我們很多認(rèn)真打磨產(chǎn)品的時(shí)間和精力,讓我們贏得了用戶口碑。
展望
通過(guò)前面的分享我們知道,Fusion 雖然能做的事情很多,但不能做的事情更多,所以我們的目標(biāo)是持續(xù)發(fā)展持續(xù)演進(jìn),把解決業(yè)務(wù)問(wèn)題當(dāng)做己任。未來(lái)我們將往分布式數(shù)據(jù)庫(kù)方向前進(jìn),解決更多的業(yè)務(wù)問(wèn)題。
作者介紹
余汶龍,滴滴出行技術(shù)專家,曾經(jīng)就職于 VMware、淘寶核心系統(tǒng)、阿里云數(shù)據(jù)庫(kù)組,長(zhǎng)期從事分布式存儲(chǔ)相關(guān)的研發(fā)。2016 年加入滴滴,從零開始構(gòu)建滴滴自研的分布式 NoSQL 數(shù)據(jù)庫(kù) Fusion。
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/10313313.html
總結(jié)
以上是生活随笔為你收集整理的滴滴自研分布式 NoSQL 数据库 Fusion 的演进之路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Go语言程序结构分析初探
- 下一篇: spring-session用mysql