聯網數據庫 IoTDB —— 存儲引擎原理篇
IotDB簡介
Apache IoTDB(物聯網數據庫)是一體化收集、存儲、管理與分析物聯網時序數據的軟件系統。Apache IoTDB 采用輕量式架構,具有高性能和豐富的功能,并與Apache Hadoop、Spark和Flink等進行了深度集成,可以滿足工業物聯網領域的海量數據存儲、高速數據讀取和復雜數據分析需求。
應用場景
高端設備
在高端制造業中,有很多設備配備有傳感器來收集工作狀態數據,例如氣象站,風力渦輪機是常見的高端設備。這些設備如果支持Java或Go(正在開發中),則可以運行TsFile在本地存儲數據。通過這種方式,TsFile可以提供具有高吞吐、高壓縮率和毫秒級查詢延遲的數據管理功能。結合TsFile-Sync工具,可以將TsFiles同步到數據中心。
本地控制器
在工廠現場,LAN網絡下有數十臺設備。IoTDB可以安裝在工廠的本地控制器服務器上,以從這些設備接收數據。安裝有IoTDB的本地服務器(普通PC或工作站)可以使用類SQL存儲和查詢數據。此外,使用TsFile-Sync工具,可以將本地控制器上的TsFile文件傳輸到云上安裝有IoTDB實例的數據中心。
云數據管理
在高速網絡(車聯網等)的場景中,安裝有傳感器的汽車可以以一定頻率收集自身的監視信息(行駛狀態等)。通常,這些汽車設備的硬件配置有限,并且難以進行復雜的應用。輕量級的IoTDB(IoTDB客戶端)應運而生。借助JDBC API(或MQTT),它可以使用窄帶IoT或4G/5G發送數據,從而將設備和云連接在一起。
存儲架構
IoTDB 存儲引擎基于 LSM Tree 結構設計,寫入的數據先記錄 WAL,再寫到內存 memtable,在后臺逐步刷到磁盤 TsFile;磁盤上的 TsFile 通過一定的規則進行 Compaction,保證查詢效率。
那么我們就先從LSM Tree 說起吧。
LSM Tree
LSM Tree(Log Structured Merge Tree) 并不像B+樹、紅黑樹一樣是一顆嚴格的樹狀數據結構,其實它是一種存儲結構,目前HBase,LevelDB,RocksDB這些NoSQL存儲都是采用的LSM樹。
Mentable
MemTable是在內存中的數據結構,用于保存最近更新的數據,會按照Key有序地組織這些數據,LSM樹對于具體如何組織有序地組織數據并沒有明確的數據結構定義,例如Hbase使跳躍表來保證內存中key的有序。
因為數據暫時保存在內存中,內存并不是可靠存儲,如果斷電會丟失數據,因此通常會通過WAL(Write-ahead logging,預寫式日志)的方式來保證數據的可靠性。
SSTable(Sorted String Table)
有序鍵值對集合,是LSM樹組在磁盤中的數據結構。為了加快SSTable的讀取,可以通過建立key的索引以及布隆過濾器來加快key的查找。
我們在寫入數據時,首先將對數據的修改增量保存Memtable中,同時會提交wal,當Memtable達到指定大小限制之后批量把數據刷到磁盤(SSTable)中,磁盤中樹定期可以做merge操作,合并成一棵大樹,以優化讀性能。不過讀取的時候稍微麻煩一些,讀取時看這些數據在內存中,如果未能命中內存,則需要訪問較多的磁盤文件。極端的說,基于LSM樹實現的hbase寫性能比mysql高了一個數量級,讀性能卻低了一個數量級。
綜上所述,LSM樹會將數據的所有增、刪、改等操作,記錄到內存中,再順序刷新到磁盤里,這就造成了其與B+樹最大的不同,B+樹會直接在數據的位置更新,而LSM樹則可能追到到不同的SSTable中,當然,最新的那條記錄才是準確的。這樣設計的雖然大大提高了寫性能,但同時也會帶來一些問題:
1)冗余存儲,對于某個key,實際上除了最新的那條記錄外,其他的記錄都是冗余無用的,但是仍然占用了存儲空間。因此需要進行Compact操作(合并多個SSTable)來清除冗余的記錄。2)讀取時需要從最新的倒著查詢,直到找到某個key的記錄。最壞情況需要查詢完所有的SSTable,這里可以通過前面提到的索引/布隆過濾器來優化查找速度。
壓縮(Compact)策略
在正式介紹壓縮策略以前,我們有必要先了解一下這3個基礎概念,這也是以下壓縮策略需要權衡的關鍵:讀放大(Read Amplifier)、寫放大(Write Amplifier)、空間放大(Space Amplifier)
- 讀放大(Read Amplifier):是指我們要找到一個我們所需的數據,需要進行多少次磁盤的讀操作。例如我們出門前需要找鑰匙,你可能要每個房間,每件衣服口袋翻一遍。這并不應該是一個正常操作。
- 寫放大(Write Amplifier):寫入數據時實際寫入的數據量大于真正的數據量。例如在LSM樹中寫入時可能觸發Compact操作,導致實際寫入的數據量遠大于該key的數據量。
- 空間放大(Space Amplifier):數據實際占用的磁盤空間比數據的真正大小更多。上面提到的冗余存儲,對于一個key來說,只有最新的那條記錄是有效的,而之前的記錄都是可以被清理回收的。
size-tiered 策略
size-tiered策略相對簡單粗暴,其主旨保證每層SSTable的大小相近,同時限制每層SSTable的數量。如下圖所示,每層限制SSTable為N,當每層SSTable達到N后,則觸發Compact操作合并這些SSTable,并將合并后的結果寫入到下一層成為一個更大的sstable。
由此可以看出,當層數達到一定數量時,最底層的單個SSTable的大小會變得非常大。并且size-tiered策略會導致空間放大比較嚴重。即使對于同一層的SSTable,每個key的記錄是可能存在多份的,只有當該層的SSTable執行compact操作才會消除這些key的冗余記錄。
leveled策略
leveled策略也是采用分層的思想,每一層限制總文件的大小。但與size-tiered策略不同的是,leveled會將每一層切分成多個大小相近的SSTable。這些SSTable是這一層是全局有序的,這意味著一個key在每一層至多只有1條記錄,不存在冗余記錄。并且每層維持“唯一一個” Run。我們來了解一下Run這個LSM里面重要的概念,以下摘自Wiki:
Each run contains data *sorted* by the index key. A run can be represented on disk as a single file, or alternatively as a collection of files with *non-overlapping* key ranges.
run需要滿足什么條件呢?第一sorted,第二non-overlapping key ranges
如下圖所示:
每層的大小是上一級的T(下圖是10) 倍,層數越高,data越多但是也越舊.每層都有target size
整個的過程可以簡化成:in memory的table寫滿了,那么就flush到第一級的Disk SStable并且做compaction,要是第一級又滿了,就又開始flush到第二級做compaction,以此類推直到max level, 下面幾張圖描述了整個過程。
假設如下圖是起始狀態
level0 有數據寫入,這個時候觸發level0到level1的compact
level1 超出限制,觸發level1到level2compact
此時會從level1中選擇至少一個文件,然后把它跟level2有交集的部分(非常關鍵)進行合并。生成的文件會放在level2
由于level1第二SSTable的key的范圍覆蓋了level2中前三個SSTable,那么就需要將level1中第二個SSTable與level2中前三個SSTable執行Compact操作。
level2合并完成后,如果其超出了level2閾值的限制,那么會觸發level2到level3的compact
以此類推,上一層達到閾值以后,就出觸發到下一層的compact操作。
值得一提的是,無論是同級還是不同級,不重疊的key,可以并行進行。
簡單總結,Level compaction目標就是維持每個level都保持住one data sorted run,所以每個level都可以和下一個level做compaction,同時很有可能會被上一個level做compaction。這樣做好處就是level之間的compaction可以multithread來做(除了memory到level0),提高效率。
LSM Tree vs. B+ Tree vs. Hash
對比三種引擎的實現:
- hash存儲引擎:哈希表持久化的實現,可以快速支持增刪改查等隨機操作,且時間復雜度為o(1),但是不支持順序讀取掃描,對應的存儲系統為k-v存儲系統的實現。
- b樹存儲引擎是b樹的持久化實現,不僅支持單條記錄的增刪改查操作,還支持順序掃描,對應的存儲系統就是mysql。
- lsm樹存儲引擎和b樹存儲引擎,一樣支持,增刪改查,也支持順序掃描操作。LSM犧牲了讀性能,提高寫性能。
Iotdb寫入流程
相關代碼
- org.apache.iotdb.db.engine.StorageEngine
負責一個 IoTDB 實例的寫入和訪問,管理所有的 StorageGroupProsessor。
- org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor
負責一個存儲組一個時間分區內的數據寫入和訪問。管理所有分區的TsFileProcessor。
- org.apache.iotdb.db.engine.storagegroup.TsFileProcessor
負責一個 TsFile 文件的數據寫入和訪問。
這篇文章,我們先介紹到這里,下一篇文章再見。
參考鏈接
http://iotdb.apache.org/zh/SystemDesign/StorageEngine/StorageEngine.html
http://iotdb.apache.org/zh/SystemDesign/TsFile/Format.html
http://iotdb.apache.org/zh/SystemDesign/StorageEngine/DataPartition.html
https://www.scylladb.com/2018/01/31/compaction-series-leveled-compaction/
https://github.com/facebook/rocksdb/wiki/Leveled-Compaction
https://www.cnblogs.com/yankang/p/11041173.html
https://zhuanlan.zhihu.com/p/181498475
https://zhuanlan.zhihu.com/p/151234708
https://zhuanlan.zhihu.com/p/112574579
本文轉載自微信公眾號「麒思妙想」,可以通過以下二維碼關注。轉載本文請聯系麒思妙想公眾號。