友快網

導航選單

儲存引擎的最佳化與升級,TDengine 3.0支援訊息佇列和流式計算

在 8 月 13 日的 TDengine 開發者大會上,濤思資料聯合創始人、TDengine儲存引擎架構師程洪澤帶來題為《TDengine 的儲存引擎升級之路——從 1。0 到 3。0》的主題演講,詳細闡述了 TDengine 3。0 儲存引擎的技術最佳化與升級。本文根據此演講整理而成。

相比前兩個版本,3。0 的儲存引擎更注重各種場景下的儲存和查詢效率,不僅要對管理節點進一步“減負”,提供高效合理的更新、刪除功能,支援資料備份、流式處理等功能,還要考慮到資料維度膨脹下的高效處理、多表場景下的開機啟動速度、合理高效且準確地使用系統資源等需求。

TDengine 3。0 儲存引擎的更新可以分為三大塊,首先是 TQ,基於 WAL 的訊息佇列;其次是 META,基於 TDB 的元資料儲存引擎;第三是 TSDB(Time-Series Database),用來儲存時序資料的類 LSM 儲存引擎(TSDB SE)。

訊息佇列儲存引擎

在 1。0 和 2。0 時我們提供了一個基於 TSDB 儲存的連續查詢功能,當時的想法是用它來代替流式處理。工作流程可以概括為:App 定時發查詢任務下去,查詢引擎執行查詢,TSDB 返回結果給查詢引擎,查詢引擎再把結果返回給 App。

這一功能的優點是可以複用查詢引擎,簡單易開發,但缺點也很明顯,不僅會出現計算實時性差、查詢壓力大導致算力浪費的情況,更重要的是亂序問題無法處理。資料在進入 TSDB 之後都會按照時間戳進行排序,一個亂序的資料進來後插到前面,TSDB 是無法推出這條資料的,這就會導致亂序問題的出現;另外由於查詢引擎是複用的,TSDB 的查詢引擎也不會對新的亂序資料進行處理、對結果進行更改校驗。

從這一技術背景出發,TDengine 3.0 中需要設計一個儲存引擎來支援訊息佇列和流式計算。

這個儲存引擎能告訴我們什麼樣的資料是增量資料,這樣一來,流式計算只需處理增量資料就好了,其他的資料就不用管了。此外這個儲存引擎需要構建在一個 Pipe 之上,保證資料進入和出去的前後順序一致。在設計時,我們發現 TDengine 的 WAL 其實就是一個天然的 Pipe。於是我們在 WAL 之上加了一層索引,並進行大量的適配開發,實現了 TQ 儲存引擎。如果大家深入研究過 TDengine 的模型,就會發現它的架構模型和 Kafka 的很多設計都是相對應的,超級表和 Kafka 的 Topic 相似、Vnode 跟 Kafka 中的 Partition 也很接近,子表的表名跟 Kafka 中的 Event Key 對應,因此這個架構設計天然地就帶有訊息佇列的特點,從這點出發,TDengine 3。0 想要實現一個訊息佇列是非常容易的。

基於 TQ 儲存引擎,在實際操作時,查詢引擎只會處理增量資料,將計算結果修正後返回給 App,而不會再進行全量資料的再查詢。

它帶來的優點是實時性非常高,因為能對增量資料進行明確地區分,亂序資料也得以高效處理,同時還節省了更多的計算資源,將計算結果修正。

元資料儲存引擎

在元資料儲存這塊,此前的 1。0 和 2。0 採取的都是比較簡單的儲存機制,即全記憶體儲存,資料在記憶體中以 hash 表的方式儲存,並輔以跳錶索引,這個 hash 表中有一個 Backup Storage Engine,它可以保證資料的持久化。該方式的優點是全記憶體、效率高,但缺點也很明顯,當啟動時,這部分資料就會全部載入到記憶體之中,不僅記憶體佔用無法精準控制,還會導致開機啟動時間長。

為了解決這些問題,在 3.0 中我們研發了 TDB(一個 B+ 樹格式的的儲存引擎),來儲存元資料及元資料索引。

TDB 的 B+ 樹儲存適合元資料讀多寫少的場景,能夠支援百億時間線的儲存,避免了元資料全記憶體儲存以及長時間的載入,同時解決了在有限記憶體下,表數量膨脹的問題。對於 TDB 是如何實現的,大家如果感興趣,可以去 GitHub(https://github。com/taosdata/TDengine)上看一下原始碼。

TDB 的優點是記憶體可以精確控制,開機啟動速度快,在有限記憶體下也可以儲存海量的元資料,此外如果 TDB 外加 Cache 輔助的話,在一定程度上可以提供接近全記憶體 hash 表的查詢速度。

時序資料儲存引擎

1.   時序資料的更新和刪除

在 2。0 中,更新刪除功能是在引擎開發玩後補充開發的一個功能,因此 2。0 的更新和刪除功能相對簡單,但功能較弱。2。0 的更新是基於一個分佈在橫軸上的時間戳,更新資料的操作就是在後面追加相同時間戳的資料,簡單來講就是用亂序資料的方法來處理更新,然後查詢引擎把這些亂序資料進行合併,就得到了更新後的結果。刪除的實現更加簡單,近似於物理刪除,要刪除的資料會在記憶體、硬碟上被直接“幹掉”,效率相對較低。

TDengine 3.0 完全拋棄了 2.0 的更新刪除機制,在設計層面考慮了更新和刪除的實現,引入了版本號,

把時序資料變成了二維圖形上的點,每個寫入請求都帶有一個版本號,版本號按照寫入請求處理順序遞增。

那 3。0 具體是如何做更新的?如上圖所示,這些藍色的點是你要更新的資料,資料的版本號肯定比要更新的舊資料版本號大,所以我們就引入了版本號機制。當時間戳相同時,版本號大的資料將更新版本號小的資料,因為版本號大的資料是後寫入的資料,相對較“新”。以前每張表中的資料,不論在記憶體裡還是在硬碟中,都是按照時間戳進行排序的,但在引入了版本號之後排序規則也進行了修改。首先還是按時間戳進行排序,在時間戳相同的情況下要按照版本號進行排序,在這樣的排序流程下,我們就可以把資料更新用一個近乎於追加的方式處理,查詢引擎負責將最後的資料合併整理後得到最終結果。       在 3。0 中,時序資料的刪除機制也完全重做。相比 2。0,3。0 支援的過濾條件也明顯增加,比如 where tag、where timestamp 等等。那具體底層是如何實現的呢?首先還是基於版本號機制。

對於刪除操作來說,我們需要記錄開始和結束的時間區間,以及刪除請求的版本號,如上圖所示,一個刪除請求對應二維圖上的一個紫色矩形,這個矩形內部的所有點都被刪除了。

在 3.0 中,時序資料刪除時會追加一條(st, et, version)的記錄元組,在查詢時,查詢引擎會將寫入的資料和刪除記錄元組進行最終的合併,並得到刪除後的最終結果。

採用這種機制,刪除功能對於寫操作變得相對簡單了,但是對於查詢而言,則變得更加複雜了。查詢在合併資料時,要判斷記錄是不是被刪除了,即檢查記錄是不是在所有的刪除區間(矩形)裡面,這是相當耗時的。       TDengine 3。0 採用

Skyline 演算法

來提高有刪除資料下的查詢速度。這一演算法的本質是構造一個點資料,用來代替所有的刪除區間,如上圖中的兩個矩形刪除區域可以用三個點來表示。原來對於每條記錄都要檢查是否被刪除的演算法,現在變成了一個單向掃描過濾的操作,從而大大提高查詢速度。

2.   多表場景下的儲存最佳化

在時序資料場景下,海量資料代表的也有可能是海量的表。在有些業務場景下表數量非常之多,但採集的資料卻很少,比如有一千萬張表,但每天每張表採集資料只有兩條,這種場景對於 2。0 的儲存結構並不是很友好。在 TDengine 2。0 中,一張表會落在硬碟上,一個數據塊裡面只有一張表的資料;如果有一千萬張表,每張表兩條資料,那一個數據塊就只有兩條記錄,壓縮都沒法壓縮,而且這種情況下壓縮的話還會導致資料的膨脹。TDengine 的超級表下面所有子表都共享同一個 schema,這樣的話在 last 檔案裡我們就可以將同一個超級表下不同子表的資料合併成一個數據塊——原來一個表裡只有兩條記錄,但如果能把一百張表的兩條記錄合併成一個數據塊,那就有兩百條記錄。但是這可能需要讀一百次表,如果我們就想讀一次那要怎麼操作呢?

為了解決這個問題,3.0 在資料塊中又加了一個屬性,那就是表的 UID。

在 3。0 的 last 檔案中,資料塊中的資料會按照 UID、Timestamp、Version 排序,即先比較 UID、UID 相同的情況下比較時間戳,時間戳相同的情況下比較版本號,這樣的話就可以更有效地處理多表低頻的場景了。

結語

TDengine 是一個開源產品,3。0 的程式碼也已經開放在了上,非常希望大家能夠積極地參與進來,去下載和體驗。也歡迎大家加入 TDengine 的生態交流群(微訊號:tdengine),和我們以及 TDengine 的關注者和支持者一起交流和探討。

開啟App看更多精彩內容

上一篇:暴跌、內卷,掃地機賽道的新一輪洗牌
下一篇:老年人怎麼泡腳更健康