select選擇框必輸校驗_輪子這么多,我們為什么選擇自研NewSQL

作者介紹

李鑫,滴滴資深軟件開發工程師,多年分布式存儲領域設計及開發經驗。曾參與NoSQL/NewSQL數據庫Fusion、分布式時序數據庫sentry、NewSQL數據庫SDB等系統的設計開發工作。

一、背景

Fusion-NewSQL是由滴滴自研的在分布式KV存儲基礎上構建的NewSQL存儲系統。Fusion-NewSQ兼容了MySQL協議,支持二級索引功能,提供超大規模數據持久化存儲和高性能讀寫。

我們的問題

滴滴的業務快速持續發展,數據量和請求量急劇增長,對存儲系統等壓力與日俱增。雖然分庫分表在一定程度上可以解決數據量和請求增加的需求,但是由于滴滴多條業務線(快車、專車、兩輪車等)的業務快速變化,數據庫加字段加索引的需求非常頻繁,分庫分表方案對于頻繁的Schema變更操作并不友好,會導致DBA任務繁重,變更周期長,并且對巨大的表操作還會對線上有一定影響。同時,分庫分表方案對二級索引支持不友好或者根本不支持。鑒于上述情況,NewSQL數據庫方案就成為我們解決業務問題的一個方向。

開源產品調研

最開始,我們調研了開源的分布式NewSQL方案TiDB。雖然TiDB是非常優秀的NewSQL產品,但是對于我們的業務場景來說,TiDB并不是非常適合,原因如下:

  • 我們需要一款高吞吐,低延遲的數據庫解決方案,但是TiDB由于要滿足事務,2pc方案天然無法滿足低延遲(100ms以內的99rt,甚至50ms內的99rt)。

  • 我們的多數業務,并不真正需要分布式事務,或者說可以通過其他補償機制,繞過分布式事務。這是由于業務場景決定的。

  • TiDB三副本的存儲空間成本相對比較高。

  • 我們內部一些離線數據導入在線系統的場景,不能直接和TiDB打通。

基于以上原因,我們開啟了自研符合自己業務需求的NewSQL之路。

我們的基礎

我們并沒有打算從0開發一個完備的NewSQL系統,而是在自研的分布式KV存儲Fusion的基礎上構建一個能滿足我們業務場景的NewSQL。Fusion是采用了Codis架構,兼容Redis協議和數據結構,使用RocksDB作為存儲引擎的NoSQL數據庫。Fusion在滴滴內部已經有幾百個業務在使用,是滴滴主要的在線存儲之一。

Fusion的架構圖如下:

8663cee104eb7d663f7b85850c2635df.png

我們采用hash分片的方式來做數據sharding。從上往下看,用戶通過Redis協議的客戶端就可以訪問Fusion,用戶的訪問請求發到proxy,再由proxy轉發數據到后端Fusion的數據節點。proxy到后端數據節點的轉發,是根據請求的key計算hash值,然后對slot分片數取余,得到一個固定的slotid,每個slotid會固定的映射到一個存儲節點,以此解決數據路由問題。

有了一個高并發,低延遲,大容量的存儲層后,我們要做的就是在之上構建MySQL協議以及二級索引。那么如何將MySQL的數據格式轉成Redis的數據結構存儲就是我們必須面臨的問題,后面會詳細說。

二、需求

7bd736c51032b676c643e4be8e22b2bc.png

綜合考慮大多數用戶對需求,我們整理了我們的NewSQL需要提供的幾個核心能力:

  • 高吞吐,低延遲,大容量。

  • 兼容MySQL協議及下游生態。

  • 支持主鍵查詢和二級索引查詢。

  • Schema變更靈活,不影響線上服務穩定性。

三、架構設計

Fusion-NewSQL由下面幾個部分組成:

  • 解析MySQL協議的DiseServer;

  • 存儲數據的Fusion集群-Data集群;

  • 存儲索引信息的Fusion集群-Index集群;

  • 負責Schema的管理配置中心-ConfigServer;

  • 異步構建索引程序-Consumer負責消費Data集群寫到MQ中的MySQL-Binlog格式數據,根據schema信息,生成索引數據寫入Index集群;

  • 外部依賴,MQ,Zookeeper。

架構圖如下:

8a69ce5cb2900c2a9d6dc00839fc89e5.png

四、詳細設計

存儲結構

MySQL的表結構數據如何轉成Redis的數據結構是我們面臨的第一個問題。

如下圖:

8101a7a0f51718b2c1aceeb328faf19f.png

我們將MySQL表的一行記錄轉成Redis的一個Hashmap結構。Hashmap的key由表名+主鍵值組成,滿足了全局唯一的特性。下圖展示了MySQL通過主鍵查詢轉換為Redis協議的方式:

aba7d204aa63cbb858329414d01c8547.png

除了數據,索引也需要存儲在Fusion-NewSQL中,和數據存成hashmap不同,索引存儲成key-value結構。根據索引類型不同,組成key-value的格式還有一點細微的差別(下面的格式為了看起來直觀,實際上分隔符,indexname都是做過編碼的):

唯一索引:

Key:?

table_indexname_indexColumnsValue?

Value: Rowkey

非唯一索引:

Key:?

table_indexname_indexColumnsValue_Rowkey?

Value:null

造成這種差異的原因就是非唯一索引在加入Rowkey之前的部分是有可能重復的,無法全局唯一。另外,唯一索引不將Rowkey編碼在key中,是因為在查詢語句是單純的“=”查詢的時候直接get操作就可以找到對應的Rowkey內容,而不需要通過scan,這樣的效率更高。

60ef41986a179f6abbcc4efdd5a9deb3.png

后面會在查詢流程中重點講述如何通過二級索引查詢到數據。

數據讀寫流程

1)數據寫入

  • 用戶通過MySQL-sdk將協議發給dise-server;

  • dise-server根據schema對用戶寫入的SQL做校驗;

  • dise-server將校驗通過的SQL轉成Redis的Hashmap結構,通過Redis協議發給Data集群;

  • Data集群將數據寫入wal文件,并將數據存儲rocksdb;

  • Data集群后臺線程將wal文件消費,轉成MySQL-Binlog格式。將數據發到MQ;

  • 異步索引模塊消費MQ,將MySQL-Binlog根據操作類型(insert,update,delete)配合schema信息,構建索引信息,并將索引數據寫入index集群。

通過上面的鏈路,用戶的一條MySQL寫操作就完成了數據存儲和索引構建。由于通過數據構建索引這一步是通過MQ異步完成,所以會存在數據和索引有一定的時間差的情況。

2)查詢

下面是一個使用二級索引查詢數據的案例:

  • dise-server接收到SQL查詢,根據條件,選擇索引,如果沒有命中任何索引,給用戶返回錯誤(Fusion-NewSQL不能以非索引字段作為查詢條件)。

  • 根據選中的索引,構建查詢范圍,通過scan命令遍歷Index集群,獲取符合條件的主鍵集合。下圖以一個SQL查詢,展示使用scan遍歷二級索引的例子:

ac4f1d91c62742ef20a636a21ab218b9.png

  • 根據主鍵,通過hgetall命令向Data集群查詢符合條件的結果集。

  • 將結果集構建成MySQL的結果返回給用戶。

根據上面索引數據的格式可以看到,scan范圍的時候,前綴必須固定,映射到SQL語句到時候,意味著where到條件中,范圍查詢只能有一個字段,而不能多個字段。比如:

2b7735402fe8013bd15c686ebeca9f4c.png

索引是age和name兩個字段的聯合索引。如果查詢語句如下:

select * from student where age > 20 and name >‘W’;

scan就沒有辦法確定前綴,也就無法通過index_age_name這個索引查詢到滿足條件的數據,所以使用KV形式存儲到索引只能滿足where條件中有一個字段是范圍查詢。當然可以通過將聯合索引分開存放,多次交互搜索取交集的方式解決,但是這就和我們降低RPC次數,降低延遲的設計初衷相違背了。為了解決這個問題,我們引入了Elastic Search搜索引擎,這部分后面會詳細說明。

Schema變更

用戶涉及Schema變更時,會以工單形式發給管控系統。管控系統審批過后,會將變更請求推給配置中心,配置中心進行安全性檢查后,將新的Schema寫入到存儲中,并給各個節點推送變更。

字段變更:

節點接收到推送,更新本地的Schema。對于歷史數據,并不真正去修改數據,而是在查詢的時候,根據Schema信息匹配字段,如果數據比Schema缺失某些字段,就使用默認值代替;如果數據比Schema多了字段,就隱藏掉多余字段不展示。

新增索引分為兩步處理:

  • 新增索引,歷史數據不處理,增量數據立刻走索引構建流程。

  • 通過歷史索引構建工具,掃描歷史數據,構建新索引的KV,將歷史數據完成索引構建。這里有個優化點,掃描slave而不是master,避免對線上產生影響。

五、生態構建

一個單獨的存儲產品解決所有問題的時代早已經過去,數據孤島是沒有辦法很好服務業務的,Fusion-NewSQL從設計的那天起就考慮了和其他存儲系統的打通。

Fusion-NewSQL到其他存儲系統

Fusion-NewSQL通過兼容MySQL的Binlog格式,將數據發到MQ中。下游各個系統凡是能接入MySQL數據的,都可以通過消費MQ中相同格式的Fusion-NewSQL數據,將數據存到其他系統中。這樣的方式用最小的工作量最大程度做到了兼容。

Hive到Fusion-NewSQL

Fusion-NewSQL還支持將離線的Hive表中的數據通過Fusion-NewSQL提供的FastLoad(DTS)工具,將Hive表數據轉入到Fusion-NewSQL,滿足離線數據到在線的數據流動。

如果用戶自己完成數據流轉,一般會掃描Hive表,然后構建MySQL的寫入語句,一條條將數據寫入到Fusion-NewSQL,流程如下面這樣:

a709a5f223a8886638e23a3cf1da0cbf.png

  • MySQL-client將寫請求發給DiseServer。

  • DiseServer將MySQL寫做解析,轉成hashmap將轉換后的數據以Redis協議發給Data集群。

  • Data集群的存儲節點收到數據,將數據寫到wal文件。

  • Data集群的存儲節點走RocksDB的寫流程,這里包括了寫memtable,還有可能memtable寫滿,發生flush以及觸發后臺的compact。

  • 異步線程消費wal,將數據構建MySQL-Binlog格式發到MQ。

  • 異步索引程序消費MySQL-Binlog,構建Index集群需要的數據,向Index集群發送寫入請求。

  • Index集群的存儲節點寫wal。

  • Index集群的存儲節點進入RocksDB的寫流程。

從上面的流程可以看出這種遷移方式有幾個痛點:

  • 有這種Hive到Fusion-NewSQL數據導入需求的用戶都需要開發一套相同邏輯的代碼,維護成本高。

  • 每條Hive數據都要經過較長鏈路,數據導入耗時較長。

  • 離線平臺的數據量大,吞吐高,直接大幅提升在線系統的QPS,對在線系統的穩定性有較大影響。

基于上述的痛點,我們設計了Fastload數據導入平臺,通過約定Hive到Fusion-NewSQL的表格式,使用Hadoop并發處理數據,并構建RocksDB能識別的sst存儲文件,繞過復雜的DISE寫鏈路,直接將數據導入到Fusion-NewSQL中,流程如下:

494f075a7187c4d20b88d7c6004468af.png

  • 用戶填寫工單,選中將指定Hive表的某些字段映射為Fusion-NewSQL表的字段(這里可以Hive中多個字段組成一個Fusion-NewSQL字段)。

  • Hadoop遍歷Hive表,并且通過Zookeeper獲取數據應該存放在Data集群和Index集群的路由信息。

  • 通過上面的遍歷,計算,之后,將數據直接構建成、Rocksdb能識別的sst,并且其中存的數據已經是按DISE的表結構信息組成的KV數據。

  • 將sst文件直接發送到指定的存儲節點,存儲節點或通過Rocksdb提供的ingest功能,直接將sst文件加載到Fusion-NewSQL中,用戶可以讀到。

這個方案避免了冗長復雜的寫鏈路,同時不會增加系統的QPS,在磁盤和網絡IO沒有達到瓶頸的情況下對線上訪問幾乎是沒有任何影響;同時,用戶只需要填寫Hive到Fusion-NewSQL的Schema映射關系即可,不必再關心實現。

通過ElasticSearch實現復雜查詢

在業務使用MySQL或Fusion-NewSQL的過程中,我們發現有這樣一種場景:業務的查詢條件很復雜,涉及的字段數,條件,聚合都比較多,這種場景下,業務會選擇將ElasticSearch作為MySQL或Fusion-NewSQL的下游,將數據導入Elastic Search,然后通過ElasticSearch豐富的搜索能力,先從ElasticSearch中獲取數據在MySQL或Fusion-NewSQL的主鍵,然后再根據主鍵獲取全部數據。

根據上面的場景,Fusion-NewSQL提供一個特殊的索引類型:ES。用戶在創建索引的時候,可以將需要做復雜查詢的字段勾選出來,共同構建成一個ES索引,這樣既滿足了業務需求,避免了每個業務都需要開發一套和ElasticSearch交互的復雜邏輯,又統一了數據庫使用接口都為MySQL。同時,還彌補了前面提到的Fusion-NewSQL的KV二級索引不能支持多個字段范圍檢索的能力。

架構圖如下:

326a55ebcd87038694c36f57cf0c9733.png

ES索引只是在上圖紅4處,將ES索引中包含的字段信息和主鍵寫入到ElasticSearch中。在查詢時綠1如果選中了ES類型的索引,就根據where條件中涉及的字段,組裝成ElasticSearch的DSL語句,從ElasticSearch獲取主鍵,再從Data集群獲取。由于ElasticSearch查詢的延遲比較慢,Fusion-NewSQL可以支持一張表的多個索引采用KV索引和ES索引并存,對于延遲要求高,查詢條件相對簡單的使用KV索引;對于查詢條件復雜,延遲要求不高的使用ES索引。

六、總結

Fusion-NewSQL當前已經接入訂單、預估、賬單、用戶中心、交易引擎等70個核心業務,總QPS超過200W,總數據超過600TB。

06410f8be6d6ec4712339ed7c38d4ea8.png

當然,Fusion-New不是一個通用完備的NewSQL方案,而是在已有的NoSQL數據庫基礎上,通過對SQL協議的支持以及組合各種組件,構建一個對外表達的數據庫,但是這種方式,可以以最小的開發代價,滿足大多數的業務場景,具備較高的投入產出比。?

七、后續工作

  • 有限制的事物支持,比如讓業務規劃落在一個節點的數據可以支持單機跨行事務。

  • 實時索引替代異步索引,滿足即寫即讀。目前已經有一個寫穿+補償機制的方案,在沒有分布式事務的前提下滿足正常狀態的實時索引,異常情況下保證數據索引最終一致的方案。

  • 更多的SQL協議和功能支持。

作者丨李鑫來源丨滴滴技術(ID:didi_tech)

參與相關討論,請在公眾號回復關鍵詞:讀者群。

參與相關討論,請在公眾號回復關鍵詞:讀者群。

往期推薦

? ??? ?架構師技術領導力成長之路

  • 初創型公司,如何選擇技術方案?

  • 單機和分布式場景下,流控方案大全

  • 干貨 | 攜程容器偶發性超時問題案例

  • DDD如何講清楚,做出來?

? ? ???……

? ?關注本公眾號,歡迎訂閱。

技術瑣話?

以分布式設計、架構、體系思想為基礎,兼論研發相關的點點滴滴,不限于代碼、質量體系和研發管理。本號由坐館老司機技術團隊維護。

0512efbeb3924b6b087ab3200c979d9b.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446125.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446125.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446125.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言深度剖析書籍學習記錄 第五章 內存管理

常見的內存錯誤 定義了指針變量,但是沒有為指針分配內存,即指針沒有指向一塊合法的內存。 結構體成員指針未初始化 很多初學者犯了這個錯誤還不知道是怎么回事。這里定義了結構體變量 stu,但是他沒 想到這個結構體內部 char *name 這成員在定…

怎么改電腦網絡ip地址_拋棄重啟路由器獲取ip地址方式,巧妙運用ip代理改IP工具...

網絡是簡單的也是復雜的,在如此龐大的網絡世界里有太多的不確定因素,導致我們遇到IP限制問題,從而影響到我們的網絡訪問,而大家都知道,如果遇到ip被限制的問題,最快速直接的辦法就是把被限制的ip更換一個新…

C語言深度剖析書籍學習記錄 第六章 函數

函數的好處 1、降低復雜性:使用函數的最首要原因是為了降低程序的復雜性,可以使用函數來隱含信息,從而使你不必再考慮這些信息。2、避免重復代碼段:如果在兩個不同函數中的代碼很相似,這往往意味著分解工作有誤。這時,應該把兩個…

如何把word分裝到兩個byte_如何核對兩個Word文檔的內容差別?同事加班半小時,我只花了30秒...

昨天下班前,老板突然發了兩份Word文檔過來,一份是原稿,還有一份是修訂稿,叫我們找出兩份文檔的內容差別之處,我只花了30秒就搞定了,然后準時下班!你想知道我是怎么操作的嗎?下面小源…

stm32f767中文手冊_ALIENTEK 阿波羅 STM32F767 開發板資料連載第五章 SYSTEM 文件夾

1)實驗平臺:alientek 阿波羅 STM32F767 開發板2)摘自《STM32F7 開發指南(HAL 庫版)》關注官方微信號公眾號,獲取更多資料:正點原子第五章 SYSTEM 文件夾介紹第三章,我們介紹了如何在 MDK5 下建立 STM32F7 工程。在這個新建的工程之…

手機安卓學習 內核開發

官網開源代碼 Documentation - MiCode/Xiaomi_Kernel_OpenSource - Sourcegraph Xiaomi 11T Pro GitHub - MiCode/Xiaomi_Kernel_OpenSource: Xiaomi Mobile Phone Kernel OpenSourceAndroid 開源項目 | Android Open Source Project google安卓官網 目錄概覽 參考…

vs 啟動調用的目標發生異常_如何解決不可測、異常場景的問題?

阿里QA導讀:在軟件研發過程中,發布前跨多個系統的聯調測試是不可或缺的一環,而在聯調過程中,經常會遇到一些比較棘手的困難,阻塞整個聯調進程。其中比較典型的有:第三方的研發節奏不一致,導致無…

Linux內核 scatterlist介紹

scatterlist 物理內存的散列表。通俗講,就是把一些分散的物理內存,以列表的形式組織起來 誕生背景 假設有三個模塊可以訪問memory:CPU、DMA控制器和某個外設。CPU通過MMU以虛擬地址(VA)的形式訪問memory;…

www.python123.org_python爬蟲-requests

Requests庫是目前常用且效率較高的爬取網頁的庫1.一個簡單的例子import requests #引入requests庫r requests.get("http://www.baidu.com")  #調用get方法獲取界面print(r.status_code)    #輸出狀態碼print(r.text)    #輸出頁面信息通過以下代碼&#x…

Linux內核 crypto文件夾 密碼學知識學習

密碼算法分類 對稱算法非對稱算法消息摘要(單向哈希)算法這些算法作為加密函數框架的最底層,提供加密和解密的實際操作。這些函數可以在內核crypto文件夾下,相應的文件中找到。不過內核模塊不能直接調用這些函數,因為…

python隨機出100道加法題_自動出題隨機100題-20以內加減法全部算式

班 級:姓 名:12-819-411-1114-1018-111417-261215-113-417-819-1914-341516-31269619-161159312817-014-1414-1112-501414-017-616-111-012-211520-711113051019-1810619-691118-1220-519-818018114-1416-712-1015-1319-916-714-920-717-118-1611-815-416-1014-919-416-1413-…

Linux crypto相關知識的匯總 Linux加密框架crypto中的算法和算法模式(一)

Linux加密框架中的算法和算法模式 Linux加密框架中的算法和算法模式(一)_家有一希的博客-CSDN博客 加密框架支持的密碼算法主要是對稱密碼算法和哈希算法,暫時不支持非對稱密碼算法。除密碼算法外,加密框架還包括偽隨機數生成算法…

python3.5.2安裝pygame_【閑來無事,py寫game】Mac-Python3.5安裝pygame 1.9.2 小計

13正文之前沒錯,我就是這么不學無術,C實在學的雞兒疼,所以干脆搞點娛樂措施,昨天趕上了京東圖書做大活動,所以屯了一批書,好久沒碰python了。所以就整本玩玩!今天這不就上手了么!自己…

Linux crypto相關知識的匯總 Linux加密框架crypto對稱算法和哈希算法加密模式

參考鏈接 Linux加密框架中的算法和算法模式(二)_家有一希的博客-CSDN博客 對稱算法 分組算法模式 ECB模式 ECB模式下,明文數據被分為大小合適的分組,然后對每個分組獨立進行加密或解密如下圖所示如果兩個明文塊相同&#xff0c…

物化視圖和視圖的最大區別_基于catalyst的物化視圖改寫引擎的實現

更新日志:1. 2020/06/16 group by 視圖的部分描述錯誤,已修正。什么是物化視圖我先用我的話解釋一下什么是物化視圖。假設我們已經有A,B兩張表,現在我創建了一張表C,C是由A,B兩張表經過一條SQL處理得到的,這個時候我們…

Linux加密框架中的算法和算法模式

參考鏈接 Linux加密框架中的算法和算法模式(三)_家有一希的博客-CSDN博客 對稱算法 14 如上所示,在arc4.c中定義了兩個與RC4算法相關的算法實現,分別為arc4和ecb(arc4),其中arc4是RC算法的算法實現,而ecb…

python學籍管理系統 flask_taskday05-Python之flask學習 web開發最基本的需要(特別詳細且適用)...

1.首先一個Flask的Web項目的創建需求一(文章概述):一:必須實現命令工具管理App,用于在命令行輸入命令對項目進行管理,對后期多多益善二:必須實現“藍圖”管理,用于將app啟動函數與路由分開管理,…

Linux加密框架crypto AES代碼相關

例子 aes_generic.c - crypto/aes_generic.c - Linux source code (v5.15.11) - Bootlin static struct crypto_alg aes_alg {.cra_name "aes",.cra_driver_name "aes-generic",.cra_priority 100,.cra_flags CRYPTO_ALG_TYPE_CIPHER,.cra_blocks…

python語言print函數_Python 的 print 函數

Python 2.x 系列已經停止維護了, python 3.x 系列正在成為主流,盡管有些項目還是python2.x 的,之后寫Python 代碼為了保持兼容性,還是盡量和Python 3 標準保持一致作為一個Python newbee 而言, python 2.x 和 3.x 的 …