1、KUDU分區數必須預先預約 2、在內存中對每一個Tablet分區維護一個MemRowSet來管理最新更新的數據,默認是1G刷新一次或者是2分鐘。後Flush到磁盤上造成DiskRowSet, 多個DiskRowSet在適當的時候進行歸併處理 3、和HBase採用的LSM(LogStructured Merge,很難對數據進行特殊編碼,因此處理效率不高)方案不一樣的是,Kudu對同一行的數據更新記錄的合併工做, 不是在查詢的時候發生的(HBase會將多條更新記錄前後Flush到不一樣的Storefile中,因此讀取時須要掃描多個文件,比較rowkey,比較版本等,而後進行更新操做), 而是在更新的時候進行,在Kudu中一行數據只會存在於一個DiskRowSet中,避免讀操做時的比較合併工做。 那Kudu是怎麼作到的呢? 對於列式存儲的數據文件,要原地變動一行數據是很困難的,因此在Kudu中,對於Flush到磁盤上的DiskRowSet(DRS)數據, 其實是分兩種形式存在的,一種是Base的數據,按列式存儲格式存在,一旦生成,就再也不修改,另外一種是Delta文件,存儲Base數據中有變動的數據, 一個Base文件能夠對應多個Delta文件,這種方式意味着,插入數據時相比HBase,須要額外走一次檢索流程來斷定對應主鍵的數據是否已經存在。 所以,Kudu是犧牲了寫性能來換取讀取性能的提高。 更新、刪除操做須要記錄到特殊的數據結構裏,保存在內存中的DeltaMemStore或磁盤上的DeltaFIle裏面。 DeltaMemStore是B-Tree實現的,所以速度快,並且可修改。磁盤上的DeltaFIle是二進制的列式的塊,和base數據同樣都是不可修改的。 所以當數據頻繁刪改的時候,磁盤上會有大量的DeltaFiles文件,Kudu借鑑了Hbase的方式,會按期對這些文件進行合併。
impala操做界面裏 能夠執行 invalidate metadata; 命令刷新元數據。 ==================== impala-shell ============================================ impala-shell 從Impala建立一個新的Kudu表
Kudu 1.9.0文檔:https://kudu.apache.org/releases/1.9.0/docs/ Kudu 1.9.0 Java API文檔:https://kudu.apache.org/releases/1.9.0/apidocs/ 1.Kudu介紹:Kudu集HDFS的順序讀和HBASE的隨機讀於一身,同時具有高性能的隨機寫,以及很強大的可用性(單行事務,一致性協議),支持Impala spark計算引擎。 2.何時使用kudu 大規模數據複雜的實時分析,例如大數據量的join。 數據有更新 查詢準實時 3.存儲 Kudu的存儲是不基於HDFS的,構建集羣時,kudu頗有可能和HDFS共同佔用物理磁盤或者雲磁盤,理想狀況是獨立空間。 4.表設計 10+G a tablet 10-100 tablets individual node 在配置32C,64G機器上,跑過1000個tablet狀況,能正常寫入,但在大量查詢狀況下,出現tablet time out,rpc滿了的狀況, 5.分區設計 hash range 6.參數 1、Kudu Tablet Server Maintenance Threads 參數:maintenance_manager_num_threads 解釋:Kudu後臺對數據進行維護操做,如寫入數據時的併發線程數,通常設置爲4,官網建議的是數據目錄的3倍 Kudu Tablet Server Maintenance Threads 這個參數決定了Kudu後臺對數據進行維護操做,如寫入數據時的併發線程數。併發數越大,吞吐量越高, 但對集羣計算能力的要求也越高。默認值爲1,表示Kudu會採用單線程操做;對於須要大量數據進行快速寫入/刪除的集羣,能夠設置更大的值。 該值能夠設置跟計算節點的數據磁盤數量和CPU核數有關,通常來講,建議設置爲4以獲取比較均衡的性能,最大不超過8。 2、Kudu Tablet Server Block Cache Capacity Tablet 參數:block_cache_capacity_mb 解釋:分配給Kudu Tablet Server塊緩存的最大內存量,建議是2-4G Kudu Tablet Server Block Cache Capacity Tablet的Block buffer cache,根據集羣內存配置和數據量規模設置。通常建議至少2GB~4GB。 3、Kudu Tablet Server Hard Memory Limit Kudu 參數:memory_limit_hard_bytes 解釋:Tablet Server能使用的最大內存量,有多大,設置多大,tablet Server在批量寫入數據時並不是實時寫入磁盤,而是先Cache在內存中, 在flush到磁盤。這個值設置太小時,會形成Kudu數據寫入性能顯著降低。對於寫入性能要求比較高的集羣,建議設置更大的值(通常是機器內存的百分之80) Kudu Tablet Server Hard Memory Limit Kudu的Tablet Server能使用的最大內存。Tablet Server在批量寫入數據時並不是實時寫入磁盤, 而是先Cache在內存中,在flush到磁盤。這個值設置太小時,會形成Kudu數據寫入性能顯著降低。對於寫入性能要求比較高的集羣,建議設置更大的值,好比32GB。 4.Maximum Process File Descriptors 這個參數決定了Kudu可以同時打開的操做系統文件數。不設置則使用系統的ulimits值,設置後會覆蓋系統的設置。 須要根據集羣的規模及併發處理能力,很是謹慎的設置這個值。 5.Default Number of Replicas 這個參數設置了每一個Tablet的默認複製因子,默認值爲3,表示每一個表的數據會在Kudu中存儲3份副本。 咱們能夠根據須要修改這個全局默認值,也能夠在建表語句中經過’kudu.num_tablet_replicas’屬性來設置每一個表的副本數, 好比:’kudu.num_tablet_replicas’ = ‘1’。 7.建議每一個表50columns左右,不能超過300個 8.hash分區數量 * range分區數量不能超過60個(1.7.0版本以後沒限制了) 9.設置block的管理器爲文件管理器(默認是日誌服務器) 解釋:並不是全部文件系統格式都須要設置該選項。ext四、xfs格式支持hole punching(打孔),因此不須要設置block_manager=file,可是ext3 格式須要。 能夠經過df -Th命令來查看文件系統的格式。 參數:--block_manager=file 10.設置ntp服務器的時間偏差不超過20s(默認是10s) 參數:max_clock_sync_error_usec=20000000 11.設置rpc的鏈接時長(默認是3s,建議不要設置) 參數:--rpc_negotiation_timeout_ms=300000 12.設置rpc一致性選擇的鏈接時長(默認爲1s,建議不要設置) 參數:--consensus_rpc_timeout_ms=1000 13.記錄kudu的crash的信息 解釋: Kudu在遇到崩潰時,使用Google Breakpad庫來生成minidump。這些minidumps的大小一般只有幾MB,即便禁用了核心轉儲生成,也會生成, 生成minidumps只能在Linux上創建。 minidump文件包含有關崩潰的進程的重要調試信息,包括加載的共享庫及其版本,崩潰時運行的線程列表,處理器寄存器的狀態和每一個線程的堆棧內存副本, 以及CPU和操做系統版本信息。 Minitump能夠經過電子郵件發送給Kudu開發人員或附加到JIRA,以幫助Kudu開發人員調試崩潰。爲了使其有用, 開發人員將須要知道Kudu的確切版本和發生崩潰的操做系統。請注意,雖然minidump不包含堆內存轉儲,但它確實包含堆棧內存, 所以能夠將應用程序數據顯示在minidump中。若是機密或我的信息存儲在羣集上,請不要共享minidump文件。 參數: --minidump_path=minidumps --max_minidumps=9 (默認是在設置的log目錄下生成minidumps目錄,裏邊包含最多9個以dmp結尾的文件,沒法設置爲空值,須要注意的是若是自定義minidump文件, 在master不能啓動的狀況下,須要將該目錄中的文件刪除) 14.Stack WatchLog 解釋:每一個Kudu服務器進程都有一個稱爲Stack Watchdog的後臺線程,它監視服務器中的其餘線程,以防它們被阻塞超過預期的時間段。 這些跟蹤能夠指示操做系統問題或瓶頸存儲。經過WARN日誌信息的跟蹤(Trace)能夠用於診斷因爲Kudu如下的系統(如磁盤控制器或文件系統)引發的根本緣由延遲問題。 15.cdh設置多master 參數:--master_addresses=cdh01:7051,cdh02:7051cdh03:7051 16.kudu出現啓動速度特別慢 解決辦法: 1、取消全部配置參數(除了資源、時間同步) 2、升級版本到kudu1.6.0 3、client必須中止(client不佔用io的狀況,3臺機器,每臺機器60G,127分區數量,啓動速度3分鐘) 4、查看io使用狀況 iostat -d -x -k 1 200 17.單hash分區最大是60 18.安裝kudu過程當中,會要求CPU支持ssc4.2指令集,可是咱們的虛擬機cpu沒有這個執行集,因此沒法安裝 19.設置client長鏈接過時時間 參數:--authn_token_validity_seconds=12960000(150天) 注意:設置到tserver的配置文件中 20.tserver和master的wal和data目錄要分隔(或者是目錄設置爲lvm卷軸) 緣由:wal目錄只能設置爲1個 參數:--fs_wal_dir_reserved_bytes 解釋: Number of bytes to reserve on the log directory filesystem for non-Kudu usage. The default, which is represented by -1, is that 1% of the disk space on each disk will be reserved. Any other value specified represents the number of bytes reserved and must be greater than or equal to 0. Explicit percentages to reserve are not currently supported 用於非kudu都使用的日誌目錄文件系統的字節數,默認狀況下是-1,每一個磁盤上的磁盤空間的1%將被保留,指定的任何其餘值表示保留的字節數,必須大於或等於0。 21.設置用戶權限,能移動tablet 參數:--superuser_acl=* 22.tserver宕掉後,5分鐘後沒有恢復的狀況下,該機器上的tablet會移動到其餘機器 參數:--follower_unavailable_considered_failed_sec=300 23.超過參數時間的歷史數據會被清理,若是是base數據不會被清理。而真實運行時數據大小持續累加,沒有被清理。 參數:--tablet_history_max_age_sec=900
從Impala在Kudu中建立新表相似於將現有Kudu表映射到Impala表,除了您須要本身指定模式和分區信息。 使用如下示例做爲指導。Impala首先建立表,而後建立映射。 CREATE TABLE my_first_table ( id BIGINT, name STRING, PRIMARY KEY(id) ) PARTITION BY HASH PARTITIONS 16 STORED AS KUDU; 在CREATE TABLE語句中,必須首先列出組成主鍵的列。此外,隱式標記主鍵列NOT NULL。 建立新的Kudu表時,您須要指定分發方案。請參閱分區表:https://kudu.apache.org/docs/kudu_impala_integration.html#partitioning_tables id爲簡單起見,上面的表建立示例經過散列列分佈到16個分區中。有關分區的指導,請參閱 分區規則:https://kudu.apache.org/docs/kudu_impala_integration.html#partitioning_rules_of_thumb 在Impala中查詢現有的Kudu表:Impala中建立映射Kudu表的外部映射表 經過Kudu API或其餘集成(如Apache Spark)建立的表在Impala中不會自動顯示。要查詢它們,必須首先在Impala中建立外部表,以將Kudu表映射到Impala數據庫: CREATE EXTERNAL TABLE `bigData` STORED AS KUDU TBLPROPERTIES( 'kudu.table_name' = 'bigData', 'kudu.master_addresses' = 'node1:7051,node2:7051,node3:7051') Kudu中的分區方法主要有兩種:partition by hash 和 partition by range kudu表基於其partition方法被拆分紅多個分區,每一個分區就是一個tablet,一張kudu表所屬的全部tablets均勻分佈並存儲在tablet servers的磁盤上。 所以在建立kudu表的時候須要聲明該表的partition方法,同時要指定primary key做爲partition的依據。 基於hash的分區方法的基本原理是: 基於primary key的hash值將每一個row(行)劃分到相應的tablet當中,分區的個數即tablet的個數必須在建立表語句中指定,建表語句示例以下: 注:若是未指定基於某個字段的hash值進行分區,默認以主鍵的hash值進行分區。 create table test ( name string, age int, primary key (name) ) partition by hash (name) partitions 8 stored as kudu; 基於range的分區方法的基本原理是: 基於指定主鍵的取值範圍將每一個row(行)劃分到相應的tablet當中,用於range分區的主鍵以及各個取值範圍都必須在建表語句中聲明,建表語句示例以下: 例子:有班級、姓名、年齡三個字段,表中的每一個row將會根據其所在的班級劃分紅四個分區,每一個分區就表明一個班級。 create table test ( classes int, name string, age int, primary key (classes,name) ) partition by range (classes) ( partition value = 1, partition value = 2, partition value = 3, partition value = 4 ) stored as kudu; kudu表還能夠採用基於hash和基於range相結合的分區方式,使用方法與上述相似 kudu表支持3種insert語句: 1.insert into test values(‘a’,12); 2.insert into test values(‘a’,12),(‘b’,13),(‘c’,14); 3.insert into test select * from other_table; update語句 kudu表的update操做不能更改主鍵的值,其餘與標準sql語法相同。 upsert 語句 對於 upsert into test values (‘a’,12) 若是指定的values中的主鍵值 在表中已經存在,則執行update語義,反之,執行insert語義。 delete語句 與標準sql語法相同。
impala + kudu一些優化心得 1.一開始須要全量導入kudu,這時候咱們先用sqoop把關係數據庫數據導入臨時表,再用impala從臨時表導入kudu目標表 因爲sqoop從關係型數據直接以parquet格式導入hive會有問題,這裏默認hive的表都是text格式;每次導完到臨時表, 須要作invalidate metadata 表操做,否則後面直接導入kudu的時候會查不到數據 2.除了查詢,建議全部impala操做都在impala-shell而不在hue上面執行 3.impala併發寫入kudu的時候,數據量比較大的時候 這時候kudu配置參數 --memory_limit_hard_bytes能大點就大點,由於kudu寫入首先保存再內存裏面,到必定閥值才溢寫到磁盤,這個是直接最能提升寫的方法; 固然不是全部機器都有那麼多資源,能夠把--maintenance_manager_num_threads 這個參數稍微調大,須要調試,提升數據從內存寫入磁盤的效率 4.impala查詢kudu 首先全部表作徹底量的etl操做,必須得執行compute stats 表名,否則impala執行sql生成的計劃執行數評估的內存不許確,容易評估錯誤致使實際執行不了 kudu表最好不要作任何壓縮,保證原始掃描性能發揮最好;假如對查詢性能要求比存儲要求高的話;大部分企業對實時查詢效率要求高,並且存儲成本畢竟低; kudu針對大表要作好分區,最好range和hash一塊兒使用,前提是主鍵列包含能hash的id,但range分區必定要作好,經驗告訴我通常是基於時間; 查詢慢的sql,通常要拿出來;方便的話作下explain,看下kudu有沒有過濾部分數據關鍵字kudu predicates;假如sql沒問題,那在impala-shell執行這個sql, 最後執行summray命令,重點查看單點峯值內存和時間比較大的點,對相關的表作優化,解決數據傾斜問題 5.kudu數據刪除 大表不要delete,不要猶豫直接drop,在create吧;磁盤空間會釋放的 6.關於impala + kudu 和 impala + parquet 網上不少分析impala + kudu 要比 impala + parquet 優越不少;誰信誰XB; 首先兩個解決的場景不同,kudu通常解決實時,hive解決的是離線(一般是T + 1或者 T -1) hive基於hdfs,hdfs已經提供一套較爲完善的存儲機制,底層數據和文件操做便利;安全性,可擴展性都比kudu強不少, 最重要parquet + impala效率要比kudu高,數倉首選是它 kudu最大優點是能作相似關係型數據庫同樣的操做,insert, update, delete,這樣熱點的數據能夠存儲在kudu裏面並隨時作更新 7.最後談到的實時同步工具 同步工具咱們這裏使用streamsets,一個拖拉拽的工具,很是好用;但內存使用率高,經過jconsole咱們發現,全部任務同時啓動; JVM新生代的內容幾乎都跑到老年代了,GC沒來的及,就內存溢出了;後面單獨拿幾臺服務器出來作這個ETL工具,jvm配置G1垃圾回收器
1.在 kudu 以前,大數據主要以兩種方式存儲: 第一種是靜態數據:以 HDFS 引擎做爲存儲引擎,適用於高吞吐量的離線大數據分析場景。 這類存儲的侷限性是數據沒法進行隨機的讀寫和批量的更新操做。 第二種是動態數據:以 HBase做爲存儲引擎,適用於大數據隨機讀寫場景。這類存儲的侷限性是批量讀取吞吐量遠不如 HDFS、不適用於批量數據分析的場景。 2.從上面分析可知,這兩種數據在存儲方式上徹底不一樣,進而致使使用場景徹底不一樣,但在真實的場景中,邊界可能沒有那麼清晰,面對既須要隨機讀寫, 又須要批量分析的大數據場景,該如何選擇呢? 3.這個場景中,單種存儲引擎沒法知足業務需求,咱們須要經過多種大數據組件組合來知足這一需求,一個常見的方案是: 數據實時寫入 HBase,實時的數據更新也在 HBase 完成,爲了應對 OLAP 需求,咱們定時(一般是 T+1 或者 T+H)將 HBase的 數據寫成靜態的文件(Parquet) 導入到 OLAP 引擎(HDFS)。這一架構能知足既須要隨機讀寫,又能夠支持 OLAP 分析的場景。 但他有以下缺點: 第一:架構複雜。從架構上看,數據在 HBase、消息隊列Kafka、HDFS 間流轉,涉及環節太多,運維成本很高。 而且每一個環節須要保證高可用、維護多個副本、存儲空間浪費。最後數據在多個系統上,對數據安全策略、監控等都提出了挑戰。 第二:時效性低。數據從 HBase 導出成靜態文件是週期性的,通常這個週期是一天(或一小時),在時效性上不是很高。 第三:難以應對後續的更新。真實場景中,總會有數據是延遲到達的。若是這些數據以前已經從 HBase 導出到 HDFS, 新到的變動數據就難以處理了,一個方案是把新變動的數據和原有數據進行對比,把不一樣的數據從新進行更新操做,這時候代價就很大了。 假如說,咱們想要sql實時對大量數據進行分析該怎麼辦?或者是我想讓數據存儲可以支持Upsert(更新插入操做),又該怎麼辦?因此這就是kudu的優點。 kudu 的定位是 Fast Analytics on Fast Data,是一個既支持隨機讀寫、又支持 OLAP 分析的大數據存儲引擎。 4.KUDU在 HDFS 和 HBase 這兩個中平衡了隨機讀寫和批量分析的性能,既支持了SQL實時查詢,也支持了數據更新插入操做。 完美的和impala集成,統一了hdfs數據源和kudu數據源,從而使得開發人員可以高效的進行數據分析。
5.hdfs不支持批量更新操做,kudu支持 hdfs適用於離線sql分析,kudu適用於實時sql分析 hbase不支持sql操做,kudu支持(hbase-hive表可支持sql操做,可是效率極低) hbase不支持結構化數據存儲,kudu支持 hbase開發語言使用的java,內存的釋放經過gc來完成,在內存比較緊張時可能引起full gc進而致使服務不穩定;kudu核心模塊用的c++來實現,沒有full gc的風險 hbase的timestamp是暴露的,kudu沒有暴露 hbase的插入和更新操做都是看成一條數據進行處理的,而kudu是分隔開的 6.適合於在線實時分析的應用 適合大數據量更新操做的應用 適合將mysql的數據同步到kudu,減輕備庫mysql查詢的壓力 適合存儲ADS數據,包含用戶標籤、各種指標數據等 適合於存儲結構化數據 適合於和Impala繼承,SQL分析數據 適合於和HDFS一塊兒使用,聚合數據源 實時預測模型的應用,支持根據全部歷史數據週期地更新模型 7.kudu完美的和impala集成,統一了hdfs數據源和kudu數據源,從而使得開發人員可以高效的進行數據分析。 impala-kudu 主要用於實時的分析海量數據,即海量的結構化數據不斷更新到kudu中,底層是以列式結構分佈式存儲,查詢是獲取結構化數據, 而後進行 OLAP 分析、數據挖掘、機器學習等分析型操做,這些分析型操做所涉及的數據延遲性很小。 可是kudu對硬件資源要求很高,特別是IO這塊,以前公司遇到的集羣瓶頸是多臺機器(寫30m/s)IO使用率達到100%,從而使用rpc鏈接超時,致使數據丟失。 impala-kudu 的應用適用於多個行業,凡是結構化數據分析的情景均可使用,從實時性方面來說,使用sql實時的查詢結構化數據,使得分析操做快速和高效。 從離線方面來說,能夠查詢hdfs的數據,從而保證了數據的統一化和多元化,而且有利於構建數據倉庫模型。
1.Kudu特色 特色一:主從架構 主爲master,從爲tserver,一般爲三主多從 特色二:高可用性(High availability) Tablet server 和 Master 使用 Raft Consensus Algorithm 來保證節點的高可用,確保只要有一半以上的副本可用, 該 tablet 即可用於讀寫。例如,若是3個副本中有2個可用 或 5個副本中的有3個可用,則該tablet可用。即便在 leader tablet 出現故障的狀況下, 讀取功能也能夠經過 read-only(只讀的)follower tablets來進行服務,或者是leader宕掉的狀況下,會根據raft機制從新選舉leader 特色三:水平可擴展 特色四:OLAP 工做的快速處理。 特色五:與 MapReduce,Spark ,Impala和其餘 Hadoop 生態系統組件集成 特色六:使用 Cloudera Manager 輕鬆維護和管理 特色七:底層存儲徹底是列式結構,每一列均可以自定義壓縮 特色八:查詢出來的數據是結構化模型,支持sql操做 2.Kudu概念和術語 1.開發語言 C++ 2.Columnar Data Store(列式數據存儲) 3.Read Efficiency(高效讀取) 對於分析查詢,容許讀取單個列或該列的一部分同時忽略其餘列 4.Data Compression(數據壓縮) 因爲給定的列只包含一種類型的數據,因此基於此模式的壓縮會比壓縮混合數據類型(在基於行的解決案中使用)時更有效幾個數量級。 結合從列讀取數據的效率,壓縮容許從磁盤讀取更少的塊時完成查詢
5.Table(表) 一張table是數據存儲在 Kudu 的位置。表具備schema(結構)和全局有序的primary key(主鍵)。table被分紅不少段,也就是稱爲tablets(Tablet的複數) 6.Tablet(段) 一個tablet 是 一張表table 連續的segment(段),與其它數據存儲引擎或關係型數據庫partition(分區)類似。 在必定的時間範圍內,tablet的副本冗餘到多個tserver服務器上,其中一個副本被認爲是leader tablet。 任何副本均可以對讀取進行服務,而且寫入時須要爲tablet服務的一組tablet server之間達成一致性。 一張表分紅多個tablet,分佈在不一樣的tablet server中,最大並行化操做,Tablet在Kudu中被切分爲更小的單元,叫作RowSets, RowSets分爲兩種MemRowSets和DiskRowSet,MemRowSets每生成32M,就溢寫到磁盤中,也就是DiskRowSet 7.Tablet Server tablet server是 存儲tablet 和 爲tablet向client提供服務。對於給定的tablet,一個tablet server充當 leader, 其餘tablet server充當該tablet的follower副本。只有leader爲每個服務提供寫請求,leader和followers爲每一個服務提供讀請求。 leader使用Raft協議來進行選舉 。一個tablet server能夠服務多個tablets,而且一個 tablet 能夠被多個tablet servers服務着。 8.Master 保持跟蹤全部的tablets、tablet servers、catalog tables(目錄表)和其它與集羣相關的metadata。在給定的時間點,只能有一個起做用的master(也就是 leader)。 若是當前的leader消失,則選舉出一個新的master,使用Raft協議來進行選舉。master還協調客戶端的metadata operations(元數據操做)。 例如,當建立新表時,客戶端內部將請求發送給master。 master將新表的元數據寫入catalog table(目錄表),並協調在tablet server上建立tablet的過程。 全部master的元數據都存儲在一個tablet中,能夠複製到全部其餘候選的master。tablet server以設定的間隔向master發出心跳(默認值爲每秒一次)。 master是以文件的形式存儲在磁盤中。 9.Raft Consensus Algorithm Kudu 使用 Raft consensus algorithm 做爲確保常規 tablet 和 master 數據的容錯性和一致性的手段。 經過 Raft協議,tablet 的多個副本選舉出 leader,它負責接受請求和複製數據寫入到其餘follower副本。 一旦寫入的數據在大多數副本中持久化後,就會向客戶確認。給定的一組N副本(一般爲 3 或 5 個)可以接受最多(N - 1)/2 錯誤的副本的寫入。 10.Catalog Table(目錄表) catalog table是Kudu 的 metadata(元數據中)的中心位置。它存儲有關tables和tablets的信息。 該catalog table(目錄表)可能不會被直接讀取或寫入。相反,它只能經過客戶端 API中公開的元數據操做訪問。 catalog tables存儲如下兩類元數據。 Tables:table schemas 表結構,locations 位置,states 狀態 Tablets:現有tablet 的列表,每一個 tablet 的副本所在哪些tablet server,tablet的當前狀態以及開始和結束的keys(鍵)。 3.Kudu-Impala 集成特性 CREATE/ALTER/DROP TABLE Impala 支持使用 Kudu 做爲持久層來 creating(建立),altering(修改)和 dropping(刪除)表。 這些表遵循與 Impala 中其餘表格相同的 Internal / external(內部 / 外部)方法,容許靈活的數據採集和查詢。 INSERT 數據可使用「與那些使用 HDFS 或 HBase 持久性的任何其餘 Impala 表相同的」語法插入 Impala 中的 Kudu 表。 UPDATE / DELETE Impala 支持 UPDATE 和 DELETE SQL 命令逐行或批處理修改 Kudu 表中的已有的數據。 選擇 SQL 命令的語法與現有標準儘量兼容。除了簡單 DELETE 或 UPDATE 命令以外,還能夠 FROM 在子查詢中指定帶有子句的複雜鏈接。 Flexible Partitioning(靈活分區) 與 Hive 中的表分區相似,Kudu 容許您經過 hash 或 range 動態預分割成預約義數量的 tablets,以便在集羣中均勻分佈寫入和查詢。 您能夠經過任意數量的 primary key(主鍵)列,任意數量的 hashes 和可選的 list of split rows 來進行分區。 Parallel Scan(並行掃描) 爲了在現代硬件上實現最高的性能,Impala 使用的 Kudu 客戶端能夠跨多個 tablets掃描。 High-efficiency queries(高效查詢) 在可能的狀況下,Impala 將謂詞評估下推到 Kudu,以便使謂詞(in,between and,>,<)評估爲儘量接近數據。在許多任務中,查詢性能與 Parquet 至關。 ---------------------
1.底層存儲原理 1.1個Table(表)包含多個Tablet,其中Tablet的數量是根據hash或者是range進行設置的。 2.1個Tablet中包含MetaData信息和多個RowSet信息,其中MetaData信息是block和block在data中的位置。 3.1個RowSet包含一個MemRowSet和多個DiskRowSet,其中MemRowSet用於存儲insert數據和update後的數據,寫滿後會刷新到磁盤中也就是多個DiskRowSet中, 默認是1G刷新一次或者是2分鐘。 4.1個DiskRowSet用於老數據的mutation(更新),好比說數據的更新操做,後臺按期對DiskRowSet進行合併操做,刪除歷史數據和沒有的數據,減小查詢過程當中的IO開銷。 5.1個DiskRowSet包含BloomFilter、Ad_hoc Index、UndoFile、RedoFile、BaseData、DeltaMem。 BloomFile:根據DiskRowSet中key生成一個bloom filter,用於快速模糊的定位某一個key是否在DiskRowSet中。 Ad_hoc Index:是主鍵的索引,用於定位key在DiskRowSet中具體哪一個偏移位置。 BaseData:是MemRowSet flush下來的數據,按照列存儲,按照主鍵有序。 UndoFile:是BaseData以前的數據歷史數據。 RedoFile:是BaseData以後的mutation(更新)記錄,能夠得到較新的數據。 DeltaMem:用於在內存中存儲mutation(更新)記錄,先寫到內存中,而後寫滿後flush到磁盤,造成deltafile。 ---------------------
2.MemRowSet 實現方式:B+Tree 3.DiskRowSet 1.實現方式:二叉平衡樹 2.內部數據組織:DeltaMem 和 MemRowSet在存在中的組織方式是一致的,都是B+Tree,在磁盤上的存儲都是放在CFile文件中的,右圖爲CFile的文件格式 3.Cfile:包含Header,Data,Index,Footer四塊,Index有兩種,posidx index是根據rowId找到Data中的偏移,validx index是根據key的值找到data中的偏移, validx只針對只有一個column爲key的狀況下,這個時候DiskRowSet是沒有Ad_hoc索引的,使用validx來代替。 這兩個index內部實現了B-Tree,index不必定是聯繫的,在達到必定長度後就會刷盤,Footer是記錄CFile的元數據, 包括posidx_index,validx_index兩棵樹根節點所在位置,數據條目、編碼、壓縮方式等 4.壓縮:對於ad_hoc文件使用的prefix,delta fle使用的是plain,bloomfile使用的是plain 5.磁盤上每個DiskRowSet有若*.metadata和*.data文件,metadata文件記錄的是DiskRowSet的元信息,主要包括哪些block和block在data中的位置, 左圖爲block和DiskRowSet中各部分的映射關係,在寫磁盤時是經過container來寫入,每一個container能夠寫很大的一塊連續的磁盤空間, 用於給某一個CFile寫數據,當一個CFile寫完後會將container歸還給BlockManager,這時container就能夠用於下一個CFile寫數據了, 當BlockManager中沒有container可用是會建立一個新的container給新的CFile使用。 6.對應新建block先看看是否有container可用,若沒有,目前默認的是在所在的配置中的data_dir中隨機選取一個dir建一個新的metadata和data文件。 先寫data,block落盤後再寫metadata
4.MVCC 表的主鍵排序,受益於MVCC(Multi-Version Concurrency Control 多版本併發控制,一旦數據寫入到MemRowSet,後續的reader能立馬查詢到 5.Compaction minor compaction:多個deltafile進行合併。默認是1000個deltafile進行合併一次 major compaction:deltafile文件的大小和basedata的文件的比例爲0.1的時候,會進行合併操做
1.kudu中的Tablet是負責Table表的一部分的讀寫工做,Tablet是有多個或一個Rowset組成的,其中一個Rowset處於內存中,叫作MemRowSet,MemRowSet主要是負責處理新的數據寫入請求。 2.DiskRowSet是MemRowSet達到1G刷新一次或者是時間超過2分鐘後刷新到磁盤後生成的,實際底層存儲是是有Base Data(一個CFile文件)、 多個Delta file(Undo data、Redo data組成)的和Delta MemStore,其中位於磁盤中的Base data、Undo data、Redo data是不可修改的, Delta Memstore達到必定程度後會刷新到磁盤中的生成Redo data,其中kudu後臺有一個相似HBase的compaction線程策略進行合併處理: 1、Minor Compaction:多個DeltaFile進行合併生成一個大的DeltaFile。默認是1000個DeltaFile進行合併一次 2、Major Compaction:DeltaFile文件的大小和Base data的文件的比例爲0.1的時候,會進行合併操做,生成Undo data 3、將多個DiskRowSet進行合併,減小DiskRowSet的數量 Base Data:是MemRowSet flush下來的數據,按照列存儲,按照主鍵有序 Undo Data:是BaseData以前的數據歷史數據 Redo Data:是BaseData以後的mutation記錄,能夠得到較新的數據 Delta Memstore:用於在內存中存儲更新爲磁盤中數據的mutation記錄,先寫到內存中,而後寫滿後flush到磁盤,造成DeltaFile 3.當建立Kudu客戶端時,其會從主master上獲取tablet位置信息,而後直接與服務於該tablet的服務器進行交談。 爲了優化讀取和寫入路徑,客戶端將保留該信息的本地緩存,以防止他們在每一個請求時須要查詢主機的tablet位置信息。 隨着時間的推移,客戶端的緩存可能會變得過期,而且當寫入被髮送到不是領導者的tablet服務器時,則將被拒絕。 而後,客戶端將經過查詢主服務器發現新領導者的位置來更新其緩存。 4.讀流程 1.客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息 2.客戶端找到須要讀取的數據的tablet所在的TServer,Kudu接受讀請求,並記錄timestamp信息,若是沒有顯式指定,那麼表示使用當前時間 3.從內存中讀取數據,也就是MemRowSet和DeltaRowSet中讀取數據,根據timestamp來找到對應的mutation鏈表 4.從磁盤中讀取數據,從metadata文件中使用boom filter快速模糊的判斷全部候選RowSet是否含有此key。 而後從DiskRowSet中讀取數據,實際是根據B+樹,判斷key在那些DiskRowSet的range範圍內,而後從metadata文件中,獲取index來判斷rowId在Data中的偏移, 或者是利用validex來判斷數據的偏移(只有一個key),根據讀操做中包含的timestamp信息判斷是否須要將base data進行回滾操做從而獲取數據 5.寫流程 1.Kudu插入一條新數據 1.客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息 2.客戶端找到負責處理讀寫請求的tablet所負責維護的TServer。Kudu接受客戶端的請求,檢查請求是否符合要求(表結構) 3.Kudu在Tablet中的全部rowset(memrowset,diskrowset)中進行查找,看是否存在與待插入數據相同主鍵的數據,若是存在就返回錯誤,不然繼續 4.寫入操做先被提交到tablet的預寫日誌(WAL),並根據Raft一致性算法取得追隨節點的贊成,而後纔會被添加到其中一個tablet的內存中, 插入會被添加到tablet的MemRowSet中。爲了在MemRowSet中支持多版本併發控制(MVCC),對最近插入的行(即還沒有刷新到磁盤的新的行)的更新和刪除操做 將被追加到MemRowSet中的原始行以後以生成REDO記錄的列表 5.Kudu在MemRowset中寫入一行新數據,在MemRowset(1G或者是120s)數據達到必定大小時,MemRowset將數據落盤,並生成一個diskrowset用於持久化數據, 還生成一個memrowset繼續接收新數據的請求 2.Kudu對原有數據的更新 1.客戶端鏈接TMaster獲取表的相關信息,包括分區信息,表中全部tablet的信息 2.Kudu接受請求,檢查請求是否符合要求 3.由於待更新數數據可能位於memrowset中,也可能已經flush到磁盤上,造成diskrowset。因 此根據待更新數據所處位置不一樣,kudu有不一樣的作法 4.當待更新數據位於memrowset時,找到待更新數據所在行,而後將更新操做記錄在所在行中一個mutation鏈表中; 在memrowset將數據落盤時,Kudu會將更新合併到base data,並生成UNDO records用於查看歷史版本的數據,REDO records實際上也是以DeltaFile的形式存放 6.應用場景 1.當待更新數據位於DiskRowset時,找到待更新數據所在的DiskRowset,每一個DiskRowset都會在內存中設置一個DeltaMemStore,將更新操做記錄在DeltaMemStore中, 在DeltaMemStore達到必定大小時,flush在磁盤,造成DeltaFile中。 2.實際上Kudu提交更新時會使用Raft協議將更新同步到其餘replica(複製品)上去,固然若是在memrowset和DiskRowset中都沒有找到這條數據,那麼返回錯誤給客戶端; 另外當DiskRowset中的deltafile太多時,Kudu會採用必定的策略對一組deltafile進行合併。 3.wal日誌的做用是若是咱們在作真正的操做以前,先將這件事記錄下來,持久化到可靠存儲中(由於日誌通常很小,而且是順序寫,效率很高), 而後再去執行真正的操做。這樣執行真正操做的時候也就不須要等待執行結果flush到磁盤再執行下一步,由於不管在哪一步出錯,咱們都可以根據備忘錄重作一遍, 獲得正確的結果。
1.架構 1.hbase的物理模型是master和regionserver,regionserver存儲的是region,region裏邊頗有不少store,一個store對應一個列簇, 一個store中有一個memstore和多個storefile,store的底層是hfile,hfile是hadoop的二進制文件,其中HFile和HLog是hbase兩大文件存儲格式, HFile用於存儲數據,HLog保證能夠數據寫入到HFile中。 2.kudu的物理模型是master和tserver,其中table根據hash和range分區,分爲多個tablet存儲到tserver中,tablet分爲leader和follower, leader負責寫請求,follower負責讀請求,總結來講,一個ts能夠服務多個tablet,一個tablet能夠被多個ts服務。 2.聯繫 設計理念和想法是一致的。 kudu的思想是基於hbase的,以前cloudera公司向對hbase改造,支持大數據量更新,但是因爲改動源碼太大,因此todd直接開發了kudu。 hbase基於rowkey查詢和kudu基於主鍵查詢是很快的。 底層存儲架構都是以列式存儲的。 ---------------------
1.hbase的物理模型是master和regionserver,regionserver存儲的是region,region裏邊頗有不少store,一個store對應一個列簇, 一個store中有一個memstore和多個storefile,store的底層是hfile,hfile是hadoop的二進制文件,其中HFile和HLog是hbase兩大文件存儲格式, HFile用於存儲數據,HLog保證能夠寫入到HFile中; 2.kudu的物理模型是master和tserver,其中table根據hash和range分區,分爲多個tablet存儲到tserver中,tablet分爲leader和follower, leader負責寫請求,follower負責讀請求,總結來講,一個ts能夠服務多個tablet,一個tablet能夠被多個ts服務(基於tablet的分區,最低爲2個分區); 3.hbase基於rowkey查詢和kudu基於主鍵查詢是很快的; 4.Kudu結構看上去跟HBase差異並不大,主要的區別包括: 1.Kudu將HBase中zookeeper的功能放進了TMaster內,Kudu中TMaster的功能比HBase中的Master任務要多一些,kudu全部集羣的配置信息均存儲在本地磁盤中, hbase的集羣配置信息是存儲在zookeeper中; 2.Hbase將數據持久化這部分的功能交給了Hadoop中的HDFS,最終組織的數據存儲在HDFS上。Kudu本身將存儲模塊集成在本身的結構中, 內部的數據存儲模塊經過Raft協議來保證leader Tablet和replica Tablet內數據的強一致性,和數據的高可靠性。 爲何不像HBase同樣利用HDFS來實現數據存儲,因此Kudu本身從新完成了底層的數據存儲模塊,並將其集成在TServer中, 可是kudu對磁盤的IO要求很高,它是以寫的性能換取讀的性能; ---------------------
5.數據存儲方式 1.HBase是面向列族式的存儲,每一個列族都是分別存放的,HBase表設計時,不多使用設計多個列族,大多狀況下是一個列族。 這個時候的HBase的存儲結構已經與行式存儲無太大差異了。而Kudu,實現的是一個真正的面向列的存儲方式,表中的每一列都是單獨存放的; 因此HBase與Kudu的差別主要在於相似於行式存儲的列族式存儲方式與典型的面向列式的存儲方式的差別; 2.HBase是一款NoSQL類型的數據庫,對錶的設計主要在於rowkey與列族的設計,列的類型能夠不指定,由於HBase在實際存儲中都會將全部的value字段轉換成二進制的字節流。 由於不須要指定類型,因此在插入數據的時候能夠任意指定列名(列限定名),這樣至關於能夠在建表以後動態改變表的結構。 Kudu由於選擇了列式存儲,爲了更好的提升列式存儲的效果,Kudu要求在建表時指定每一列的類型,這樣的作法是爲了根據每一列的類型設置合適的編碼方式, 實現更高的數據壓縮比,進而下降數據讀入時的IO壓力; 3.HBase對每個cell數據中加入了timestamp字段,這樣可以實現記錄同一rowkey和列名的多版本數據,另外HBase將數據更新操做、刪除操做也是做爲一條數據寫入, 經過timestamp來標記更新時間,type來區分數據是插入、更新仍是刪除。HBase寫入或者更新數據時能夠指定timestamp,這樣的設置能夠完成某些特定的操做; Kudu也在數據存儲中加入了timestamp這個字段,不像HBase能夠直接在插入或者更新數據時設置特殊的timestamp值,Kudu的作法是由Kudu內部來控制timestamp的寫入。 不過Kudu容許在scan的時候設置timestamp參數,使得客戶端能夠scan到歷史數據; 4.相對於HBase容許多版本的數據存在,Kudu爲了提升批量讀取數據時的效率,要求設計表時提供一列或者多列組成一個主鍵,主鍵惟一, 不容許多個相同主鍵的數據存在。這樣的設置下,Kudu不能像HBase同樣將更新操做直接轉換成插入一條新版本的數據,Kudu的選擇是將寫入的數據,更新操做分開存儲; 固然還有一些其餘的行式存儲與列式存儲之間在不一樣應用場景下的性能差別。 5.hbase中,同一個主鍵數據是能夠存在多個storefile裏的,爲了讓mutation和磁盤的存在的key組合在一塊兒,hbase須要基於rowkey執行merge。 Rowkey能夠是任意長度的字符串,所以對比rowkey是很是耗性能的。另外,在一個查詢中,即便key列沒有被使用(例如聚合計算),它們也要被讀取出來, 這致使了額外的IO。複合主鍵在hbase應用中很常見,主鍵的大小可能比你關注的列大一個數量級,特別是查詢的列被壓縮的狀況下; 6.kudu中,讀取一條數據或者執行非排序查詢,不須要merge操做。例如,聚合必定範圍內的key能夠獨立的查詢每一個RowSet(甚至能夠並行的), 而後執行求和,由於key的順序是不重要的,顯然查詢的效率更高,kudu中,mutation是與rowid綁定的。因此merge會更加高效,經過維護計數器的方式, 給定下一個須要保存的mutation,咱們能夠簡單的相減,就能夠獲得從base data到當前版本有多少個mutation。 或者,直接尋址能夠用來高效的獲取最新版本的數據。獲取block也很是的高效,由於mutation直接指向了block的索引地址; 7.hbase的系統中,每一個cell的timstamp都是暴露給用戶的,本質上組成了這個cell的一個符合主鍵。意味着,這種方式能夠高效的直接訪問指定版本的cell, 且它存儲了一個cell的整個時間序列的全部版本; 而Kudu卻不高效(須要執行多個mutation),它的timestamp是從MVCC實現而來的,它不是主鍵的另一個描述; 8.hbase採用的LSM(LogStructured Merge,很難對數據進行特殊編碼,因此處理效率不高),hbase會將多條更新記錄前後Flush到不一樣的Storefile中, 因此讀取時須要掃描多個文件,比較rowkey,比較版本等,而後進行更新操做,特別是major compaction操做的時候,會佔用大量的性能; 9.Kudu對同一行的數據更新記錄的合併工做,不是在查詢的時候發生的,而是在更新的時候進行,在Kudu中一行數據只會存在於一個DiskRowSet中, 避免讀操做時的比較合併工做。對於列式存儲的數據文件,要原地變動一行數據是很困難的,因此在Kudu中,對於Flush到磁盤上的DiskRowSet(DRS)數據, 其實是分兩種形式存在的,一種是Base的數據,按列式存儲格式存在,一旦生成,就再也不修改,另外一種是Delta文件,存儲Base數據中有變動的數據, 一個Base文件能夠對應多個Delta文件(Kudu用MVCC(多版本併發控制)來實現數據的刪改功能。更新、刪除操做須要記錄到特殊的數據結構裏, 保存在內存中的DeltaMemStore或磁盤上的DeltaFIle裏面。DeltaMemStore是B-Tree實現的,所以速度快,並且可修改。 磁盤上的DeltaFIle是二進制的列式的塊,和base數據同樣都是不可修改的。所以當數據頻繁刪改的時候,磁盤上會有大量的DeltaFiles文件, Kudu借鑑了Hbase的方式,會按期對這些文件進行合併),這種方式意味着,插入數據時相比HBase,須要額外走一次檢索流程來斷定對應主鍵的數據是否已經存在。 所以,Kudu是犧牲了寫性能來換取讀取性能的提高。另外,若是在查詢中沒有指定key,那執行計劃就不會查閱key,除了須要肯定key邊界狀況; 10.hbase中insert和mutation是相同的操做,直接存儲到storefile中。 kudu中insert和mutation是不一樣的操做:insert寫入數據至MemRowSet,而mutation(delete、update)寫入存在這條數據的RowSet的DeltaMemStore裏, 寫入時必須肯定這是一條新數據。這會產生一個bloom filter查詢全部RowSet。若是布隆過濾器獲得一個可能的match(即計算出可能在一個RowSet裏), 接着爲了肯定是不是insert仍是update,一個尋址就必須被執行。 假設,只要RowSet足夠小,bloom filter的結果就會足夠精確, 那麼大部分插入將不須要物理磁盤尋址。另外,若是插入的key是有序的,例如timeseries+「_」+xxx,因爲頻繁使用,key所在的block可能會被保存在數據塊緩存中。 Update時,須要肯定key在哪一個RowSet。與上雷同,須要執行bloom filter。 這有點相似於關係型數據庫RDBMS,當插入一條主鍵存在的數據時會報錯, 且不會更新這條數據。相似的,更新一條數據時,若是這條數據不存在也會報錯。hbase的語法卻不是這樣,它不存在主鍵的概念; 6.寫入和讀取過程 1.寫過程 HBase寫的時候,無論是新插入一條數據仍是更新數據,都看成插入一條新數據來進行;而Kudu將插入新數據與更新操做分別看待; Kudu表結構中必須設置一個惟一鍵,插入數據的時候必須判斷一些該數據的主鍵是否惟一,因此插入的時候其實有一個讀的過程; 而HBase沒有太多限制,待插入數據將直接寫進memstore; HBase實現數據可靠性是經過將落盤的數據寫入HDFS來實現,而Kudu是經過將數據寫入和更新操做同步在其餘副本上實現數據可靠性; 結合以上幾點,能夠看出Kudu在寫的性能上相對HBase有必定的劣勢; 2.讀過程 在HBase中,讀取的數據可能有多個版本,因此須要結合多個storefile進行查詢;Kudu數據只可能存在於一個DiskRowset或者MemRowset中, 可是由於可能存在還未合併進原數據的更新,因此Kudu也須要結合多個DeltaFile進行查詢; HBase寫入或者更新時能夠指定timestamp,致使storefile之間timestamp範圍的規律性下降,增長了實際查詢storefile的數量; Kudu不容許人爲指定寫入或者更新時的timestamp值,DeltaFile之間timestamp連續,能夠更快的找到須要的DeltaFile; HBase經過timestamp值能夠直接取出數據;而Kudu實現多版本是經過保留UNDO records(已經合併過的操做)和REDO records(未合併過的操做)完成的, 在一些狀況下Kudu須要將base data結合UNDO records進行回滾或者結合REDO records進行合併而後才能獲得真正所須要的數據; 結合以上三點能夠得出,無論是HBase仍是Kudu,在讀取一條數據時都須要從多個文件中搜尋相關信息。相對於HBase,Kudu選擇將插入數據和更新操做分開, 一條數據只可能存在於一個DiskRowset或者memRowset中,只須要搜尋到一個rowset中存在指定數據就不用繼續往下找了,用戶不能設置更新和插入時的timestamp值, 減小了在rowset中DeltaFile的讀取數量。這樣在scan的狀況下能夠結合列式存儲的優勢實現較高的讀性能,特別是在更新數量較少的狀況下可以有效提升scan性能; 另外,本文在描述HBase讀寫過程當中沒有考慮讀寫中使用的優化技術如Bloomfilter、timestamp range等。其實Kudu中也有使用相似的優化技術來提升讀寫性能, 本文只是簡單的分析,所以就再也不詳細討論讀寫過程; 7.其餘差別 HBase:使用的java,內存的釋放經過GC來完成,在內存比較緊張時可能引起full GC進而致使服務不穩定; Kudu:核心模塊用的C++來實現,沒有full gc的風險; 8.總結 Kudu經過要求完整的表結構設置,主鍵的設定,以列式存儲做爲數據在磁盤上的組織方式,更新和數據分開等技巧, 使得Kudu可以實現像HBase同樣實現數據的隨機讀寫以外,在HBase不太擅長的批量數據掃描(scan)具備較好的性能。 而批量讀數據正是olap型應用所關注的重點,正如Kudu官網主頁上描述的,Kudu實現的是既能夠實現數據的快速插入與實時更新, 也能夠實現數據的快速分析。Kudu的定位不是取代HBase,而是以下降寫的性能爲代價,提升了批量讀的性能,使其可以實現快速在線分析。
報錯一:tablet初始化時長好久 解決方案: 升級版本到kudu1.6.0以上版本 .參考:https://kudu.apache.org/2017/12/08/apache-kudu-1-6-0-released.html 查看io使用狀況 iostat -d -x -k 1 200.(多是IO瓶頸) Recommended maximum number of tablet servers is 100. Recommended maximum number of tablets per tablet server is 2000.
報錯二:rpc鏈接超時(IO問題) RPC can not complete before timeout: KuduRpc(method=CreateTable, tablet=null, attempt=26, DeadlineTracker(timeout=30000, elapsed=29427) 解決方案:session.setTimeoutMillis(60000) 報錯三:移動tablet,權限不能訪問 解決方案:--superuser_acl=*
報錯四:新增master找不到元數據 解決方案: 由於master的存儲所有在本地磁盤文件,若是額外的添加了一個master,會報錯,找不到consensus-meta,也就是master的容錯機制,須要對master的元數據數據格式化, 初始化的時候直接設計好。 Recommended maximum number of masters is 3. ---------------------
報錯五:minidumps文件(存儲crash信息)出錯 [New I/O worker #1] WARN org.apache.kudu.client.GetMasterRegistrationReceived - None of the provided masters (hadoop6:7051) is a leader, will retry. 解決方案: rm -rf /home/var/lib/kudu/master/log_dir/minidumps 補充: minidump文件包含有關崩潰的進程的重要調試信息,包括加載的共享庫及其版本,崩潰時運行的線程列表,處理器寄存器的狀態和每一個線程的堆棧內存副本, 以及CPU和操做系統版本信息。Minitump能夠經過電子郵件發送給Kudu開發人員或附加到JIRA,以幫助Kudu開發人員調試崩潰。 報錯六:impala操做kudu超時 解決方案:kudu_operation_timeout_ms = 1800000
報錯七:CDH安裝kudu設置master 解決方案: --master_addresses=hadoop4:7051,hadoop5:7051,hadoop6:7051
1.Kudu Tablet Server Maintenance Threads 解釋:Kudu後臺對數據進行維護操做,如flush、compaction、inserts、updates、and deletes,通常設置爲4,官網建議的是數據目錄的3倍 參數:maintenance_manager_num_threads 2.Kudu Tablet Server Block Cache Capacity Tablet 解釋:分配給Kudu Tablet Server塊緩存的最大內存量,建議是2-4G 參數:block_cache_capacity_mb 3.數據插入都kudu中,使用manual_flush策略 4.設置ntp服務器的時間偏差不超過20s(默認是10s) 參數:max_clock_sync_error_usec=20000000 5.Kudu Tablet Server Hard Memory Limit Kudu 解釋:寫性能,Tablet Server能使用的最大內存量,建議是機器總內存的百分之80,master的內存量建議是2G,Tablet Server在批量寫入數據時並不是實時寫入磁盤, 而是先Cache在內存中,在flush到磁盤。這個值設置太小時,會形成Kudu數據寫入性能顯著降低。對於寫入性能要求比較高的集羣,建議設置更大的值 參數:memory_limit_hard_bytes 6.建議每一個表50columns左右,不能超過300個 7.kudu的wal只支持單目錄,若是快達到極限了,就會初始化tablte失敗。因此說在部署集羣的時候要單獨給wal設置一個單獨的目錄。 8.impala中建立表,底層使用kudu存儲(Impala::TableName),經過kudu的client端讀取數據,讀取不出來。 9.kudu表若是不新建的狀況下,在表中增長字段,對數據是沒有影響的,kudu中增長一個字段user_id,以前impala已經和kudu進行關聯操做了, impala讀取kudu的數據按照以前的所定義的字段讀取的。 10.設置client長鏈接過時時間,默認是7天(實際生產環境中設置的是180天) --authn_token_validity_seconds=604800 注意:設置到tserver的配置文件中 11.tserver宕掉後,5分鐘後沒有恢復的狀況下,該機器上的tablet會移動到其餘機器,由於咱們一般設置的是3個副本,其中一個副本宕掉,也就是一臺機器的tserver出現故障, 實際狀況下,還存在一個leader和follower,讀寫仍是可以正常進行的,因此說這個參數很重要,保證數據不會轉移。 --follower_unavailable_considered_failed_sec=300 12.超過參數時間的歷史數據會被清理,若是是base數據不會被清理。而真實運行時數據大小持續累加,沒有被清理,默認是900s。 --tablet_history_max_age_sec=900
package src.main.sample; import com.google.common.collect.ImmutableList; import org.apache.kudu.ColumnSchema; import org.apache.kudu.Schema; import org.apache.kudu.Type; import org.apache.kudu.client.*; import java.util.ArrayList; import java.util.List; public class CreateTable { public static void main(String[] args) { String tableName = "bigData"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.241.128,192.168.241.129,192.168.241.130").defaultAdminOperationTimeoutMs(60000).build(); KuduSession session = client.newSession(); // 此處所定義的是rpc鏈接超時 session.setTimeoutMillis(60000); try { // 測試,若是table存在的狀況下,就刪除該表 if(client.tableExists(tableName)) { client.deleteTable(tableName); System.out.println("delete the table!"); } List<ColumnSchema> columns = new ArrayList(); // 建立列 columns.add(new ColumnSchema.ColumnSchemaBuilder("id", Type.INT64).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("user_id", Type.INT64).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("start_time", Type.INT64).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("name", Type.STRING).nullable(true).build()); // 建立schema Schema schema = new Schema(columns); /* 建立 hash分區 + range分區 二者同時使用 的表 addHashPartitions(ImmutableList.of("字段名1","字段名2",...), hash分區數量) 默認使用主鍵,也可另外指定聯合主鍵 setRangePartitionColumns(ImmutableList.of("字段名")) */ // id,user_id至關於聯合主鍵,三個條件都知足的狀況下,才能夠更新數據,不然就是插入數據 ImmutableList<String> hashKeys = ImmutableList.of("id","user_id"); CreateTableOptions tableOptions = new CreateTableOptions(); /* 建立 hash分區 + range分區 二者同時使用 的表 addHashPartitions(ImmutableList.of("字段名1","字段名2",...), hash分區數量) 默認使用主鍵,也可另外指定聯合主鍵 setRangePartitionColumns(ImmutableList.of("字段名")) */ // 設置hash分區,包括分區數量、副本數目 tableOptions.addHashPartitions(hashKeys,3); //hash分區數量 tableOptions.setNumReplicas(3); //副本數目 // 設置range分區 tableOptions.setRangePartitionColumns(ImmutableList.of("start_time")); // 設置range分區數量 // 規則:range範圍爲時間戳是1-10,10-20,20-30,30-40,40-50 int count = 0; for(long i = 1 ; i <6 ; i++) { PartialRow lower = schema.newPartialRow(); lower.addLong("start_time",count); PartialRow upper = schema.newPartialRow(); count += 10; upper.addLong("start_time", count); tableOptions.addRangePartition(lower, upper); } System.out.println("create table is success!"); // 建立table,並設置partition client.createTable(tableName, schema, tableOptions); } catch (Exception e) { e.printStackTrace(); } finally { try { // client.deleteTable(tableName); } catch (Exception e) { e.printStackTrace(); } finally { try { client.shutdown(); } catch (Exception e) { e.printStackTrace(); } } } } } ---------------------
package src.main.sample; import org.apache.kudu.Type; import org.apache.kudu.client.AlterTableOptions; import org.apache.kudu.client.KuduClient; public class AlterTable { public static void main(String[] args) { String tableName = "bigData"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.161.128,192.168.161.129,192.168.161.130").defaultAdminOperationTimeoutMs(60000).build(); try { Object o = 0L; // 建立非空的列 client.alterTable(tableName, new AlterTableOptions().addColumn("device_id", Type.INT64, o)); // 建立列爲空 client.alterTable(tableName, new AlterTableOptions().addNullableColumn("site_id", Type.INT64)); // 刪除字段 // client.alterTable(tableName, new AlterTableOptions().dropColumn("site_id")); } catch (Exception e) { e.printStackTrace(); } } }
package src.main.sample; import org.apache.kudu.client.*; public class InsertData { public static void main(String[] args) { try { String tableName = "bigData"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.241.128,192.168.241.129,192.168.241.130").defaultAdminOperationTimeoutMs(60000).build(); // 獲取table KuduTable table = client.openTable(tableName); // 獲取一個會話 KuduSession session = client.newSession(); session.setTimeoutMillis(60000); /** * mode形式: * SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND 後臺自動一次性批處理刷新提交N條數據 * SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC 每次自動同步刷新提交每條數據 * SessionConfiguration.FlushMode.MANUAL_FLUSH 手動刷新一次性提交N條數據 */ session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH); //mode形式 session.setMutationBufferSpace(10000);// 緩衝大小,也就是數據的條數 // 插入時,初始時間 long startTime = System.currentTimeMillis(); int val = 0; // 插入數據 for (int i = 0; i < 60; i++) { Insert insert = table.newInsert(); PartialRow row = insert.getRow(); // row.addString("字段名", 字段值)、row.addLong(第幾列, 字段值) row.addLong(0, i); //指第一個字段 "id"(hash分區的聯合主鍵之一) row.addLong(1, i*100);//指第二個字段 "user_id"(hash分區的聯合主鍵之一) row.addLong(2, i);//指第三個字段 "start_time"(range分區字段) row.addString(3, "bigData");//指第四個字段 "name" session.apply(insert); if (val % 10 == 0) { session.flush(); //手動刷新提交 val = 0; } val++; } session.flush(); //手動刷新提交 // 插入時結束時間 long endTime = System.currentTimeMillis(); System.out.println("the timePeriod executed is : " + (endTime - startTime) + "ms"); session.close(); } catch (Exception e) { e.printStackTrace(); } } }
package src.main.sample; import com.google.common.collect.ImmutableList; import org.apache.kudu.ColumnSchema; import org.apache.kudu.Schema; import org.apache.kudu.Type; import org.apache.kudu.client.*; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * 數據刷新策略對比 */ public class InsertFlushData { // 緩衝大小,也就是數據的條數 private final static int OPERATION_BATCH = 2000; /** * mode形式: * SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND 後臺自動一次性批處理刷新提交N條數據 * SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC 每次自動同步刷新提交每條數據 * SessionConfiguration.FlushMode.MANUAL_FLUSH 手動刷新一次性提交N條數據 */ // 支持三個模式的測試用例 public static void insertTestGeneric(KuduSession session, KuduTable table, SessionConfiguration.FlushMode mode, int recordCount) throws Exception { //設置 刷新提交模式 session.setFlushMode(mode); //當刷新提交模式 不爲 AUTO_FLUSH_SYNC(自動同步刷新)時,才設置緩衝大小(數據條數) if (SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC != mode) { // 緩衝大小,也就是數據的條數 session.setMutationBufferSpace(OPERATION_BATCH); } int commit = 0; for (int i = 0; i < recordCount; i++) { Insert insert = table.newInsert(); PartialRow row = insert.getRow(); UUID uuid = UUID.randomUUID(); // row.addString("字段名", 字段值)、row.addLong(第幾列, 字段值) row.addString("id", uuid.toString()); row.addInt("value1", 16); row.addLong("value2", 16); Long gtmMillis; /** * System.currentTimeMillis() 是從1970-01-01開始算的毫秒數(GMT), kudu API是採用納秒數, 因此須要時間*1000 * 另外, 考慮到咱們是東8區時間, 因此轉成Long型須要再加8個小時, 不然存到Kudu的時間是GTM, 比東8區晚8個小時 */ // 第一步: 獲取當前時間對應的GTM時區unix毫秒數 // 第二步: 將timestamp轉成對應的GTM時區unix毫秒數 Timestamp localTimestamp = new Timestamp(System.currentTimeMillis()); gtmMillis = localTimestamp.getTime(); // 將GTM的毫秒數轉成東8區的毫秒數量 Long shanghaiTimezoneMillis = gtmMillis + 8 * 3600 * 1000; row.addLong("timestamp", shanghaiTimezoneMillis); session.apply(insert); // 對於在MANUAL_FLUSH(手動刷新)模式時,進行 手動刷新提交 if (SessionConfiguration.FlushMode.MANUAL_FLUSH == mode) { commit = commit + 1; // 對於手工提交, 須要buffer在未滿的時候flush,這裏採用了buffer一半時即提交 //若是要提交的數據條數 已經大於 緩衝大小(數據條數)除以2的值的話,則進行一次手動刷新提交 if (commit > OPERATION_BATCH / 2) { session.flush();//手動刷新提交 commit = 0; } } } // 對於在MANUAL_FLUSH(手動刷新)模式時,進行 手動刷新提交 // 對於手工提交, 保證完成最後的提交 if (SessionConfiguration.FlushMode.MANUAL_FLUSH == mode && commit > 0) { session.flush();//手動刷新提交 } // 對於後臺自動提交, 必須保證完成最後的提交, 並保證有錯誤時能拋出異常 if (SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND == mode) { session.flush();//手動刷新提交 RowErrorsAndOverflowStatus error = session.getPendingErrors(); // 檢查錯誤收集器是否有溢出和是否有行錯誤 if (error.isOverflowed() || error.getRowErrors().length > 0) { if (error.isOverflowed()) { throw new Exception("kudu overflow exception occurred."); } StringBuilder errorMessage = new StringBuilder(); if (error.getRowErrors().length > 0) { for (RowError errorObj : error.getRowErrors()) { errorMessage.append(errorObj.toString()); errorMessage.append(";"); } } throw new Exception(errorMessage.toString()); } } } // 支持手動flush的測試用例 public static void insertTestManualFlush(KuduSession session, KuduTable table, int recordCount) throws Exception { SessionConfiguration.FlushMode mode = SessionConfiguration.FlushMode.MANUAL_FLUSH; session.setFlushMode(mode); session.setMutationBufferSpace(OPERATION_BATCH); int commit = 0; for (int i = 0; i < recordCount; i++) { Insert insert = table.newInsert(); PartialRow row = insert.getRow(); UUID uuid = UUID.randomUUID(); row.addString("id", uuid.toString()); row.addInt("value1", 17); row.addLong("value2", 17); Long gtmMillis; /** * System.currentTimeMillis() 是從1970-01-01開始算的毫秒數(GMT), kudu API是採用納秒數, 因此須要時間*1000 * 另外, 考慮到咱們是東8區時間, 因此轉成Long型須要再加8個小時, 不然存到Kudu的時間是GTM, 比東8區晚8個小時 */ // 第一步: 獲取當前時間對應的GTM時區unix毫秒數 // 第二步: 將timestamp轉成對應的GTM時區unix毫秒數 Timestamp localTimestamp = new Timestamp(System.currentTimeMillis()); gtmMillis = localTimestamp.getTime(); // 將GTM的毫秒數轉成東8區的毫秒數量 Long shanghaiTimezoneMillis = gtmMillis + 8 * 3600 * 1000; row.addLong("timestamp", shanghaiTimezoneMillis); session.apply(insert); // 對於手工提交, 須要buffer在未滿的時候flush,這裏採用了buffer一半時即提交 commit = commit + 1; //若是要提交的數據條數 已經大於 緩衝大小(數據條數)除以2的值的話,則進行一次手動刷新提交 if (commit > OPERATION_BATCH / 2) { session.flush();//手動刷新提交 commit = 0; } } // 對於手工提交, 保證完成最後的提交 if (commit > 0) { session.flush();//手動刷新提交 } } // 自動flush的測試案例 public static void insertTestAutoFlushSync(KuduSession session, KuduTable table, int recordCount) throws Exception { SessionConfiguration.FlushMode mode = SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC; session.setFlushMode(mode); for (int i = 0; i < recordCount; i++) { Insert insert = table.newInsert(); PartialRow row = insert.getRow(); UUID uuid = UUID.randomUUID(); row.addString("id", uuid.toString()); row.addInt("value1", 18); row.addLong("value2", 18); Long gtmMillis; /** * System.currentTimeMillis() 是從1970-01-01開始算的毫秒數(GMT), kudu API是採用納秒數, 因此須要時間*1000 * 另外, 考慮到咱們是東8區時間, 因此轉成Long型須要再加8個小時, 不然存到Kudu的時間是GTM, 比東8區晚8個小時 */ // 第一步: 獲取當前時間對應的GTM時區unix毫秒數 gtmMillis = System.currentTimeMillis(); // 第二步: 將timestamp轉成對應的GTM時區unix毫秒數 Timestamp localTimestamp = new Timestamp(System.currentTimeMillis()); gtmMillis = localTimestamp.getTime(); // 將GTM的毫秒數轉成東8區的毫秒數量 Long shanghaiTimezoneMillis = gtmMillis + 8 * 3600 * 1000; row.addLong("timestamp", shanghaiTimezoneMillis); // 對於AUTO_FLUSH_SYNC模式, apply()將當即完成數據寫入,可是並非批處理 session.apply(insert); } } /** * 測試案例 */ public static void testStrategy() throws KuduException { KuduClient client = new KuduClient.KuduClientBuilder("192.168.161.128,192.168.161.129,192.168.161.130").build(); KuduSession session = client.newSession(); KuduTable table = client.openTable("bigData2"); SessionConfiguration.FlushMode mode; long d1; long d2; long timeMillis; long seconds; int recordCount = 200000; try { // 自動刷新策略(默認的刷新策略,同步刷新) mode = SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC; System.out.println(mode + " is start!"); d1 = System.currentTimeMillis(); insertTestAutoFlushSync(session, table, recordCount); d2 = System.currentTimeMillis(); timeMillis = d2 - d1; System.out.println(mode.name() + "花費毫秒數: " + timeMillis); // 後臺刷新策略(後臺批處理刷新) mode = SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND; System.out.println(mode + " is start!"); d1 = System.currentTimeMillis(); insertTestGeneric(session, table, mode, recordCount); d2 = System.currentTimeMillis(); timeMillis = d2 - d1; System.out.println(mode.name() + "花費毫秒數: " + timeMillis); // 手動刷新 mode = SessionConfiguration.FlushMode.MANUAL_FLUSH; System.out.println(mode + " is start!"); d1 = System.currentTimeMillis(); insertTestManualFlush(session, table, recordCount); d2 = System.currentTimeMillis(); timeMillis = d2 - d1; System.out.println(mode.name() + "花費毫秒數: " + timeMillis); } catch (Exception e) { e.printStackTrace(); } finally { if (!session.isClosed()) { session.close(); } } } public static void createTable() { String tableName = "bigData2"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.161.128,192.168.161.129,192.168.161.130").defaultAdminOperationTimeoutMs(60000).build(); KuduSession session = client.newSession(); session.setTimeoutMillis(60000); try { // 測試,若是table存在的狀況下,就刪除該表 if (client.tableExists(tableName)) { client.deleteTable(tableName); System.out.println("delete the table is success!"); } List<ColumnSchema> columns = new ArrayList(); // 建立列 columns.add(new ColumnSchema.ColumnSchemaBuilder("id", Type.STRING).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("value1", Type.INT32).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("value2", Type.INT64).key(true).build()); columns.add(new ColumnSchema.ColumnSchemaBuilder("timestamp", Type.INT64).key(true).build()); // 建立schema Schema schema = new Schema(columns); /* 建立 hash分區 + range分區 二者同時使用 的表 addHashPartitions(ImmutableList.of("字段名1","字段名2",...), hash分區數量) 默認使用主鍵,也可另外指定聯合主鍵 setRangePartitionColumns(ImmutableList.of("字段名")) */ // id和timestamp 組成 聯合主鍵 ImmutableList<String> hashKeys = ImmutableList.of("id", "timestamp"); CreateTableOptions tableOptions = new CreateTableOptions(); // 設置hash分區,包括分區數量、副本數目 tableOptions.addHashPartitions(hashKeys, 20); //hash分區數量 tableOptions.setNumReplicas(1);//副本數目 System.out.println("create the table is success! "); // 建立table,並設置partition client.createTable(tableName, schema, tableOptions); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { try { createTable(); testStrategy(); /** *AUTO_FLUSH_SYNC is start! AUTO_FLUSH_SYNC花費毫秒數: 588863 AUTO_FLUSH_BACKGROUND is start! AUTO_FLUSH_BACKGROUND花費毫秒數: 12284 MANUAL_FLUSH is start! MANUAL_FLUSH花費毫秒數: 17231 */ } catch (KuduException e) { e.printStackTrace(); } } } ---------------------
package src.main.sample; import org.apache.kudu.Schema; import org.apache.kudu.client.*; import java.util.ArrayList; import java.util.List; public class SelectData { public static void main(String[] args) { try { String tableName = "bigData"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.161.128,192.168.161.129,192.168.161.130").defaultAdminOperationTimeoutMs(60000).build(); // 獲取須要查詢數據的列 List<String> projectColumns = new ArrayList<String>(); projectColumns.add("id"); projectColumns.add("user_id"); projectColumns.add("start_time"); projectColumns.add("name"); KuduTable table = client.openTable(tableName); // 簡單的讀取 KuduScanner scanner = client.newScannerBuilder(table).setProjectedColumnNames(projectColumns).build(); // 根據主鍵設置讀取的上限和下限 // Schema schema = table.getSchema(); // PartialRow lower = schema.newPartialRow(); // lower.addLong("id", 10); // lower.addLong("user_id", 10); // lower.addLong("start_time", 50); // PartialRow upper = schema.newPartialRow(); // upper.addLong("id", 50); // upper.addLong("user_id", 50); // upper.addLong("start_time", 50); // KuduScanner scanner = client.newScannerBuilder(table) // .setProjectedColumnNames(projectColumns) // .lowerBound(lower) // .exclusiveUpperBound(upper) // .build(); while (scanner.hasMoreRows()) { RowResultIterator results = scanner.nextRows(); // 15個tablet,每次從tablet中獲取的數據的行數 int numRows = results.getNumRows(); System.out.println("numRows count is : " + numRows); while (results.hasNext()) { RowResult result = results.next(); long id = result.getLong(0); long user_id = result.getLong(1); long start_time = result.getLong(2); String name = result.getString(3); System.out.println("id is : " + id + " === user_id is : " + user_id + " === start_time : " + start_time + " === name is : " + name); } System.out.println("--------------------------------------"); } } catch (Exception e) { e.printStackTrace(); } } }
package src.main.sample; import org.apache.kudu.client.*; public class UpsertData { public static void main(String[] args) { try { String tableName = "bigData"; KuduClient client = new KuduClient.KuduClientBuilder("192.168.161.128,192.168.161.129,192.168.161.130").defaultAdminOperationTimeoutMs(60000).build(); // 獲取table KuduTable table = client.openTable(tableName); /** * mode形式: * SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND 後臺自動一次性批處理刷新提交N條數據 * SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC 每次自動同步刷新提交每條數據 * SessionConfiguration.FlushMode.MANUAL_FLUSH 手動刷新一次性提交N條數據 */ // 獲取一個會話 KuduSession session = client.newSession(); session.setTimeoutMillis(60000); session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH); //手動刷新一次性提交N條數據 session.setMutationBufferSpace(10000); // 緩衝大小,也就是數據的條數 // 插入時,初始時間 long startTime = System.currentTimeMillis(); int val = 0; // 在使用 upsert 語句時,當前須要 三個條件(key)都知足的狀況下,才能夠更新數據,不然就是插入數據 // 三個條件(key) 分別指的是 hash分區的聯合主鍵id、user_id,還有range分區字段 start_time for (int i = 0; i < 60; i++) { //upsert into 表名 values (‘xx’,123) 若是指定的values中的主鍵值 在表中已經存在,則執行update語義,反之,執行insert語義。 Upsert upsert = table.newUpsert(); PartialRow row = upsert.getRow(); row.addLong(0, i); //指第一個字段 "id"(hash分區的聯合主鍵之一) row.addLong(1, i*100); //指第二個字段 "user_id"(hash分區的聯合主鍵之一) row.addLong(2, i); //指第三個字段 "start_time"(range分區字段) row.addString(3, "bigData"+i); //指第四個字段 "name" session.apply(upsert); if (val % 10 == 0) { session.flush(); //手動刷新提交 val = 0; } val++; } session.flush(); //手動刷新提交 // 插入時結束時間 long endTime = System.currentTimeMillis(); System.out.println("the timePeriod executed is : " + (endTime - startTime) + "ms"); } catch (Exception e) { e.printStackTrace(); } } }
impala命令刷新元數據 1.impala-shell 命令進入交互界面 執行 invalidate metadata; 命令刷新元數據 2.Hue的wen頁面中,在impala執行sql的窗口 執行 invalidate metadata; 命令刷新元數據 -------------------------------------------------------------------------- 從Impala建立一個新的Kudu表 從Impala在Kudu中建立新表相似於將現有Kudu表映射到Impala表,除了您須要本身指定模式和分區信息。 使用如下示例做爲指導。Impala首先建立表,而後建立映射。 Impala 中建立一個新的 Kudu 表 CREATE TABLE my_first_table ( id BIGINT, name STRING, PRIMARY KEY(id) ) PARTITION BY HASH PARTITIONS 16 STORED AS KUDU; 在CREATE TABLE語句中,必須首先列出組成主鍵的列。此外,隱式標記主鍵列NOT NULL。 建立新的Kudu表時,您須要指定分發方案。 請參閱分區表:https://kudu.apache.org/docs/kudu_impala_integration.html#partitioning_tables 爲了爲簡單起見,上面的表建立示例經過散列 id 列分紅 16 個分區。 有關分區的指導,請參閱 分區規則:https://kudu.apache.org/docs/kudu_impala_integration.html#partitioning_rules_of_thumb CREATE TABLE AS SELECT 您可使用 CREATE TABLE ... AS SELECT 語句查詢 Impala 中的任何其餘表或表來建立表。 如下示例將現有表 old_table 中的全部行導入到 Kudu 表 new_table 中。 new_table 中的列的名稱和類型 將根據 SELECT 語句的結果集中的列肯定。 請注意,您必須另外指定主鍵和分區。 CREATE TABLE new_table PRIMARY KEY (ts, name) PARTITION BY HASH(name) PARTITIONS 8 STORED AS KUDU AS SELECT ts, name, value FROM old_table; -------------------------------------------------------------------------- 在Impala中查詢現有的Kudu表:Impala中建立映射Kudu表的外部映射表 經過Kudu API或其餘集成(如Apache Spark)建立的表在Impala中不會自動顯示。 要查詢它們,必須首先在Impala中建立外部表,以將Kudu表映射到Impala數據庫: CREATE EXTERNAL TABLE `bigData` STORED AS KUDU TBLPROPERTIES( 'kudu.table_name' = 'bigData', 'kudu.master_addresses' = 'node1:7051,node2:7051,node3:7051') 查詢 Impala 中現有的 Kudu 表(Impala中建立映射表(外部表)映射Kudu中的表) 經過 Kudu API 或其餘集成(如 Apache Spark )建立的表不會在 Impala 中自動顯示。 要查詢它們,您必須先在 Impala 中建立外部表以將 Kudu 表映射到 Impala 數據庫中: CREATE EXTERNAL TABLE my_mapping_table STORED AS KUDU TBLPROPERTIES ( 'kudu.table_name' = 'my_kudu_table' ); -------------------------------------------------------------------------- 內部和外部 Impala 表 使用 Impala 建立新的 Kudu 表時,能夠將表建立爲內部表或外部表。 Internal ( 內部表 ) 內部表由 Impala 管理,當您從 Impala 中刪除時,數據和表確實被刪除。當您使用 Impala 建立新表時,一般是內部表。 External ( 外部表 ) 外部表(由 CREATE EXTERNAL TABLE 建立)不受 Impala 管理,而且刪除此表不會將表從其源位置(此處爲 Kudu)丟棄。 相反,它只會去除 Impala 和 Kudu 之間的映射。這是 Kudu 提供的用於將現有表映射到 Impala 的語法。 -------------------------------------------------------------------------- Kudu中的分區方法主要有兩種:partition by hash 和 partition by range kudu表基於其partition方法被拆分紅多個分區,每一個分區就是一個tablet,一張kudu表所屬的全部tablets均勻分佈並存儲在tablet servers的磁盤上。 所以在建立kudu表的時候須要聲明該表的partition方法,同時要指定primary key做爲partition的依據。 基於hash的分區方法的基本原理是: 基於primary key的hash值將每一個row(行)劃分到相應的tablet當中,分區的個數即tablet的個數必須在建立表語句中指定,建表語句示例以下: 注:若是未指定基於某個字段的hash值進行分區,默認以主鍵的hash值進行分區。 create table test ( name string, age int, primary key (name) ) partition by hash (name) partitions 8 stored as kudu; 基於range的分區方法的基本原理是: 基於指定主鍵的取值範圍將每一個row(行)劃分到相應的tablet當中,用於range分區的主鍵以及各個取值範圍都必須在建表語句中聲明,建表語句示例以下: 例子:有班級、姓名、年齡三個字段,表中的每一個row將會根據其所在的班級劃分紅四個分區,每一個分區就表明一個班級。 create table test ( classes int, name string, age int, primary key (classes,name) ) partition by range (classes) ( partition value = 1, partition value = 2, partition value = 3, partition value = 4 ) stored as kudu; kudu表還能夠採用基於hash和基於range相結合的分區方式 /* 建立 hash分區 + range分區 二者同時使用 的表 addHashPartitions(ImmutableList.of("字段名1","字段名2",...), hash分區數量) 默認使用主鍵,也可另外指定聯合主鍵 setRangePartitionColumns(ImmutableList.of("字段名")) */ // 設置hash分區,包括分區數量、副本數目 tableOptions.addHashPartitions(hashKeys,3); //hash分區數量 tableOptions.setNumReplicas(3); //副本數目 // 設置range分區 tableOptions.setRangePartitionColumns(ImmutableList.of("start_time")); -------------------------------------------------------------------------- kudu表支持3種insert語句: 1.insert into test values(‘a’,12); 2.insert into test values(‘a’,12),(‘b’,13),(‘c’,14); 3.insert into test select * from other_table; update語句 kudu表的update操做不能更改主鍵的值,其餘與標準sql語法相同。 upsert 語句 對於 upsert into test values (‘a’,12) 若是指定的values中的主鍵值 在表中已經存在,則執行update語義,反之,執行insert語義。 注意:若是同時存在 主鍵/聯合主鍵、hash分區字段、range分區字段時,那麼便要求三個條件都符合的狀況下,才能夠更新數據,不然就是插入數據。 delete語句 與標準sql語法相同。 -------------------------------------------------------------------------- Impala 中建立一個新的 Kudu 表 create table test ( classes int, name string, age int, primary key (classes,name) ) partition by range (classes) ( partition value = 1, partition value = 2, partition value = 3, partition value = 4 ) stored as kudu; insert into test values(1,"nagisa",16); select * from test; kudu webUI頁面顯示 impala::default.test Impala中建立映射Kudu表的外部映射表 CREATE EXTERNAL TABLE `EXTERNAL_test` STORED AS KUDU TBLPROPERTIES( 'kudu.table_name' = 'impala::default.test', 'kudu.master_addresses' = 'node1:7051,node2:7051,node3:7051'); insert into test values(2,"ushio",5); select * from EXTERNAL_test; -------------------------------------------------------------------------- 指定 Tablet Partitioning ( Tablet 分區 ) 表分爲每一個由一個或多個 tablet servers 提供的 tablets 。理想狀況下,tablets 應該相對平等地拆分表的數據。 Kudu 目前沒有自動(或手動)拆分預先存在的 tablets 的機制。在實現此功能以前,您必須在建立表時指定分區。 在設計表格架構時,請考慮使用主鍵,您能夠將表拆分紅以相似速度增加的分區。 使用 Impala 建立表時,可使用 PARTITION BY 子句指定分區: 注意:Impala 關鍵字(如 group)在關鍵字意義上不被使用時,由背面的字符包圍。 CREATE TABLE cust_behavior ( _id BIGINT PRIMARY KEY, salary STRING, edu_level INT, usergender STRING, `group` STRING, city STRING, postcode STRING, last_purchase_price FLOAT, last_purchase_date BIGINT, category STRING, sku STRING, rating INT, fulfilled_date BIGINT ) PARTITION BY RANGE (_id) ( PARTITION VALUES < 1439560049342, PARTITION 1439560049342 <= VALUES < 1439566253755, PARTITION 1439566253755 <= VALUES < 1439572458168, PARTITION 1439572458168 <= VALUES < 1439578662581, PARTITION 1439578662581 <= VALUES < 1439584866994, PARTITION 1439584866994 <= VALUES < 1439591071407, PARTITION 1439591071407 <= VALUES ) STORED AS KUDU; 若是您有多個主鍵列,則可使用元組語法指定分區邊界:('va',1),('ab',2)。該表達式必須是有效的 JSON Impala 數據庫和 Kudu 每一個 Impala 表都包含在稱爲數據庫的命名空間中。默認數據庫稱爲默認數據庫,用戶可根據須要建立和刪除其餘數據庫 當從 Impala 中建立一個受管 Kudu 表時,相應的 Kudu 表將被命名爲 my_database :: table_name 不支持 Kudu 表的 Impala 關鍵字 建立 Kudu 表時不支持如下 Impala 關鍵字: - PARTITIONED - LOCATION - ROWFORMAT -------------------------------------------------------------------------- 優化評估 SQL 謂詞的性能 若是您的查詢的 WHERE 子句包含與 operators = , <= , '\ , '\' , > = , BETWEEN 或 IN 的比較,則 Kudu 直接評估該條件,只返回相關結果。 這提供了最佳性能,由於 Kudu 只將相關結果返回給 Impala 。 對於謂詞 != , LIKE 或 Impala 支持的任何其餘謂詞類型, Kudu 不會直接評估謂詞,而是將全部結果返回給 Impala ,並依賴於 Impala 來評估剩餘的謂詞並相應地過濾結果。 這可能會致使性能差別,這取決於評估 WHERE 子句以前和以後的結果集的增量。 分區表 根據主鍵列上的分區模式將表格劃分爲 tablets 。每一個 tablet 由至少一臺 tablet server 提供。 理想狀況下,一張表應該分紅多個 tablets 中分佈的 tablet servers ,以最大化並行操做。您使用的分區模式的詳細信息將徹底取決於您存儲的數據類型和訪問方式。關於 Kudu 模式設計的全面討論,請參閱 Schema Design。 Kudu 目前沒有在建立表以後拆分或合併 tablets 的機制。建立表時,必須爲表提供分區模式。在設計表格時,請考慮使用主鍵,這樣您就能夠將表格分爲以相同速率增加的 tablets 。 您可使用 Impala 的 PARTITION BY 關鍵字對錶進行分區,該關鍵字支持 RANGE 或 HASH 分發。分區方案能夠包含零個或多個 HASH 定義,後面是可選的 RANGE 定義。 RANGE 定義能夠引用一個或多個主鍵列。基本 和 高級分區 的示例以下所示。 ---------------------