Apache IoTDB 系列教程-6:性能優化(0.8-0.10)

今天的內容包括建模優化、讀寫性能優化,會涉及一些簡單的原理介紹。主要面向 0.8 - 0.10 版本。git

正文 3754 字,預計閱讀時間 10 分鐘。github

建模指南

關於存儲組數據庫

如今每一個存儲組是一個相對獨立的引擎,並且讀寫鎖是存儲組級別的。所以把存儲組從1改到10,讀寫基本能增速8倍。單個 IoTDB 實例推薦設置 CPU 核數個存儲組。存儲組越多,並行度就越高。咱們以後打算把鎖粒度下放到設備層。apache

設備緩存

設備這個概念沒有在 SQL 語句裏顯示的定義出來,而是在服務器端處理時候默認將倒數第二層設置爲設備,致使你們容易忽略這個概念。先說一下設備影響什麼。性能優化

(1)區分順序數據 和 亂序數據是以設備爲粒度的。舉個例子,假如一個設備在內存裏寫了時間戳 1-10 的數據(不論寫哪些測點,時間戳都會算到這個設備頭上),落盤了,再寫時間戳<=10 的數據,這些數據就會被當作亂序數據緩存並落盤。服務器

(2)設備粒度的時間範圍索引。對於每一個 TsFile 文件,會構造設備粒度的索引在內存裏,假如全部設備都活躍,N 個 TsFile,D 個設備,就有 N*D 條索引。百萬級設備的索引內存會吃不消。這個東西咱們會在一兩個版本內改掉。網絡

再說一下怎麼設計建模來控制設備數。對於實際應用設備和傳感器層次比較簡單的狀況比較好說,設備下直接是傳感器層,通常不會建錯。對於設備下有多層結構的就要注意了。工具

好比我一個設備下有10個傳感器(s1,s2,...,s10),每一個傳感器採集10個時間序列的數據(f1,f2,...f10)。這時候很容易建成 root.xxx.device.s1.f1 這種。當你建成這種時候,你覺得的 device 就再也不是你覺得的 device 了,實際的device 變成了 root.xxx.device.s1 。 實際 device 數量就是你覺得的 10 倍了。性能

怎麼辦嘞,若是設備下的子設備很少,這樣建模也沒啥問題,只要你內心清楚系統中實際有多少個設備就行,這樣溝通不會出現誤差,便於之後排查問題。

若是子設備很是多,能夠把設備後的那幾層壓成一層,好比 root.xxx.device.s1_f1 。因爲咱們是以 . 做爲分隔符的,這樣 s1_f1 就變成 1 層了。 實際的設備仍是 root.xxx.device。

Measurement 定義

Measurement 也就是最後一層的測點。假如一個測點是 INT32 或者 INT64 類型的,並且大部分時間這個數據的值都同樣,沒什麼變化,這時候用 RLE 編碼就很好。能夠大大節省磁盤空間,固然刷盤速度也會變快。壓縮方式開着 SNAPPY 就挺好。

Tag & Attribute

0.10.0 引入的這兩個概念,容易分不清這兩個有啥區別。 雖然都是 key-value 類型的屬性。可是 Tag 是能夠反向查詢時間序列的元數據的,假若有個 tag  的 key 是 owner,就能夠用 show timeseries where owner=Thanos 查滅霸擁有的時間序列。Tag 常駐內存,有Tag到時間序列的索引。

Attribute 就是普通屬性了,好比有個屬性是 description="this is my series"。這些屬性只能是給定時間序列的路徑順帶展現一下,輔助人查看的。

所以,要根據實際需求進行區分,那些須要作反向查詢的屬性,就建成 tag,其餘的就搞成 attribute 就好了。 

讀寫優化

讀和寫關係密切,數據的寫入和參數配置會影響查詢性能。

寫入接口

以 0.10 爲例,先同類比較,insertRecords 接口確定比 insertRecord 接口要快,這個相似 JDBC 的 executeBatch 和 execute 的區別,節省了網絡通訊次數。同理,insertTablets 比 insertTablet 要快,createMultiTimeseries 也比 createTimeseries 要快。

進一步,insertRecords 方法咱們提供了兩種,一種是傳 Object 的 value,一種是傳 String 的 value。若是客戶端能獲取 value 的類型,建議用 Object 的,會比 String 的快 25% 左右。

跨類比較的話,若是不考慮客戶端作格式轉化的耗時,insertTablet 比 insertRecords 要快不少,可能 8 倍以上,節省了不少對象封裝的耗時,batch size 1000左右就能夠。

insertTablet 這個接口默認是沒排序的,若是你能保證一個 Tablet 數據的時間戳是非遞減的,那就能夠多加一個 sorted 爲 true 的參數。就節省了客戶端的排序。

在統計耗時的時候,還須要注意客戶端作格式轉化的耗時,能夠把接口參數構造的時間和執行的時間分開統計。

查詢接口

查詢接口比較簡單,Session 默認的 hasNext 和 next 會返回 RowRecord 結構,這個結構不必定你們都須要,能夠用 SessionDataSet 的 iterator 獲得一個迭代器,而後經過相似 JDBC 的接口去獲得原始數據,避免不少沒用的對象生成。

有一個須要注意的是,查詢有一個 fetchSize,一次從服務器取的點數,這個在最好大一些,1萬左右,否則網絡通訊次數會不少。

順序寫入

對於時序數據庫,時序是一個很重要的概念,最好不要亂來。IoTDB 支持數據的亂序寫入,可是亂序數據會影響查詢性能,主要是對於聚合查詢,原理是亂序數據會讓預計算的統計信息失效,把聚合查詢降維打擊到讀原始數據。

正常狀況下,有個幾倍的亂序都沒問題,可是若是往一個時間段寫入了過多(幾萬倍)的亂序數據,查詢時候有可能爆內存。舉個例子,內存緩衝區寫了時間戳1-10的數據落盤了,而後又寫了 9999 遍 1-10 的緩衝區,這樣磁盤上就有 1 萬個時間戳是 1-10 的數據塊。查詢時候須要將 1萬 個數據塊都讀出來進行合併,內存佔用就比較大了。

面對這種場景,咱們會後臺作數據整理來處理亂序(在0.9引入的merge,可是0.9版本有bug,0.10修掉了,可是先默認關掉了,會在0.11從新開放merge),可是若是能在客戶端避免亂序,就儘可能寫入的時候避免掉。一個設備就按遞增的順序寫入。

若是前邊接了 kafka,最好注意一下,把設備 id 做爲分區粒度,這樣一個設備的數據都會發送到一個分區裏,消費的時候同一個分區也能保證順序。

內存緩衝區

先介紹一下每一個序列在內存裏能緩存多少個點的怎麼算的,用 memtable 大小除以序列數,再除以每一個點的大小,好比long類型就是 16字節(8字節時間戳,8字節值),float是12字節。

memtable 的大小能夠從日誌裏看到,搜 reaches,大概日誌就是 the memtable size xxx reaches the threshold。若是配置文件裏的 enable_parameter_adapter 沒有改成 false,這個 memtable 大小就不固定,隨着註冊的序列數量調整的。

內存緩衝區在必定範圍內儘量大有利於讀寫。平均每一個序列能緩衝100萬點如下是比較好的。可是不建議太大,查詢時候會臨時排序,若是內存中數據點過多,好比千萬級,查詢時候內存排序會佔個十幾秒。

爲了不這個問題,0.10.0 里加了個參數,avg_series_point_number_threshold ,默認是 10000,也就是內存緩衝區中每一個序列最多緩存這麼多點就會刷盤,這個默認參數沒給好,能夠改爲50萬或者100萬。

memtable_size_threshold 這個參數越大,寫入速度快,通常在幾百M到一兩G左右。不要設置的太小,好比幾M,會嚴重影響寫入速度。在設置這個參數時候須要注意不要超內存限制,調這個參數以前須要保證 enable_parameter_adapter 改成 false。

多數據目錄

數據庫的瓶頸在磁盤IO,簡單的提高磁盤IO能力的就是配置多盤。IoTDB 的數據目錄能夠在 data_dirs 參數配置,用逗號分隔多個目錄。能夠每塊盤一個目錄。在寫數據的時候會到這幾個盤裏找最空閒的寫。

客戶端優化

剛纔說了存儲組級別的鎖,對於同一個存儲組的N個寫線程,這N個寫線程都會搶一把鎖,一個存儲組對應不超過50個客戶端比較好,寫線程過多會致使過多的鎖競爭。

線程池 SessionPool 的容量,通常搞個服務器 CPU 核數就能夠了,不要過多。

客戶端的內存,數據的生產和消費速率也能夠監控起來,避免提交的任務積壓過多,若是客戶端內存滿了,會出現一個現象:客戶端發送請求到服務器,服務器執行和返回很快,可是客戶端接收結果會很慢。

容易爆內存的點

select * from root 這個語句在序列過多時候最好不要作,這個操做會把整個庫當作一張表,一下查出來全部列的一批數據,容易爆內存,咱們會在0.11版本加個檢查,及時拒絕。

show timeseries 在 0.10 及之前的版本會把系統全部序列在內存裏拷貝一遍傳給客戶端,若是序列過多,最好指定前綴作個過濾。或者 show child paths 一層一層往下查。

時間序列過多(億級),元數據可能爆內存,能夠按照一條時間序列 200字節估計一下,大概1千萬序列會佔2G元數據(就是那個元數據樹)。

總結

數據庫前期須要比較多的手動調優,如今的自動調優工具還有待完善,咱們的目標是越簡單越好,0.11 版本會完善內存和參數配置。今天內容比較多,以後想到什麼再出續集!

歡迎關注,轉發,給 github 點 star!

https://github.com/apache/incubator-iotdb/tree/master