Kudu表和關係型數據庫的表類似,都有着結構化的數據模型。對於最佳性能和操做的穩定性來說,schema的設計相當重要。沒有哪種schema可以適用於全部的表。算法
建立Kudu表時,涉及到列設計、主鍵設計和分區設計。對於傳統的非分佈式關係型數據庫來說,只有分區是新概念。數據庫
優雅的schema應該具有如下條件:性能優化
數據的讀寫均勻分佈到每一個tablet server,這一點主要受到分區的影響。服務器
tablet可以以穩定、可預測的速率增長,加載的數據保證全天候可用,這主要受到分區的影響。架構
掃描時讀取最少的數據量,這一點主要受主鍵設計的影響,但分區也會起到重要做用。app
好的shema設計依賴於數據特徵、對數據操做什麼以及集羣的拓撲結構。Schema設計對於kudu集羣性能最大化來講是最重要的事情。分佈式
4.1. 數據類型
Kudu表包含多個列,每一個類都有類型,非主鍵列能夠爲null。支持的列類型有:ide
布爾
8位有符號整數
16位有符號整數
32位有符號整數
64位有符號整數
unixtime_micros(Unix時代以來的64位微秒)
單精度(32位)IEEE-754浮點數
雙精度(64位)IEEE-754浮點數
十進制(詳見十進制類型)
UTF-8編碼字符串(最多64KB未壓縮)
二進制(最多64KB未壓縮)
Kudu利用強類型列和列式磁盤存儲格式來提供高效的編碼和序列化。爲了充分利用這些功能,應將列指定爲適當的類型,而不是使用字符串或二進制列來模擬「無模式」表。除了編碼以外,Kudu還容許在每列的基礎上指定壓縮。工具
//沒有版本和時間戳列 ,同hbase不一樣,kudu沒有提供version和timestamp列來跟蹤行的變化。若是須要的話,須要自行設計一列。oop
4.2. Decimal類型
該類型是具備固定標度和精度適合於金融等算術運算,float與double的不精確表示和舍入行爲比較不切實際。該decimal類型對於大於int64的整數和主鍵中具備小數值的狀況也頗有用。
精度:表示列能夠表示的總位數,不管小數點的位置如何。此值必須介於1和38之間,而且沒有默認值。例如,精度爲4表示最大值爲9999的整數值,或者表示最多99.99的值,帶有兩個小數位。您還能夠表示相應的負值,而不會
對精度進行任何更改。例如,-9999到9999的範圍仍然只須要4的精度。
刻度:表示小數位數。該值必須介於0和精度之間。刻度爲0會產生整數值,沒有小數部分。若是精度和刻度相等,則全部數字都在小數點後面。例如,精度和刻度等於3的小數能夠表示介於-0.999和0.999之間的值。
性能考慮:Kudu將每一個值存儲在儘量少的字節中,具體取決於decimal指定的精度。所以,爲方便起見,不建議儘量使用最高精度。這樣作可能會對性能,內存和存儲產生負面影響。
在編碼和壓縮以前:
精度爲9或更小的十進制值以4個字節存儲。
精度爲10到18的十進制值以8個字節存儲。
精度大於18的十進制值以16個字節存儲。
alter命令不能修改的列的精度和刻度
4.3. 列編碼
能夠根據列的類型進行編碼。
列類型
編碼
默認
int8, int16, int32
plain, bitshuffle, run length
bitshuffle
int64, unixtime_micros
plain, bitshuffle, run ength
bitshuffle
float, double, decimal
plain, bitshuffle
bitshuffle
bool
plain, run length
run length
string, binary
plain, prefix, dictionary
dictionary
4.3.1. Plain encoding
數據以其天然格式存儲。例如,int32 值存儲爲固定大小的32位little-endian整數。
4.3.2. Bitshuffle編碼
從新排列一個值塊以存儲每一個值的最高有效位,而後是第二個最高有效位,依此類推。最後,結果進行LZ4壓縮。 若是值重複的比較多,或者按主鍵排序時值的變化很小,Bitshuffle編碼是一個不錯的選擇。
4.3.3. 運行長度編碼
對連續的重複值採用壓縮存儲,主要是經過只存儲值和個數。該編碼對按主鍵排序時具備許多連續重複值的列有效。
4.3.4. 字典編碼
建立一個字典存放全部的值,每一個列值使用索引進行編碼存儲。若是值的個數較少,這種方式比較有效。反之Kudu將透明地回退到該行集的純編碼。這在flush期間進行評估計算。
4.3.5. 前綴編碼
在連續的列值中對公共前綴進行壓縮。對於有公共前綴的值或主鍵的第一列或許有效,由於tablet中的行是經過對主鍵排序存儲的。
4.3.6. 列壓縮
Kudu容許列使用壓縮LZ四、Snappy或zlib壓縮編解碼器。默認狀況下,不進行壓縮。若是減小存儲空間比原始掃描性能更重要,請考慮使用壓縮。
每一個數據集的壓縮方式都不一樣,但通常來講LZ4是性能最佳的編解碼器,而zlib空間壓縮比較大。Bitshuffle編碼的列自動使用LZ4壓縮,所以不建議在此編碼之上應用其餘的壓縮。
每一個Kudu表必須聲明由一列或多列組成的主鍵。與RDBMS主鍵同樣,Kudu主鍵強制執行惟一性約束。嘗試插入具備與現有行相同的主鍵值的行將致使重複鍵錯誤。
主鍵列必須是非可空的,而且不能夠是boolean,float或double類型。
表建立指定主鍵後,就不能更改。
與RDBMS不一樣,Kudu不提供列的自增,所以應用程序必須提供完整的主鍵。
刪除和更新時必須指定完整主鍵。Kudu自己不支持範圍刪除或更新。即都是經過主鍵完成操做。
主鍵值沒法修改。可是,能夠刪除後從新插入曲線救國。
5.1. 主鍵索引
與許多傳統的關係數據庫同樣,Kudu的主鍵是聚簇索引。tablet中的全部行都按主鍵排序的。 掃描Kudu行時,在主鍵列上使用相等或範圍過濾能夠有效地查找行。
5.2. 回填插入的考量
這裏考慮主鍵是時間戳或主鍵的第一列是時間戳的狀況。每次插入,kudu都會主鍵索引存儲區域中查找主鍵看主鍵是否存在,若是存在,就返回主鍵重複錯誤。若是以數據產生的時間做爲主鍵存儲,則熱點數據就比較少,執行存在性檢查時就會比較快,在內存中就能夠命中,不須要走磁盤。
若是是離線的歷史數據,每次插入均可能會預冷(主鍵沒法在內存中定位),就會訪問磁盤,有時甚至是多個磁盤。正常狀況下,kudu能夠達到每秒幾百萬次插入,可是若是是回填數據的話,每秒只能維持幾千的插入量。
回填數據的性能優化:
使主鍵更易壓縮
使用固態盤
改變主鍵結構,使回填主鍵位於連續的範圍中。
kudu中的表被分紅不少tablet分佈在多個tserver上。每一行屬於一個tablet。行劃分到哪一個tablet有分區決定,分區是在表建立期間設置的。
寫入頻繁時,考慮將寫入動做平衡到全部tablet之間可以有效下降單個tablet的壓力,對於小範圍掃描操做比較多的狀況,若是所掃描的數據都爲一個tablet上則能夠提升性能。
kudu沒有默認分區,建表時,kudu不提供默認的分區策略。建議讀寫都較重的table能夠設置和tserver服務器數量相同的分區數。
kudu提供兩種類型的分區:範圍分區和哈希分區。表能夠有多級分區,組合使用範圍和哈希或者多個哈希組合使用。
6.1. 範圍分區
Kudu容許在運行時動態添加和刪除範圍分區,而不會影響其餘分區的可用性。刪除分區將刪除屬於該分區中包含的數據。後續插入到已刪除的分區中將失敗。能夠添加新分區,但它們不得與任何現有範圍分區重疊。Kudu容許在單個事務更改表操做中刪除和添加任意數量的範圍分區。
動態添加和刪除範圍分區對於時間序列特別有用。隨着時間的推移,能夠添加範圍分區以覆蓋即將到來的時間範圍。例如,存儲事件日誌的表能夠在每月開始以前添加月份分區,以便保存即將發生的事件。能夠刪除舊範圍分區,以便根據須要有效地刪除歷史數據。
6.2. 哈希分區
哈希分區按哈希值將行分配到存儲桶中的一個。在單級散列分區表中,每一個桶只對應一個tablet。在表建立期間設置桶的數量。一般,主鍵列用做要散列的列,但與範圍分區同樣,可使用主鍵列的任何子集。
當不須要對錶進行有序訪問時,散列分區是一種有效的策略。散列分區對於在平板電腦之間隨機寫入很是有效,這有助於緩解熱點和不均勻的平板電腦大小。
6.3. 多級分區
Kudu容許表在單個表上組合多個級別的分區。零個或多個哈希分區能夠與範圍分區組合。除了各個分區類型的約束以外,多級分區的惟一附加約束是多級哈希分區不能散列相同的列。
若是使用正確,多級分區能夠保留各個分區類型的好處,同時減小每一個分區類型的缺點。多級分區表中的平板電腦總數是每一個級別中分區數的乘積。
6.4. 修剪分區
當經過掃描條件可以徹底肯定分區的時候,kudu就會自動跳過整個分區的掃描。要肯定哈希分區,掃描條件必須包含每一個哈希列的等值斷定條件。多級分區表的掃描能夠單獨利用每一級的分區界定。
您能夠經過如下方式更改表結構:
重命名錶
重命名主鍵列
重命名,添加或刪除非主鍵列
添加和刪除範圍分區
能夠在單個事務操做中組合多個更改步驟。
Kudu目前有一些已知的侷限性可能會影響到架構設計。
列數
默認狀況下,Kudu不容許建立超過300列的表。咱們建議使用較少列的架構設計以得到最佳性能。
cell大小
在編碼或壓縮以前,單個單元不得大於64KB。在Kudu完成內部複合密鑰編碼以後,構成複合密鑰的單元限制爲總共16KB。插入不符合這些限制的行將致使錯誤返回給客戶端。
行的大小
雖然單個單元可能高達64KB,而Kudu最多支持300列,但建議單行不要大於幾百KB。
有效標識符
表名和列名等標識符必須是有效的UTF-8序列且不超過256個字節。
主鍵不可變
Kudu不容許更新主鍵列。
不可更改的主鍵
Kudu不容許您在建立表後修改主鍵列。
不可更改的分區
除了添加或刪除範圍分區以外,Kudu不容許您在建立後更改表的分區方式。
不可改變的列類型
Kudu不容許更改列的類型。
分區拆分
建立表後,沒法拆分或合併分區。
kudu單臺tablet_server 分區不能超過1500.多臺tablet_server 超過1500 則須要進行評估擴容。
tablet server 22臺
master 5臺
最大數據存儲量爲,複製和壓縮後,每一個tablet server 24TB。
每一個tablet server管理的tablet爲1500,包含tablet的副本。
在建立表時,每一個Tablet server的每一個表的最大tablet數爲60。
基於以上限制,能夠推測出一下內容:
Kudu中存儲的總數據量建議爲:tablet server總數單個tablet server的數據量=2224TB=528TB/3 =176TB
單個tablet的數據量爲:單個tablet server的數據量/每一個tablet server中tablet的總數=24TB/1500=16G。
Kudu支持的壓縮方式有LZ4, Snappy,或zlib。鑑於各類壓縮算法的壓縮比通常不超過50%,則每一個tablet中的數據量在壓縮前的大小建議應小於8G。
kudu單表存儲最大數量:8億
常規表
SUBS層:全部系統的採集數據必須進SUBS層,原則上數據永久存儲,可根據實際狀況調整(數據量很是大,例如一天數據量大於100 GB),kudu表數據保留12個月(根據實際狀況定義),12個月以上的能夠轉儲到hive。
基於kudu存儲的數據,根據業務實際需求按照如下定義保留
ODS、DW、DM、ADS層:
當3個月內的最大訪問跨度小於或等於4天時,建議將保留天數設爲7天。
當3個月內的最大訪問跨度小於或等於12天時,建議將保留天數設爲15天。
當3個月內的最大訪問跨度小於或等於30天時, 建議將保留天數設爲33天。
當3個月內的最大訪問跨度小於或等於90天時,建議將保留天數設爲93天。
當3個月內的最大訪問跨度小於或等於180天時, 建議將保留天數設爲183天。
當3個月內的最大訪問跨度小於或等於365天時,建議將保留天數設爲368天。
以上生命週期定義爲業界通用定義,能夠根據業務實際狀況調整。
根據數據域的不一樣要求存儲,配套相應的冷熱數據分離存儲方案。
對於時間分區表,根據其數據內容的生命週期,按期刪除超出生命週期的分去表
中間表
根據實際狀況設置存儲週期,原則上存儲週期不大於結果表。
備份表
根據實際備份須要設置存儲週期。
臨時表
臨時表的最大生命週期爲10天,針對臨時表生命週期,應配套相應的檢測工具和手段,並在刪除前作好相關提示。
1.hash分區最大不能超過60分區
2.Kudu hash分區建立表例子
CREATE TABLE IF NOT EXISTS [db_name.]table_name ( id BIGINT PRIMARY KEY COMMENT '註釋', agent STRING COMMENT '註釋', ... PRIMARY KEY ( uuid ) ) PARTITION BY HASH (id) PARTITIONS 4 COMMENT '註釋' stored AS kudu;
kudu-混合分區例子:
CREATE TABLE cust_behavior_1 ( id BIGINT, sku STRING, salary STRING, edu_level INT, usergender STRING, group STRING, city STRING, postcode STRING, last_purchase_price FLOAT, last_purchase_date BIGINT, category STRING, rating INT, fulfilled_date BIGINT, PRIMARY KEY (id, sku) ) PARTITION BY HASH (id) PARTITIONS 4, RANGE (sku) ( PARTITION VALUES < ‘g’, PARTITION ‘g’ <= VALUES < ‘o’, PARTITION ‘o’ <= VALUES < ‘u’, PARTITION ‘u’ <= VALUES ) STORED AS KUDU TBLPROPERTIES( ‘kudu.table_name’ = ‘cust_behavior_1 ‘,’kudu.master_addresses’ = ‘hadoop5:7051’);
時間分區例子:
CREATE TABLE IF NOT EXISTS k_ods_collect_dw_yarn_apps_resource_ds( app_id string comment'任務ID', use_type string comment'資源使用類別', name string comment'資源名稱', ds string comment'日彙總', resource_type string comment'資源類別', maximum_allocation bigint comment'最大分配', minimum_allocation bigint comment'最小分配', shorthand_representation string comment'快速描述', units string comment'單位', value bigint comment'值', collect_time bigint comment'採集時間', PRIMARY KEY (app_id,use_type,name,ds)) partition by hash(app_id,use_type,name,ds) partitions 4, RANGE (ds) ( PARTITION '20201112' <= VALUES < '20210101', PARTITION '20210101' <= VALUES < '20210201', PARTITION '20210201' <= VALUES < '20210301', PARTITION '20210301' <= VALUES < '20210401', PARTITION '20210401' <= VALUES < '20210501', PARTITION '20210501' <= VALUES < '20210601', PARTITION '20210601' <= VALUES < '20210701', PARTITION '20210701' <= VALUES < '20210801', PARTITION '20210801' <= VALUES < '20210901', PARTITION '20210901' <= VALUES < '20211001', PARTITION '20211001' <= VALUES < '20211101', PARTITION '20211101' <= VALUES < '20211201', PARTITION '20211201' <= VALUES < '20220101' ) COMMENT 'k_ods_collect_dw_yarn_apps_resource_ds' stored as kudu;