做者介紹:
韋萬,PingCAP 數據庫研發工程師,主要領域是數據庫的存儲引擎研發,以及系統性能優化。
在互聯網浪潮出現以前,企業的數據量廣泛不大,特別是核心的業務數據,一般一個單機的數據庫就能夠保存。那時候的存儲並不須要複雜的架構,全部的線上請求(OLTP, Online Transactional Processing) 和後臺分析 (OLAP, Online Analytical Processing) 都跑在同一個數據庫實例上。後來漸漸的業務愈來愈複雜,數據量愈來愈大,DBA 們再也優化不動 SQL 了。其中一個顯著問題是:單機數據庫支持線上的 TP 請求已經很是吃力,沒辦法再跑比較重的 AP 分析型任務。跑起來要麼 OOM,要麼影響線上業務,要麼作了主從分離、分庫分表以後很難實現業務需求。算法
在這樣的背景下,以 Hadoop 爲表明的大數據技術開始蓬勃發展,它用許多相對廉價的 x86 機器構建了一個數據分析平臺,用並行的能力破解大數據集的計算問題。因此從某種程度上說,大數據技術能夠算是傳統關係型數據庫技術發展過程的一個分支。固然在過程當中大數據領域也發展出了屬於本身的全新場景,誕生了許多新的技術,這個不深刻提了。數據庫
由此,架構師把存儲劃分紅線上業務和數據分析兩個模塊。以下圖所示,業務庫的數據經過 ETL 工具抽取出來,導入專用的分析平臺。業務數據庫專一提供 TP 能力,分析平臺提供 AP 能力,各施其職,看起來已經很完美了。但其實這個架構也有本身的不足。安全
<center>圖 1 Tranditional Data Platform</center>性能優化
首先是複雜性問題。自己 ETL 過程就是一個很繁瑣的過程,一個例證是 ETL 作的好,能夠成爲一個商業模式。由於是兩個系統,必然帶來更高的學習成本、維護成本和整合成本。若是你使用的是開源的大數據工具搭建的分析平臺,那麼確定會遇到各類工具之間的磨合的問題,還有因爲各類工具參差不齊所致使的質量問題。網絡
其次是實時性問題。一般咱們認爲越接近實時的數據,它的價值越大。不少業務場景,對實時性有很高的要求,好比風控系統,它須要對數據不停的分析,而且在險情出現以後儘快響應。而一般的 ETL 是一個週期性的操做,好比一天或者一個小時導一次數據,數據實時性是沒有辦法保證的。
最後是一致性問題。一致性在數據庫裏面是很重要的概念,數據庫的事務就是用來保證一致性的。若是把數據分表存儲在兩個不一樣的系統內,那麼很難保證一致性,即 AP 系統的查詢結果沒有辦法與線上業務正確對應。那麼這兩個系統的聯動效應就會受到限制,好比用戶沒辦法在一個事務裏面,同時訪問兩個系統的數據。架構
因爲現有的數據平臺存在的以上侷限性,咱們認爲開發一個HTAP(Hybrid Transactional/Analytical Processing)融合型數據庫產品能夠緩解你們在 TP or AP 抉擇上的焦慮,或者說,讓數據庫的使用者不用考慮過分複雜的架構,在一套數據庫中既能知足 OLTP 類需求,也能知足 OLAP 類需求。這也是 TiDB 最初設計時的初衷。異步
TiDB 定位爲一款 HTAP 數據庫,但願同時解決 TP 和 AP 問題。咱們知道 TiDB 能夠看成可線性擴展的 MySQL 來用,自己設計是能夠知足 TP 的需求的。在 17 年咱們發佈了 TiSpark,它能夠直接讀取 TiKV 的數據,利用 Spark 強大的計算能力來增強 AP 端的能力。然而因爲 TiKV 畢竟是爲 TP 場景設計的存儲層,對於大批量數據的提取、分析能力有限,因此咱們爲 TiDB 引入了以新的 TiFlash 組件,它的使命是進一步加強 TiDB 的 AP 能力,使之成爲一款真正意義上的 HTAP 數據庫。分佈式
<center>圖 2 What is TiFlash</center>工具
TiFlash 是 TiDB 的一個 AP 擴展。在定位上,它是與 TiKV 相對應的存儲節點,與 TiKV 分開部署。它既能夠存儲數據,也能夠下推一部分的計算邏輯。數據是經過 Raft Learner 協議,從 TiKV 同步過來的。TiFlash 與 TiKV 最大的區別,一是原生的向量化模型,二是列式存儲。 這是都是專門爲 AP 場景作的優化。TiFlash 項目藉助了 Clickhouse 的向量化引擎,所以計算上繼承了它高性能的優勢。oop
<center>圖 3 TiFlash Architecture</center>
因爲 TiFlash 節點和 TiKV 節點是分開部署的,因此即便咱們跑很重的計算任務,也不會對線上業務產生影響。
上層的計算節點,包括 TiSpark 和 TiDB,他們均可以訪問 TiKV 和 TiFlash。後面會介紹咱們是如何利用這個架構的優點,在一個系統內同時服務 TP 和 AP 這兩個場景,而且產生 1+1>2 的效果。
對於一個數據庫系統,TP 和 AP 是有系統設計上的衝突的。TP 場景咱們關注的是事務正確性,性能指標是 QPS、延遲,它一般是點寫、點查的場景;而 AP 更關心的吞吐量,是大批量數據的處理能力,處理成本。好比不少狀況下 AP 的分析查詢是須要掃描幾百上千萬條數據,join 十幾張表,這種場景下系統的設計哲學和 TP 徹底不一樣。TP 一般使用行式存儲,例如 InnoDB,RocksDB 等;而 AP 系統一般使用列式存儲。將這兩個需求放在同一個系統裏面實現,從設計上很難取捨,再加上 AP 的查詢業務一般屬於資源消耗型,隔離性作很差,很容易影響TP 業務。因此作一個 HTAP 系統是一件難度很是高的事情,很考驗系統的工程設計能力。
<center>圖 4 Row Based vs Column Based</center>
通常來講,AP 系統基本上都是使用列式存儲,TiFlash 也不例外。列式存儲自然能夠作列過濾,而且壓縮率很高,適合大數據的 Scan 場景。另外列式存儲更適合作向量化加速,適合下推的聚合類算子也更多。TiFlash 相對於 TiKV,在 Scan 場景下性能有數量級的提高。
而行式存儲顯然更適合 TP 場景,由於它很適合點查,只讀少許數據,IO 次數、粒度都更小。在絕大多數走索引的查詢中,能夠實現高 QPS 和低延遲。
因爲咱們把 TiFlash 和 TiKV 整合在了 TiDB 內部,用戶能夠靈活選擇使用哪一種存儲方式。數據寫入了 TiKV 以後,用戶能夠根據需選擇是否同步到 TiFlash,以供 AP 加速。目前可選的同步粒度是表或者庫。
數據複製永遠是分佈式系統的最重要的問題之一。TiFlash 做爲 TiDB 的另一個存儲層,須要實時同步 TiKV 的數據。咱們採用的方案也很天然:既然 TiKV 節點內部使用 Raft 協議同步,那天然 TiKV 到 TiFlash 也是能夠用 Raft 協議同步數據的。TiFlash 會把本身假裝成一個 TiKV 節點,加入 Raft Group。比較不同的是,TiFlash 只會做爲 Raft Learner,並不會成爲 Raft Leader / Follower。緣由是目前 TiFlash 還不支持來自 SQL 端(TiDB/ TiSpark)的直接寫入,咱們將在稍後支持這一特性。
<center>圖 5 Raft Learner Replication</center>
你們知道,Raft 協議爲了提升數據複製效率,Raft Log 從 Leader 到 Follower / Learner 的複製一般會優化成異步複製,只要記錄被複制到了 Leader + Follower 的 「多數」 節點,就認爲已經 commit 了。而且 Learner 是排除在 「多數」 以外的,也就是說更新不須要等待 Learner 確認。這樣的實現方式,缺點是 Learner 上的數據可能有必定延遲,優勢是大大減小了引入 TiFlash 形成的額外數據複製開銷。固然若是複製延遲太大,說明節點之間的網絡或者機器的寫入性能出現了問題,這時候咱們會有報警提示作進一步的處理。
那既然是異步複製,如何保證讀一致性呢?一般來講,由於在 Leader 節點必定能夠拿到最新的數據,因此咱們只會去 Leader 節點讀數據。可是 TiFlash 只有 Learner,不可能這樣讀數據。咱們使用 Raft Follower / Learner Read 機制來實現直接在 TiFlash 節點讀數據。原理是利用了 Raft Log 的偏移量 + 全局時間戳的特性。首先在請求發起的時候獲取一個 read ts,那麼對於全部的 Region(Region 是 TiDB 內部數據切割單位,也是 Raft Group 單位),只要肯定本地 Region 副本已經同步到足夠新的 Raft Log,那麼直接讀這個 Region 副本就是安全的。能夠利用 MVCC 的特性,對於每一條 unique key,過濾出 commit ts<= read ts 的全部版本,其中 commit ts 最大的版本就是咱們應該讀取的版本。
這裏的問題是,Learner 如何知道當前 Region 副本足夠新呢?實時上 Learner 在讀數據以前,會帶上 read ts 向 Leader 發起一次請求,從而得到確保 Region 足夠新的 Raft Log 的偏移量。TiFlash 目前的實現是在本地 Region 副本同步到足夠新以前,會等待直到超時。將來咱們會加上其餘策略,好比主動要求同步數據(如圖 6 和圖 7 所示)。
<center>圖 6 Learner Read (1/2)</center>
<center>圖 7 Learner Read (2/2)</center>
TiFlash 會同步 TiKV 上的表的全部變動,是兩個異構的系統之間同步數據,會遇到一些很困難的問題。其中比較有表明性的是如何讓 TiFlash 能實時複製 TiKV 的更新,而且是實時、事務性的更新。一般咱們認爲列式存儲的更新相對困難,由於列存每每使用塊壓縮,而且塊相對於行存更大,容易增長寫放大。而分列存儲也更容易引發更多的小 IO。另外因爲 AP 的業務特色,須要大量 Scan 操做,如何在高速更新的同時保證 Scan 性能,也是很大的問題。
<center>圖 8 Update Support</center>
目前 TiFlash 的方案是,存儲引擎使用類 LSM-Tree 的存儲架構,而且使用 MVCC 來實現和 TiDB 一致的 SI 隔離級別。LSM-Tree 架構能夠很好的處理 TP 類型的高頻小 IO 寫入;同時又有的必定的局部有序性,有利於作 Scan 優化。
在新的業務緯度上讓 TiDB 更加 Scalable。經過引入全新的 TiFlash AP 擴展,讓 TiDB 擁有了真正的 AP 能力,即爲 AP 專門優化的存儲和計算。咱們能夠經過增減相對應的節點,動態的增減 TiDB 系統的 TP 或者 AP 端的能力。數據再也不須要在兩個獨立的系統之間手動同步,而且能夠保證明時性、事務性。
AP 與 TP 業務的隔離性,讓 TiDB 的 AP 業務對線上的 TP 影響降到最低。由於 TiFlash 是獨立節點,一般和 TiKV 分開部署,因此能夠作到硬件級別的資源隔離。咱們在 TiDB 系統中使用標籤來管理不一樣類型的存儲節點。
<center>圖 9 AP 與 TP 業務隔離</center>
從 TiDB 的視角,TiFlash 和 TiKV 從層次上是一致的,都是存儲節點。區別在於它們在啓動時候給 PD (PD 爲 TiDB 集羣的 Coordinator)上報的節點標籤。TiDB 就能夠利用這些信息,把不一樣類型的請求路由到相應的節點。好比咱們能夠根據一些啓發試算法,以及統計信息,瞭解到一條 SQL 須要 Scan 大量的數據而且作聚合運算,那麼顯然這條 SQL 的 Scan 算子去 TiFlash 節點請求數據會更合理。而這些繁重的 IO 和計算並不會影響 TiKV 側的 TP 業務。
TiFlash 帶來了全新的融合體驗。TiFlash 節點並不僅是單純的從 TiKV 節點同步數據,它們其實能夠有進一步的配合,帶來 1+1>2 的效果。上層的計算層,TiDB 或者 TiSpark,是能夠同時從 TiFlash 和 TiKV 讀取數據的。
<center>圖 10 Combination of TiFlash and TiKV</center>
如圖 10 所示,好比咱們遇到一條 SQL,它須要 join 兩份數據,其中一份數據須要全表掃描,另一份則能夠走索引,那麼很顯然能夠同時利用 TiFlash 強大的 Scan 和 TiKV 的點查。值得一提的是,用戶一般會配置 3 或 5 份副本在 TiKV,爲了節約成本,可能只部署 1 份副本到 TiFlash。那麼當一個 TiFlash 節點掛掉以後,咱們就須要從新從 TiKV 同步節點。
<center>圖 11 SQL MPP Push Down</center>
咱們接下來計劃讓 TiFlash 節點成爲 MPP 集羣。即 TiDB 或者 TiSpark 接收到 SQL 以後,能夠選擇把計算徹底下推。MPP 主要是爲了進一步提高 AP 的計算效率。
<center>圖 12 性能數據</center>
上圖是 TiFlash 某一個版本的性能數據,咱們使用 TiSpark + TiFlash 來對比 Spark + Parquet。能夠看到 TiFlash 在支持了實時 update 和事務一致性的狀況下,仍然達到了基本一致的性能。TiFlash 目前還在快速迭代之中,最新版本相對於這裏其實已經有很大幅度的提高。另外咱們目前正在研發一款專門爲 TiFlash 全新設計的存儲引擎,至少帶來 2 倍的性能提高。能夠期待一下以後出來的性能。
<center>圖 13 TiDB Data Platform</center>
簡單就是生產力。傳統的數據平臺因爲技術的限制,企業須要作很是繁重的建設工做。須要把許多技術整合在一塊兒才能實現業務需求,而系統之間使用複雜繁瑣的 ETL 過程同步數據,致使數據鏈條很長,效果也不必定好。TiDB 但願把系統的複雜性留在工具層面,從而大幅度簡化用戶的應用架構。
目前 TiFlash 正在與部分合做夥伴進行內部 POC,預計年末以前會發佈一個 GA 版本,敬請期待。
(對 TiFlash 感興趣的朋友歡迎私聊做者,交流更多技術細節~ weiwan@pingcap.com)