Kudu系列-基礎


Apache Kudu 支持Insert/Update/Delete 等寫操作(Kudu 隨機寫效率也很高, 實測對一個窄表做全字段update, 其速度達到了Insert速度的88%, 而vertica的update效率比insert差很多), Kudu 表文件是列式數據格式(和Parquet格式類似), 所以Kudu是一個支持記錄修改的分析型系統, Kudu+Impala組合起來就是一個很有競爭力的MPP架構.

SQL on kudu 不同與SQL on hadoop, Hive 和其他 SQL on hadoop都是read on schema方式, 而 impala on kudu 是 write on scheme(這和普通的DB一樣), 這就導致了兩個不同點:
1. impala 插入kudu 表, 將根據記錄的分區鍵寫到對應的kudu tablet中, 而SQL on hadoop無法精細控制記錄寫入到哪個data node中.
2. 不合法的記錄將無法寫入到kudu表, 而使用hive 卻能寫入到hdfs文件中, 比如一個int字段,我們無法將'abc'這樣的字符串寫入到kudu表; 但使用hive可以寫入, 只是讀取時該字段被處理為null.
3. 因為kudu知道一個記存放在哪里(通過主鍵), 如果我們的Select/delete/update語句能利用上主鍵(聚集主鍵), 執行效率比較高. 如果利用不上的話, 效率就差了.

Apache Kudu 雖然是Cloudera貢獻的, 但並沒有包含在CDH中, 需要單獨安裝, 因為 Kudu 其實並不依賴 Hadoop 運行環境, 文件並不是存放在hdfs上. 但如果搭配Impala使用, 就需要Hadoop環境(impala以來Hive).


---==============================
安裝 kudu
---==============================
在CDH 5.5.10 VM版本以上(impala 2.7版以上), 使用下面命令安裝kudu軟件.
$ sudo yum install kudu # Base Kudu files
$ sudo yum install kudu-master # Kudu master init.d service script and default configuration
$ sudo yum install kudu-tserver # Kudu tablet server init.d service script and default configuration
$ sudo yum install kudu-client0 # Kudu C++ client shared library
$ sudo yum install kudu-client-devel # Kudu C++ client SDK


kudu安裝包會自動在Linux alternatives 數據庫下創建 kudu-conf 入口,
sudo alternatives --display kudu-conf

手動啟動kudu
$ sudo service kudu-master start
$ sudo service kudu-tserver start

自動啟動設置
$ sudo chkconfig kudu-master on # RHEL / CentOS / SLES
$ sudo chkconfig kudu-tserver on # RHEL / CentOS / SLES

 

---==============================
主鍵
---==============================
主鍵: 每個表都必須有一個Unique主鍵, 主鍵可以是單獨字段也可以由多個字段組成, 但主鍵中的每個字段都應該是non null, 而且不能是boolean和浮點類型字段. 重復的PK的記錄將無法Insert到Kudu表中, 而且主鍵值是不能修改的. Kudu主鍵是聚集索引, 也就是說每個tablet中的記錄將按照主鍵排序存儲, 在scan時候能利用這些排序加快記錄查找.
通過Kudu API更新/刪除記錄必須提供主鍵, 通過Impala查詢引擎沒有這個限制.

注意:
1. 在建表DDL語句中, 主鍵要放到字段清單的前段.
2. 除了主鍵, kudu不支持建立其他索引
3. 主鍵值不能被update
4. insert語句中, 主鍵名是大小寫敏感的, 其他語句大小寫不敏感. [懷疑是kudu的bug]


主鍵的選擇原則:
1. 選擇性強的字段(比如id 類) 應該放在PK清單最前面, 這個規則對查詢性能影響最大.
2. PK清單中只加必要的unique字段, 越少越好.
3. 如果查詢針對PK中所有字段都加了條件, 其性能是最優的. 但只要有一個PK字段未加條件, 相當於完全用不上PK索引,性能就很差.
我專門又寫了個文章討論主鍵選擇策略, http://www.cnblogs.com/harrychinese/p/kdu_pk.html

 

---==============================
分區
---==============================
分區鍵: Kudu要求分區鍵必須屬於主鍵. 如果沒有顯式指定分區鍵的時候, 主鍵就是分區鍵, 即按照主鍵Hash分區, 分區數量為1個, 而且只有缺省方式才能建立1 個 partition,  專門用語句指定的話, partition數量必須大於1.
既然分區鍵一定屬於主鍵, 所以Kudu表的主鍵最好除了包含業務主鍵, 最好再加上也寫常用的查詢字段, 比如時間字段和userId這樣的字段, 這樣能更好地設計分區策略.

分區數如何確定:

Kudu每個表在tablet server上的切片數量不能大於60個(含replica), 按照20個tablet server算, 一個表最多400個partition, Impala的限制是100K個, Vertica的限制是512個.
通常增加分區(切片)數, 對於寫入是有利的, 但對於讀取可能有利有弊(會提升讀取的並行度, 但會增加掃描切片的數量).
總的原則是: 切片數不要大於集群中CPU的總core數, 如果切片數超過CPU core數, 性能反倒會下降.
具體為:
1. Fact表, 切片數<=集群中CPU的總core數, 並是機器數量的倍數, 以防止數據傾斜.
2. Dim表, 切片數應<<集群中CPU的總core數, 保證每個tablet的size至少1GB.


分區方法:
Kudu分區方法只能在建表的時候確定, 所以確定分區方法一定要仔細考慮. Kudu支持Hash和Range分區, 而且支持使用Hash+Range作分區.
1. hash 分區: 寫入壓力較大的表, 比如發帖表, 按照帖子自增Id作Hash分區, 可以有效地將寫壓力分攤到各個tablet中. 因為hash的分區鍵是自增性的, 就不會有 hot 切片(瓶頸). 可見, 純hash分區比較適合選擇性好的分區鍵.
2. range 分區: 如果時間字段可以作為主鍵的一部分, 可以考慮將時間字段作為range分區鍵, 往往會得到較好的查詢效果. 因為Kudu分區數不能超過1000個, 所以通常不適合用日期作分區鍵, 因為這樣僅能存放3年數據, 往往使用月份作為分區鍵. 純range分區的表一般不能太大, 否則當前月的分區往往會是hot分區, 影響性能.
3. hash + range 分區: 對於較大的事實表, 推薦使用 hash+range分區方法, 使用選擇性強的字段作為hash鍵, 使用月份作為range分區鍵, 這樣能避免純range分區表的缺點.

 

---==============================
Kudu的一些限制:
---==============================

壓縮前的字段長度應該小於64KB.
集群最多100 個tablet server.
每個tablet server推薦最多1000個tablet(含replica).
每個tablet server最多管理4TB的數據(壓縮后的數據量, 含replica).
每個tablet server存儲的數據最大為4T.
每個tablet 最好控制在20GB內, 1000萬筆以內.
每個表在單個tablet server上最多60個partition(含replica). 按照20個tablet server算(replica三份), 一個表最多400個partition; 按照50個tablet server算, 一個表最多1000個partition.
更多信息: https://kudu.apache.org/docs/known_issues.html



---==============================
SQL on Kudu 工具:
---==============================
impala 操作kudu的文檔: https://impala.apache.org/docs/build/html/topics/impala_kudu.html
impala 或 Spark SQL

雖說 Kudu 提供了高效的單行插入和更新功能, 但Impala還是側重數據分析, 並沒有對單行Insert/Update進行優化, 所以如果有大量小型DML的操作, 推薦使用Kudu API而不是Impala SQL方式. 當一定要使用Impala SQL時, 最好采用批處理方式, 即一個Insert 語句一次性插入多行數據.  測試Insert 寫到Vertica小集群速度近為7000筆/秒, 而Impala(通過Hive JDBC Driver)寫到Kudu僅僅70筆/秒, Impala(通過Impala JDBC Driver)寫到Kudu僅僅40筆/秒, 


---==============================
Kudu 建表
---==============================
Kudu的數據類型:
BOOL, INT8, INT16, INT32, BIGINT, INT64, FLOAT, DOUBLE, STRING, BINARY, TIMESTAMP.
但 DECIMAL,CHAR,VARCHAR,DATE 和 ARRAY 等復雜類型不受支持.

Impala 的數據類型:
BIGINT,BOOLEAN,CHAR,DECIMAL,DOUBLE,FLOAT,INT,REAL,SMALLINT,STRING,TIMESTAMP,TINYINT,VARCHAR
還有Array,Struct,Map等復雜類型

當我們使用Impala創建Kudu格式的表, 必須使用kudu數據類型建表, 而不能使用impala支持的數據類型.

kudu 表文件是列式數據格式, 另外kudu允許為每個字段指定不同的壓縮的encoding 算法和壓縮算法.


強烈推薦 impalad 服務加上啟動參數 -kudu_master_hosts, 這樣每個impala定義kudu表的DDL語句中, 就不需要在TBLPROPERTIES中指定kudu.master_addresses屬性.
kudu.table_name 表屬性, 可以理解為在kudu中的表名(物理表名), 而建表語句的表名可以理解為impala catalog中的名稱(邏輯名稱).

drop table if exists kudu_somedb.kudu_test ;
CREATE TABLE kudu_somedb.kudu_test
(

ActionDate timestamp ENCODING BIT_SHUFFLE COMPRESSION LZ4,
UID string ENCODING PLAIN_ENCODING COMPRESSION SNAPPY,
Operation string ENCODING DICT_ENCODING COMPRESSION SNAPPY,
EUTIME timestamp ENCODING BIT_SHUFFLE COMPRESSION LZ4,-- default now(),
EID_kudu string ENCODING PLAIN_ENCODING COMPRESSION SNAPPY,-- concat(cast(unix_timestamp() as string),uuid()) ,
PRIMARY KEY (ActionDate,Uid,Operation)
)
PARTITION BY HASH (Uid) PARTITIONS 6 ,
RANGE (ActionDate) (
PARTITION '2018-03-16' <= VALUES < '2018-03-17',
PARTITION '2018-03-17' <= VALUES < '2018-03-18',
PARTITION '2018-03-18' <= VALUES < '2018-03-19',
PARTITION '2018-03-19' <= VALUES < '2018-03-20'
)

STORED AS KUDU
TBLPROPERTIES (
'kudu.table_name' = 'somedb.kudu_test',
'kudu.master_addresses' = '10.205.6.121:7051,10.205.6.122:7051,10.205.7.134:7051' ,
'kudu.num_tablet_replicas' = '3'
);

下面是增加和刪除 range 分區的語法:
alter table kudu_somedb.kudu_test add  IF NOT EXISTS range partition '2018-03-20' <= VALUES < '2018-03-21' ;
alter table kudu_somedb.kudu_test drop  IF EXISTS  range partition '2018-03-20' <= VALUES < '2018-03-21' ;

 

-- Create select 方式建表語法, 注意這里的primary key()中的字段名必須使用小寫字母.
create table temptable.test
Primary key(id,name)
STORED AS KUDU
TBLPROPERTIES (
'kudu.table_name' = 'temptable.test',
'kudu.master_addresses' = '10.205.6.121:7051,10.205.6.122:7051,10.205.7.134:7051',
'kudu.num_tablet_replicas' = '1'
)
as
select 1 id, 'name' name, 20 ag
;

---==============================
善用 if exists 語句
---==============================
很多時候我們需要在程序中創建臨時表, 邏輯一般是, 如果臨時表存在先drop, 然后新建這個臨時表. 因為是在程序中, 所以刪除動作不希望報錯, 這時候可以在drop table后加上 if exists. 下面用視圖說明這個技巧.

drop view if exists kudu_guba.v1
;

create view if not exists kudu_guba.v1
as
select 1 a
;

 

---==============================
蛋疼的大小寫規則:
---==============================
1. 使用Impala insert SQL時候, 主鍵字段名必須是小寫; 而update/delete無限制.
2. 使用 impala 創建kudu表, kudu.table_name名稱會保持DDL語句中的大小寫, 字段名會自動轉成小寫字母.
3. 使用 kudu API 訪問表時候, 表名稱大小寫敏感的(必須按照kudu.table_name名的寫法), 字段名也是大小寫敏感的(也就是必須使用小寫字母).
4. 采用Create select 方式建表語法, 注意這里的primary key()中的字段名必須使用小寫字母.
5. 普通的DDL建表, primary key()中的字段名大小寫都可以.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM