【Hbase-總結】

 【Hbase-總結】
                                                不善於總結,就是在浪費時間
1、Hbase架構及包含角色

  • Client:主要做用是提供訪問Hbase 的接口,維護了對應的Cache來加速Hbase 的訪問,例如cache的.META元數據信息。
  • Zookeeper:主要做用是提供Hmaster高可用及RegionServer的監控、元數據入口、集羣配置維護等操作:
    • 使用ZK的選舉機制進行選舉leader,若是Hmaster中的一個Master 掛掉,ZK會從新選舉一個Master。
    • 能夠監控ReagionServer的狀態,若是有異常,就會通知Master的上下限的信息。
    • 經過zk存儲元數據的統一入口。
  • Hmater:
    • 爲RegionServer分配Region.
    • 維護集羣的負載均衡。
    • 維護集羣元數據信息。
    • 發現失效的Region,並將失效的Region分配到正常的RegionServer上。
    • 當RegionServer失效的時候,協調對應的Hlog的拆分。
  • HregionServer:
    • 直接進行數據操做的角色,直接對接用戶的讀寫請求。
    • 管理Master爲其分配的 region
    • 處理用戶的讀寫請求。
    • 負責和HDFS進行交互操做,將數據存儲在HDFS中。
    • 負責region變大後的拆分。
    • 負責Storefile的合併操做。
  • HDFS:
    • 提供了底層的數據存儲服務,並實現數據及Hlog的高可用,確保數據不丟失。
  • Phonenix:
    • 實現Hbase建立二級索引。
    • 能夠提供Hbase的SQL查詢。

2、Hbase的幾個概念: 
  • CF列簇:
    • 列簇的概念:
列是根據列簇進行分組的。
    • 列簇的特色:
      • 一張表有本身獨立的列簇而列簇在一張表中最多不能多於5個通常在使用的時候都是1個。
      • 列簇必需要在表建立的時候建立,而且建立完成後沒法改變。
      • 每一個列簇中的列數是無限制的。
      • 同一列簇中表的列是存儲在一塊兒的。
      • 列在列簇中是有序的,安裝字典進行排序。
      • 列在運行時候建立。
      • 列只有插入後纔會存在,空值不進行保存。
      • 每一個CF能夠有一個或多個列成員(ColumnQualifier),列成員不須要在表定義時給出,新的列族成員能夠隨後按需、動態加入
      • 數據按CF分開存儲,HBase所謂的列式存儲就是根據CF分開存儲(每一個CF對應一個Store),這種設計很是適合於數據分析的情形
  • Rowkey行健:
    • 行鍵是字節數組, 任何字符串均可以做爲行鍵;
    • 表中的行根據行鍵進行排序,數據按照Row key的字節序(byte order)排序存儲;
    • 全部對錶的訪問都要經過行鍵 (單個RowKey訪問,或RowKey範圍訪問,或全表掃描) (二級索引)
    • Rowkey對Hbase的性能影響很是大,Rowkey的設計就顯得尤其的重要。設計的時候要兼顧基於Rowkey的單行查詢也要鍵入Rowkey的範圍掃描。
  • Region區域:
    • Region相似於數據庫的分片和分區的概念,每一個Region負責一小部分Rowkey範圍的數據的讀寫和維護,Region包含了對應的起始行到結束行的全部信息。master將對應的region分配給不一樣的RergionServer,由RegionSever來提供Region的讀寫服務和相關的管理工做。
    • Hbase會將一個大表的數據基於Rowkey的不一樣範圍分配到不通的Region中,每一個Region負責必定範圍的數據訪問和存儲。這樣即便是一張巨大的表,因爲被切割到不通的region,訪問起來的時延也很低。
    • HBase自動把表水平(按Row)劃分紅多個區域(region),每一個region會保存一個表裏面某段連續的數據;
    • 每一個表一開始只有一個region,隨着數據不斷插入表,region不斷增大,當增大到一個閥值的時候,region就會等分會兩個新的region;
    •  當table中的行不斷增多,就會有愈來愈多的region。這樣一張完整的表被保存在多個Region 上。
    •  HRegion是HBase中分佈式存儲和負載均衡的最小單元(默認256M)。最小單元表示不一樣的HRegion能夠分佈在不一樣的HRegionServer上。但一個HRegion不會拆分到多個server上。

      Region實例:
    • 上圖模擬了一個Hbase的表是如何拆分紅region,以及分配到不一樣的RegionServer中去。上面是1個Userinfo表,裏面有7條記錄,其中rowkey爲0001到0002的記錄被分配到了Region1上,Rowkey爲0003到0004的記錄被分配到了Region2上,而rowkey爲000五、0006和0007的記錄則被分配到了Region3上。region1和region2被master分配給了RegionServer1(RS1),Region3被master配分給了RegionServer2(RS2)
    • 備註:這裏只是爲了更容易的說明拆分的規則,其實真實的場景並不會幾條記錄拆分到不通的Region上,而是到必定的數據量纔會拆分,具體的在Region的拆分那部分再具體的介紹。
       
                Region尋址:
    • 既然讀寫都在RegionServer上發生,每一個RegionSever爲必定數量的region服務,那麼client要對某一行數據作讀寫的時候如何能知道具體要去訪問哪一個RegionServer呢?那就是接下來咱們要討論的問題
    • 老的Region尋址方式:
    • 在Hbase 0.96版本之前,Hbase有兩個特殊的表,分別是-ROOT-表和.META.表,其中-ROOT-的位置存儲在ZooKeeper中,-ROOT-自己存儲了 .META. Table的RegionInfo信息,而且-ROOT-不會分裂,只有一個region。而.META.表能夠被切分紅多個region。讀取的流程以下圖所示:
    • 第1步:client請求ZK得到-ROOT-所在的RegionServer地址
    • 第2步:client請求-ROOT-所在的RS地址,獲取.META.表的地址,client會將-ROOT-的相關信息cache下來,以便下一次快速訪問
    • 第3步:client請求 .META.表的RS地址,獲取訪問數據所在RegionServer的地址,client會將.META.的相關信息cache下來,以便下一次快速訪問
    • 第4步:client請求訪問數據所在RegionServer的地址,獲取對應的數據
    • 從上面的路徑咱們能夠看出,用戶須要3次請求才能直到用戶Table真正的位置,這在必定程序帶來了性能的降低。在0.96以前使用3層設計的主要緣由是考慮到元數據可能須要很大。可是真正集羣運行,元數據的大小其實很容易計算出來。在BigTable的論文中,每行METADATA數據存儲大小爲1KB左右,若是按照一個Region爲128M的計算,3層設計能夠支持的Region個數爲2^34個,採用2層設計能夠支持^17(131072)。那麼2層設計的狀況下一個 集羣能夠存儲4P的數據。這僅僅是一個Region只有128M的狀況下。若是是10G呢? 所以,經過計算,其實2層設計就能夠知足集羣的需求。所以在0.96版本之後就去掉了-ROOT-表了。
    • 新的Region尋址方式:
    • 如上面的計算,2層結構其實徹底能知足業務的需求,所以0.96版本之後將-ROOT-表去掉了。以下圖所示:
    • 訪問路徑變成了3步:
    • 第1步:Client請求ZK獲取.META.所在的RegionServer的地址。
    • 第2步:Client請求.META.所在的RegionServer獲取訪問數據所在的RegionServer地址,client會將.META.的相關信息cache下來,以便下一次快速訪問。
    • 第3步:Client請求數據所在的RegionServer,獲取所須要的數據。
    • 總結去掉-ROOT-的緣由有以下2點:
    • 其一:提升性能
    • 其二:2層結構已經足以知足集羣的需求
    • 這裏還有一個問題須要說明,那就是Client會緩存.META.的數據,用來加快訪問,既然有緩存,那它何時更新?若是.META.更新了,好比Region1不在RerverServer2上了,被轉移到了RerverServer3上。client的緩存沒有更新會有什麼狀況?
    • 其實,Client的元數據緩存不更新,當.META.的數據發生更新。如上面的例子,因爲Region1的位置發生了變化,Client再次根據緩存去訪問的時候,會出現錯誤,當出現異常達到重試次數後就會去.META.所在的RegionServer獲取最新的數據,若是.META.所在的RegionServer也變了,Client就會去ZK上獲取.META.所在的RegionServer的最新地址。
  • 時間戳(TimeStamp)
    •  每一個Cell可能又多個版本,它們之間用時間戳區分。
  • 單元格(Cell)
    • Cell 由行鍵,列族:限定符,時間戳惟一決定,數據所有以字節碼形式存儲。

3、Hbase 讀寫流程
  • Hbase的寫邏輯設計到內存、寫log、刷盤等操做。
  • 寫入流程:
    • 從上圖能夠看出氛圍3步驟:
    • 第1步:Client獲取數據寫入的Region所在的RegionServer
    • 第2步:請求寫Hlog
    • 第3步:請求寫MemStore
    • 只有當寫Hlog和寫MemStore都成功了纔算請求寫入完成。MemStore後續會逐漸刷到HDFS中。
    • 備註:Hlog存儲在HDFS,當RegionServer出現異常,須要使用Hlog來恢復數據。
  • 讀操做流程:
    • 步驟1:client訪問Zookeeper,查找.META.表信息。
    • 步驟2:從.META.表查找,獲取存放目標數據的HRegion信息,從而找到對應的HRegionServer。
    • 步驟3:經過HRegionServer獲取須要查找的數據。
    • 步驟4:HRegionserver的內存分爲MemStore和BlockCache兩部分,MemStore主要用於寫數據,BlockCache主要用於讀數據。讀請求先到MemStore中查數據,查不到就到BlockCache中查,再查不到就會到StoreFile上讀,並把讀的結果放入BlockCache。

4、MemStore刷盤:
    爲了提升Hbase的寫入性能,當寫請求寫入MemStore後,不會當即刷盤。而是會等到必定的時候進行刷盤的操做。具體是哪些場景會觸發刷盤的操做呢?總結成以下的幾個場景:
  • 全局內存控制
    • 這個全局的參數是控制內存總體的使用狀況,當全部memstore佔整個heap的最大比例的時候,會觸發刷盤的操做。這個參數是hbase.regionserver.global.memstore.upperLimit,默認爲整個heap內存的40%。但這並不意味着全局內存觸發的刷盤操做會將全部的MemStore都進行輸盤,而是經過另一個參數hbase.regionserver.global.memstore.lowerLimit來控制,默認是整個heap內存的35%。當flush到全部memstore佔整個heap內存的比率爲35%的時候,就中止刷盤。這麼作主要是爲了減小刷盤對業務帶來的影響,實現平滑系統負載的目的。
  • MemStore達到上限
    • 當MemStore的大小達到hbase.hregion.memstore.flush.size大小的時候會觸發刷盤,默認128M大小
  • RegionServer的Hlog數量達到上限
    • 前面說到Hlog爲了保證Hbase數據的一致性,那麼若是Hlog太多的話,會致使故障恢復的時間太長,所以Hbase會對Hlog的最大個數作限制。當達到Hlog的最大個數的時候,會強制刷盤。這個參數是hase.regionserver.max.logs,默認是32個。
  • 手工觸發
    • 能夠經過hbase shell或者java api手工觸發flush的操做。
  • 關閉RegionServer觸發
    • 在正常關閉RegionServer會觸發刷盤的操做,所有數據刷盤後就不須要再使用Hlog恢復數據。
  • Region使用HLOG恢復完數據後觸發
    • 當RegionServer出現故障的時候,其上面的Region會遷移到其餘正常的RegionServer上,在恢復完Region的數據後,會觸發刷盤,當刷盤完成後纔會提供給業務訪問。

5、Hlog
        Hlog是Hbase實現WAL(Write ahead log)方式產生的日誌信息,內部是一個簡單的順序日誌。每一個RegionServer對應1個Hlog(備註:1.x版本的能夠開啓MultiWAL功能,容許多個Hlog),全部對於該RegionServer的寫入都被記錄到Hlog中。Hlog實現的功能就是咱們前面講到的保證數據安全。當RegionServer出現問題的時候,能跟進Hlog來作數據恢復。此外爲了保證恢復的效率,Hbase會限制最大保存的Hlog數量,若是達到Hlog的最大個數(hase.regionserver.max.logs參數控制)的時候,就會觸發強制刷盤操做。對於已經刷盤的數據,其對應的Hlog會有一個過時的概念,Hlog過時後,會被監控線程移動到 .oldlogs,而後會被自動刪除掉。Hbase是如何判斷Hlog過時的呢?要找到這個答案,咱們就必須瞭解Hlog的詳細結構。
  • Hlog結構:
    • 從上圖咱們能夠看出多個Region共享一個Hlog文件,單個Region在Hlog中是按照時間順序存儲的,可是多個Region可能並非徹底按照時間順序。每一個Hlog最小單元由Hlogkey和WALEdit兩部分組成。Hlogky由sequenceid、timestamp、cluster ids、regionname以及tablename等組成,WALEdit是由一系列的KeyValue組成,對一行上全部列(即全部KeyValue)的更新操做,都包含在同一個WALEdit對象中,這主要是爲了實現寫入一行多個列時的原子性。注意,圖中有個sequenceid。sequenceid是一個store級別的自增序列號,這個很是重要,region的數據恢復和Hlog過時清除都要依賴它。下面就來簡單描述一下sequenceid的相關邏輯。
    • Memstore在達到必定的條件會觸發刷盤的操做,刷盤的時候會獲取刷新到最新的一個sequenceid的下一個sequenceid,並將新的sequenceid賦給oldestUnflushedSequenceId,並刷到Ffile中。有點繞,舉個例子來講明:好比對於某一個store,開始的時候oldestUnflushedSequenceId爲NULL,此時,若是觸發flush的操做,假設初始刷盤到sequenceid爲10,那麼hbase會在10的基礎上append一個空的Entry到HLog,最新的sequenceid爲11,而後將sequenceid爲11的號賦給oldestUnflushedSequenceId,並將oldestUnflushedSequenceId的值刷到Hfile文件中進行持久化。
    • Hlog文件對應全部Region的store中最大的sequenceid若是已經刷盤,就認爲Hlog文件已通過期,就會移動到.oldlogs,等待被移除。
    • 當RegionServer出現故障的時候,須要對Hlog進行回放來恢復數據。回放的時候會讀取Hfile的oldestUnflushedSequenceId中的sequenceid和Hlog中的sequenceid進行比較,小於sequenceid的就直接忽略,但與或者等於的就進行重作。回放完成後,就完成了數據的恢復工做。
  • Hlog的生命週期
    • Hlog從產生到最後刪除須要經歷以下幾個過程:
    • 產生
      • 全部涉及到數據的變動都會先寫Hlog,除非是你關閉了Hlog
    • 滾動
      • Hlog的大小經過參數hbase.regionserver.logroll.period控制,默認是1個小時,時間達到hbase.regionserver.logroll.period 設置的時間,Hbase會建立一個新的Hlog文件。這就實現了Hlog滾動的目的。Hbase經過hbase.regionserver.maxlogs參數控制Hlog的個數。滾動的目的,爲了控制單個Hlog文件過大的狀況,方便後續的過時和刪除。
    • 過時
      • 前面咱們有講到sequenceid這個東東,Hlog的過時依賴於對sequenceid的判斷。Hbase會將Hlog的sequenceid和Hfile最大的sequenceid(刷新到的最新位置)進行比較,若是該Hlog文件中的sequenceid比刷新的最新位置的sequenceid都要小,那麼這個Hlog就過時了,過時了之後,對應Hlog會被移動到.oldlogs目錄。
      • 這裏有個問題,爲何要將過時的Hlog移動到.oldlogs目錄,而不是直接刪除呢?
      • 答案是由於Hbase還有一個主從同步的功能,這個依賴Hlog來同步Hbase的變動,有一種狀況不能刪除Hlog,那就是Hlog雖然過時,可是對應的Hlog並無同步完成,所以比較好的作好是移動到別的目錄。再增長對應的檢查和保留時間。
    • 刪除
      • 若是Hbase開啓了replication,當replication執行完一個Hlog的時候,會刪除Zoopkeeper上的對應Hlog節點。在Hlog被移動到.oldlogs目錄後,Hbase每隔hbase.master.cleaner.interval(默認60秒)時間會去檢查.oldlogs目錄下的全部Hlog,確認對應的Zookeeper的Hlog節點是否被刪除,若是Zookeeper 上不存在對應的Hlog節點,那麼就直接刪除對應的Hlog。
      • hbase.master.logcleaner.ttl(默認10分鐘)這個參數設置Hlog在.oldlogs目錄保留的最長時間。

6、Hbase主鍵設置
          HBase 是三維有序存儲的,經過rowkey(行鍵),column key(column family和qualifier)和TimeStamp(時間戳)這個三個維度能夠對HBase中的數據進行快速定位。
  • HBase中rowkey能夠惟一標識一行記錄,在HBase查詢的時候,有如下幾種方式:
  • 1>經過get方式,指定rowkey獲取惟一一條記錄
  • 2>經過scan方式,設置startRow和stopRow參數進行範圍匹配
  • 3>全表掃描,即直接掃描整張表中全部行記錄 
rowkey長度原則
  • rowkey是一個二進制的,能夠是任意字符串,最大長度 64kb ,實際應用中通常爲10-100bytes,以byte[] 形式保存,通常設計成定長。
  • 建議越短越好,不要超過16個字節,緣由以下:
  • 1>數據的持久化文件HFile中是按照KeyValue存儲的,若是rowkey過長,好比超過100字節,1000w行數據,光rowkey就要佔用100*1000w=10億個字節,將近1G數據,這樣會極大影響HFile的存儲效率;
  • 2>MemStore將緩存部分數據到內存,若是rowkey字段過長,內存的有效利用率就會下降,系統不能緩存更多的數據,這樣會下降檢索效率。
  • 3>目前操做系統都是64位系統,內存8字節對齊,控制在16個字節,8字節的整數倍利用了操做系統的最佳特。
rowkey散列原則
  • 若是rowkey按照時間戳的方式遞增,不要將時間放在二進制碼的前面,建議將rowkey的高位做爲散列字段,由程序隨機生成,低位放時間字段,這樣將提升數據均衡分佈在每一個RegionServer,以實現負載均衡的概率。若是沒有散列字段,首字段直接是時間信息,全部的數據都會集中在一個RegionServer上,這樣在數據檢索的時候負載會集中在個別的RegionServer上,形成熱點問題,會下降查詢效率。
rowkey惟一原則
  • 必須在設計上保證其惟一性,rowkey是按照字典順序排序存儲的,所以,設計rowkey的時候,要充分利用這個排序的特色,將常常讀取的數據存儲到一塊,將最近可能會被訪問的數據放到一塊。

7、熱點問題
    HBase中的行是按照rowkey的字典順序排序的,這種設計優化了scan操做,能夠將相關的行以及會被一塊兒讀取的行存取在臨近位置,便於scan。然而糟糕的rowkey設計是熱點的源頭。 熱點發生在大量的client直接訪問集羣的一個或極少數個節點(訪問多是讀,寫或者其餘操做)。大量訪問會使熱點region所在的單個機器超出自身承受能力,引發性能降低甚至region不可用,這也會影響同一個RegionServer上的其餘region,因爲主機沒法服務其餘region的請求。 設計良好的數據訪問模式以使集羣被充分,均衡的利用。
爲了不寫熱點,設計rowkey使得不一樣行在同一個region,可是在更多數據狀況下,數據應該被寫入集羣的多個region,而不是一個
一些常見的避免熱點的方法以及它們的優缺點:
  • 加鹽
    • 這裏所說的加鹽不是密碼學中的加鹽,而是在rowkey的前面增長隨機數,具體就是給rowkey分配一個隨機前綴以使得它和以前的rowkey的開頭不一樣。分配的前綴種類數量應該和你想使用數據分散到不一樣的region的數量一致。加鹽以後的rowkey就會根據隨機生成的前綴分散到各個region上,以免熱點。
  • 哈希
    • 哈希會使同一行永遠用一個前綴加鹽。哈希也可使負載分散到整個集羣,可是讀倒是能夠預測的。使用肯定的哈希可讓客戶端重構完整的rowkey,可使用get操做準確獲取某一個行數據
  • 反轉
    • 第三種防止熱點的方法時反轉固定長度或者數字格式的rowkey。這樣可使得rowkey中常常改變的部分(最沒有意義的部分)放在前面。這樣能夠有效的隨機rowkey,可是犧牲了rowkey的有序性。
    • 反轉rowkey的例子以手機號爲rowkey,能夠將手機號反轉後的字符串做爲rowkey,這樣的就避免了以手機號那樣比較固定開頭致使熱點問題
  • 其餘一些建議
    • 儘可能減小行和列的大小在HBase中,value永遠和它的key一塊兒傳輸的。當具體的值在系統間傳輸時,它的rowkey,列名,時間戳也會一塊兒傳輸。若是你的rowkey和列名很大,甚至能夠和具體的值相比較,那麼你將會遇到一些有趣的問題。HBase storefiles中的索引(有助於隨機訪問)最終佔據了HBase分配的大量內存,由於具體的值和它的key很大。能夠增長block大小使得storefiles索引再更大的時間間隔增長,或者修改表的模式以減少rowkey和列名的大小。壓縮也有助於更大的索引。
  • 列族儘量越短越好,最好是一個字符
  • 冗長的屬性名雖然可讀性好,可是更短的屬性名存儲在HBase中會更好

5、預分區
        背景:HBase默認建表時有一個region,這個region的rowkey是沒有邊界的,即沒有startkey和endkey,在數據寫入時,全部數據都會寫入這個默認的region,隨着數據量的不斷  增長,此region已經不能承受不斷增加的數據量,會進行split,分紅2個region。在此過程當中,會產生兩個問題:1.數據往一個region上寫,會有寫熱點問題。2.region split會消耗寶貴的集羣I/O資源。基於此咱們能夠控制在建表的時候,建立多個空region,並肯定每一個region的起始和終止rowky,這樣只要咱們的rowkey設計能均勻的命中各個region,就不會存在寫熱點問題。天然split的概率也會大大下降。固然隨着數據量的不斷增加,該split的仍是要進行split。像這樣預先建立hbase表分區的方式,稱之爲預分區,下面給出一種預分區的實現方式:
  • 首先看沒有進行預分區的表,startkey和endkey爲空。
  • 要進行預分區,首先要明確rowkey的取值範圍或構成邏輯,以個人rowkey組成爲例:兩位隨機數+時間戳+客戶號,兩位隨機數的範圍從00-99,因而我劃分了10個region來存儲數據,每一個region對應的rowkey範圍以下:-10,10-20,20-30,30-40,40-50,50-60,60-70,70-80,80-90,90-在使用HBase API建表的時候,須要產生splitkeys二維數組,這個數組存儲的rowkey的邊界值。下面是java 代碼實現:
 
須要注意的是,在上面的代碼中用treeset對rowkey進行排序,必需要對rowkey排序,不然在調用admin.createTable(tableDescriptor,splitKeys)的時候會出錯。建立表的代碼以下:
   
 /**
    * 建立預分區hbase表
   * @param tableName 表名
   * @param columnFamily 列簇
    * @return
   */  
   @SuppressWarnings("resource")  
  public boolean createTableBySplitKeys(String tableName, List<String> columnFamily) {  
    try {  
           if (StringUtils.isBlank(tableName) || columnFamily == null  || columnFamily.size() < 0) {  
               log.error("Parameters tableName|columnFamily should not be null,Please check!");  
             }  
             HBaseAdmin admin = new HBaseAdmin(conf);  
             if (admin.tableExists(tableName)) {  
               return true;  
           } else {  
                HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));  
                for (String cf : columnFamily) {  
                   tableDescriptor.addFamily(new HColumnDescriptor(cf));  
                 }  
                byte[][] splitKeys = getSplitKeys();  
                 admin.createTable(tableDescriptor,splitKeys);//指定splitkeys  
                 log.info("Create Table " + tableName+ "Success!columnFamily:" + columnFamily.toString());  
             }  
         } catch (MasterNotRunningException e) {    
            log.error(e);  
             return false;  
        } catch (ZooKeeperConnectionException e) {   
            log.error(e);  
             return false;  
       } catch (IOException e) {   
            log.error(e);  
            return false;  
       }  
        return true;  
    }

 


6、Hbase Java API
1、幾個主要 Hbase API 類和數據模型之間的對應關係:
一、 HBaseAdmin
關係: org.apache.hadoop.hbase.client.HBaseAdmin
做用:提供了一個接口來管理 HBase 數據庫的表信息。它提供的方法包括:建立表,刪 除表,列出表項,使表有效或無效,以及添加或刪除表列族成員等。
二、 HBaseConfiguration
關係: org.apache.hadoop.hbase.HBaseConfiguration
做用:對 HBase 進行配置
三、 HTableDescriptor
關係: org.apache.hadoop.hbase.HTableDescriptor
做用:包含了表的名字極其對應表的列族
四、 HColumnDescriptor
關係: org.apache.hadoop.hbase.HColumnDescriptor
做用:維護着關於列族的信息,例如版本號,壓縮設置等。它一般在建立表或者爲表添 加列族的時候使用。列族被建立後不能直接修改,只能經過刪除而後從新建立的方式。
列族被刪除的時候,列族裏面的數據也會同時被刪除。
五、 HTable
關係: org.apache.hadoop.hbase.client.HTable
做用:能夠用來和 HBase 表直接通訊。此方法對於更新操做來講是非線程安全的
六、 Put
關係: org.apache.hadoop.hbase.client.Put
做用:用來對單個行執行添加操做
七、 Get
關係: org.apache.hadoop.hbase.client.Get
做用:用來獲取單個行的相關信息
八、 Result
關係: org.apache.hadoop.hbase.client.Result
做用:存儲 Get 或者 Scan 操做後獲取表的單行值。使用此類提供的方法能夠直接獲取值 或者各類 Map 結構( key-value 對)
2、具體增刪改查    代碼具體實現:
package HbaseDome;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.Table;
import org.apache.hadoop.hbase.util.Bytes;
public class Hbasedome implements HBaseDemoInterface{
     
    static Configuration conf =null;
    private static final String ZKconnect="192.168.123.212:2181,192.168.123.213:2181,192.168.123.214:2181";
    static{
        conf=HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", ZKconnect);
    }
//  static String tableName="student";
//  static String[] family={"lie01","lie02"};
     
     
    public static void main(String[] args) {
        Hbasedome a =new Hbasedome();
         String tableName="student11";
         String[] family={"lie01","lie02"};
        try {
            HTableDescriptor htds =new HTableDescriptor(tableName);
            for(int z=0;z<family.length;z++){
                HColumnDescriptor h=new HColumnDescriptor(family[z]);
                htds.addFamily(h);
            }
//          a.descTable("table03");
//          a.createTable(tableName, htds);
//          a.descTable("table03");
//          a.getAllTables();
//          a.createTable(tableName,family);
//          a.getResult("table03", "usr001");
//          a.dropTable("user1");
//          a.getAllTables();
//          a.putData("table03", "usr005", "liezu01", "name", "liu");
//          a.getResult("table03", "usr001");
//          a.getResultScann("table03");
//          a.getResultScann("table03","");
             
            Result result = a.getResult("table03", "usr001");
            System.out.println(result.toString());
            List<Cell> cells = result.listCells();
            for (int i = 0; i < cells.size(); i++) {
                Cell cell = cells.get(i);
                System.out.println(cell.toString());
    //          printCell(cell);
            }
         
//          List<KeyValue> list = result.list();
//          for (int i = 0; i < list.size(); i++) {
//              KeyValue kv = list.get(i);
//              printKeyValye(kv);
//          }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
    public static void printKeyValye(KeyValue kv) {
        System.out.println(Bytes.toString(kv.getRow()) + "\t" + Bytes.toString(kv.getFamily()) + "\t" + Bytes.toString(kv.getQualifier()) + "\t" + Bytes.toString(kv.getValue()) + "\t" + kv.getTimestamp());
    }
    public static void printCell(Cell cell) {
        System.out.println(Bytes.toString(cell.getRow()) + "\t" + Bytes.toString(cell.getFamily()) + "\t" + Bytes.toString(cell.getQualifier()) + "\t" + Bytes.toString(cell.getValue()) + "\t" + cell.getTimestamp());
    }
    //建立表
    @Override
    public void createTable(String tableName, String[] family) throws Exception {
        HBaseAdmin admin=new HBaseAdmin(conf);
        HTableDescriptor desc =new HTableDescriptor(tableName);
         
        for(int i=0;i<family.length;i++){
            desc.addFamily(new HColumnDescriptor(family[i]));
            System.out.println("11111111111"+family[i]);
        }
        if(admin.tableExists(tableName)){
            System.out.println("表已經存在,別瞎輸行嗎");
//          System.exit(0);
        }else{
            admin.createTable(desc);
            System.out.println("表建立成功");
        }
    }
    //建立表
    @Override
    public void createTable(String tableName, HTableDescriptor htds) throws Exception {
        HBaseAdmin admin=new HBaseAdmin(conf);
        boolean tableExists1 = admin.tableExists(Bytes.toBytes(tableName));
        System.out.println(tableExists1 ? "表已存在" : "表不存在");
        admin.createTable(htds);
        boolean tableExists = admin.tableExists(Bytes.toBytes(tableName));
        System.out.println(tableExists ? "建立表成功" : "建立失敗");
    }
     
    @Override
    public void descTable(String tableName) throws Exception {
        HBaseAdmin admin=new HBaseAdmin(conf);
        HTable table=new HTable(conf, tableName);
        HTableDescriptor desc =table.getTableDescriptor();
        HColumnDescriptor[] columnFamilies = desc.getColumnFamilies();
     
        for(HColumnDescriptor t:columnFamilies){
            System.out.println(Bytes.toString(t.getName()));
        }
         
    }
    //// 這種方式是替換該表tableName的全部列簇
    @Override
    public void modifyTable(String tableName) throws Exception {
        HBaseAdmin admin=new HBaseAdmin(conf);
        HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
        htd.addFamily(new HColumnDescriptor(Bytes.toBytes("cf3")));
        htd.addFamily(new HColumnDescriptor(Bytes.toBytes("cf2")));
        admin.modifyTable(tableName, htd);
        // 刪除該表tableName當中的特定的列簇
        // admin.deleteColumn(tableName, "cf3");
        System.out.println("修改爲功");
         
    }
    @Override
    public void getAllTables() throws Exception {
        HBaseAdmin admin =new HBaseAdmin(conf);
         
        String[] tableNames = admin.getTableNames();
        for(int i=0;i<tableNames.length;i++){
            System.out.println(tableNames[i]);
        }
    }
    //更新數據  插入數據
    @Override
    public void putData(String tableName, String rowKey, String familyName, String columnName, String value)
            throws Exception {
        HTable htable=new HTable(conf, Bytes.toBytes(tableName));
        Put put=new Put(Bytes.toBytes(rowKey));
        put.add(Bytes.toBytes(familyName), Bytes.toBytes(columnName), Bytes.toBytes(value));
        htable.put(put);
         
    }
    //爲表添加數據
    @Override
    public void addData(String tableName, String rowKey, String[] column1, String[] value1, String[] column2,
            String[] value2) throws Exception {
         
        Put put=new Put(Bytes.toBytes(rowKey));
        HTable htable=new HTable(conf, Bytes.toBytes(tableName));
        HColumnDescriptor[] columnFamilies = htable.getTableDescriptor().getColumnFamilies();
        for(int i=0;i<=columnFamilies.length;i++){
            String nameAsString = columnFamilies[i].getNameAsString();
            if(nameAsString.equals("lie01")){
                for(int j=0;j<column1.length;j++){
                    put.add(Bytes.toBytes(nameAsString), Bytes.toBytes(column1[j]),Bytes.toBytes(value1[j]));
                }
            }
            if(nameAsString.equals("lie02")){
                for(int j=0;j<column2.length;j++){
                    put.add(Bytes.toBytes(nameAsString), Bytes.toBytes(column2[j]),Bytes.toBytes(value2[j]));
                }
            }
             
        }
        htable.put(put);
        System.out.println("addData ok!");
    }
    //根據rowkey 查詢
    @Override
    public Result getResult(String tableName, String rowKey) throws Exception {
        Get get=new Get(Bytes.toBytes(rowKey));
        HTable htable=new HTable(conf, Bytes.toBytes(tableName));
        Result result=htable.get(get);
//      for(KeyValue k:result.list()){
//          System.out.println(Bytes.toString(k.getFamily()));
//          System.out.println(Bytes.toString(k.getQualifier()));
//          System.out.println(Bytes.toString(k.getValue()));
//          System.out.println(k.getTimestamp());
//      }
        return result;
    }
    //查詢指定的某列
    @Override
    public Result getResult(String tableName, String rowKey, String familyName, String columnName) throws Exception {
        Get get=new Get(Bytes.toBytes(rowKey));
        HTable htable=new HTable(conf, Bytes.toBytes(tableName));
        get.addColumn(Bytes.toBytes(familyName),Bytes.toBytes(columnName));
        Result result=htable.get(get);
        for(KeyValue k:result.list()){
            System.out.println(Bytes.toString(k.getFamily()));
            System.out.println(Bytes.toString(k.getQualifier()));
            System.out.println(Bytes.toString(k.getValue()));
            System.out.println(k.getTimestamp());
        }
        return result;
    }
     
    //遍歷查詢表
    @Override
    public ResultScanner getResultScann(String tableName) throws Exception {
     
        Scan scan=new Scan();
        ResultScanner rs =null;
        HTable htable=new HTable(conf, tableName);
        try{
            rs=htable.getScanner(scan);
            for(Result r: rs){
                for(KeyValue kv:r.list()){
     
                    System.out.println(Bytes.toString(kv.getRow()));
                    System.out.println(Bytes.toString(kv.getFamily()));
                    System.out.println(Bytes.toString(kv.getQualifier()));
                    System.out.println(Bytes.toString(kv.getValue()));
                    System.out.println(kv.getTimestamp());
                }
            }
        }finally{
            rs.close();
        }
        return rs;
    }
    @Override
    public ResultScanner getResultScann(String tableName, Scan scan) throws Exception {
         
        ResultScanner rs =null;
        HTable htable=new HTable(conf, tableName);
        try{
            rs=htable.getScanner(scan);
            for(Result r: rs){
                for(KeyValue kv:r.list()){
     
                    System.out.println(Bytes.toString(kv.getRow()));
                    System.out.println(Bytes.toString(kv.getFamily()));
                    System.out.println(Bytes.toString(kv.getQualifier()));
                    System.out.println(Bytes.toString(kv.getValue()));
                    System.out.println(kv.getTimestamp());
                }
            }
        }finally{
            rs.close();
        }       
        return rs;
    }
    //查詢表中的某一列
    @Override
    public Result getResultByColumn(String tableName, String rowKey, String familyName, String columnName)
            throws Exception {
         
         
        HTable htable=new HTable(conf, tableName);
        Get get=new Get(Bytes.toBytes(rowKey));
        get.addColumn(Bytes.toBytes(familyName),Bytes.toBytes(columnName));
        Result result=htable.get(get);
        for(KeyValue kv: result.list()){
            System.out.println(Bytes.toString(kv.getFamily()));
            System.out.println(Bytes.toString(kv.getQualifier()));
            System.out.println(Bytes.toString(kv.getValue()));
            System.out.println(kv.getTimestamp());
             
        }
        return result;
    }
     
    //查詢某列數據的某個版本
    @Override
    public Result getResultByVersion(String tableName, String rowKey, String familyName, String columnName,
            int versions) throws Exception {
     
        HTable htable=new HTable(conf, tableName);
        Get get =new Get(Bytes.toBytes(rowKey));
        get.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName));
        get.setMaxVersions(versions);
        Result result=htable.get(get);
         
        for(KeyValue kv: result.list()){
            System.out.println(Bytes.toString(kv.getFamily()));
            System.out.println(Bytes.toString(kv.getQualifier()));
            System.out.println(Bytes.toString(kv.getValue()));
            System.out.println(kv.getTimestamp());
             
        }
        return result;
    }
    //刪除指定某列
    @Override
    public void deleteColumn(String tableName, String rowKey, String falilyName, String columnName) throws Exception {
        HTable htable=new HTable(conf, tableName);
//      Delete delete1=new Delete(Bytes.toBytes(rowKey));
        Delete de =new Delete(Bytes.toBytes(rowKey));
        de.deleteColumn(Bytes.toBytes(falilyName), Bytes.toBytes(columnName));
        htable.delete(de);
    }
     
    //刪除指定的某個rowkey
    @Override
    public void deleteColumn(String tableName, String rowKey) throws Exception {
        HTable htable=new HTable(conf, tableName);
        Delete de =new Delete(Bytes.toBytes(rowKey));
         htable.delete(de);
         
    }
    //讓該表失效
    @Override
    public void disableTable(String tableName) throws Exception {
        HBaseAdmin admin=new HBaseAdmin(conf);
        admin.disableTable(tableName);
         
    }
    //刪除表
    @Override
    public void dropTable(String tableName) throws Exception {
         
        HBaseAdmin admin=new HBaseAdmin(conf);
        admin.disableTable(tableName);
        admin.deleteTable(tableName);
         
    }
}
package com.ghgj.hbase.test1610;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
public class HBaseAPIDemo1610 implements HBaseDemoInterface {
    private static final String ROWKEY = "p001";
    private static final String ROWKEY2 = "p002";
    private static final String FAMILY1 = "cf1";
    private static final String FAMILY2 = "cf2";
    private static final String KEY = "name";
    private static final String VALUE = "huangbo";
    private static final String TABLE_NAME = "person";
    private static final String[] COLUMN_FAMILY = new String[] { FAMILY1, FAMILY2 };
    static Configuration conf = null;
    static HBaseAdmin admin = null;
    static HTable table = null;
    static {
        try {
            conf = HBaseConfiguration.create();
            conf.set("hbase.zookeeper.quorum", "hadoop03:2181,hadoop04:2181,hadoop05:2181");
            admin = new HBaseAdmin(conf);
            table = new HTable(conf, TABLE_NAME);
        } catch (IOException e) {
            // e.printStackTrace();
            System.out.println("報錯");
        }
    }
    public static void main(String[] args) throws Exception {
        HBaseAPIDemo1610 hbase = new HBaseAPIDemo1610();
        // 測試建立表
        hbase.createTable(TABLE_NAME, COLUMN_FAMILY);
        // 測試建立表
        HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
        for (int i = 0; i < COLUMN_FAMILY.length; i++) {
            HColumnDescriptor cf1 = new HColumnDescriptor(COLUMN_FAMILY[i]);
            htd.addFamily(cf1);
        }
        hbase.createTable(TABLE_NAME, htd);
        // 查看錶屬性
        hbase.descTable(TABLE_NAME);
        // 查詢全部的表
        hbase.getAllTables();
        // 測試修改表
        hbase.modifyTable(TABLE_NAME);
        // 插入數據
        hbase.putData(TABLE_NAME, ROWKEY, FAMILY1, KEY, VALUE);
        // 測試插入一堆數據
        String[] column1 = new String[] { "name1", "age", "province" };
        String[] value1 = new String[] { "huangbo", "33", "xinjiang" };
        String[] column2 = new String[] { "gender" };
        String[] value2 = new String[] { "male" };
        hbase.addData(TABLE_NAME, ROWKEY2, column1, value1, column2, value2);
        // 經過rowkey查詢數據
        Result result = hbase.getResult(TABLE_NAME, ROWKEY2);
        System.out.println(result.toString());
        List<KeyValue> list = result.list();
        for (int i = 0; i < list.size(); i++) {
            KeyValue kv = list.get(i);
            printKeyValye(kv);
        }
        // 經過rowkey, family, province查詢數據
        Result result1 = hbase.getResult(TABLE_NAME, ROWKEY2, FAMILY1, "province");
        List<Cell> cells = result1.listCells();
        for (int i = 0; i < cells.size(); i++) {
            Cell cell = cells.get(i);
            printCell(cell);
        }
        // 掃描全表數據
        ResultScanner resultScann = hbase.getResultScann(TABLE_NAME);
        printResultScanner(resultScann);
         
        /*Iterator<Result> iterator = resultScann.iterator();
        while(iterator.hasNext()){
            Result next = iterator.next();
        }*/
        // 經過scan掃描全表數據,scan中能夠加入一些過濾條件
        Scan scan = new Scan();
        scan.setStartRow(Bytes.toBytes("user"));
        scan.setStopRow(Bytes.toBytes("zk002"));
        scan.setTimeRange(1488252774189l, 1488252774191l);
        ResultScanner resultScann1 = hbase.getResultScann(TABLE_NAME, scan);
        printResultScanner(resultScann1);
        // 兩種方式查詢最大版本數的hbase數據
        Result resultByVersion = hbase.getResultByVersion(TABLE_NAME, ROWKEY, FAMILY1, "name", 3);
        printResult(resultByVersion);
        System.out.println("-------------------");
        ResultScanner rs = hbase.getResultByVersion(ROWKEY, FAMILY1, "name", 3);
        printResultScanner(rs);
        // 刪除表
        hbase.dropTable(TABLE_NAME);
    }
    public static void printResultScanner(ResultScanner resultScann) {
        for (Result result : resultScann) {
            printResult(result);
        }
    }
    public static void printResult(Result result) {
        List<Cell> cells = result.listCells();
        for (int i = 0; i < cells.size(); i++) {
            Cell cell = cells.get(i);
            printCell(cell);
        }
    }
    public static void printCell(Cell cell) {
        System.out.println(Bytes.toString(cell.getRow()) + "\t" + Bytes.toString(cell.getFamily()) + "\t" + Bytes.toString(cell.getQualifier()) + "\t" + Bytes.toString(cell.getValue()) + "\t" + cell.getTimestamp());
    }
    public static void printKeyValye(KeyValue kv) {
        System.out.println(Bytes.toString(kv.getRow()) + "\t" + Bytes.toString(kv.getFamily()) + "\t" + Bytes.toString(kv.getQualifier()) + "\t" + Bytes.toString(kv.getValue()) + "\t" + kv.getTimestamp());
    }
    // create 'tablename','cf1','cf2'
    @Override
    public void createTable(String tableName, String[] family) throws Exception {
        HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
        for (int i = 0; i < family.length; i++) {
            HColumnDescriptor cf1 = new HColumnDescriptor(family[i]);
            htd.addFamily(cf1);
        }
        admin.createTable(htd);
        boolean tableExists = admin.tableExists(Bytes.toBytes(tableName));
        System.out.println(tableExists ? "建立表成功" : "建立失敗");
    }
    @Override
    public void createTable(String tableName, HTableDescriptor htd) throws Exception {
        admin.createTable(htd);
        boolean tableExists = admin.tableExists(Bytes.toBytes(tableName));
        System.out.println(tableExists ? "建立表成功" : "建立失敗");
    }
    // desc 'person'
    @Override
    public void descTable(String tableName) throws Exception {
        HTableDescriptor tableDescriptor = table.getTableDescriptor();
        HColumnDescriptor[] columnFamilies = tableDescriptor.getColumnFamilies();
        for (HColumnDescriptor hcd : columnFamilies) {
            // System.out.println(hcd.toString()+"\t");
            System.out.println(Bytes.toString(hcd.getName()));
        }
    }
    @Override
    public void modifyTable(String tableName) throws Exception {
        // 這種方式是替換該表tableName的全部列簇
        HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
        htd.addFamily(new HColumnDescriptor(Bytes.toBytes("cf3")));
        htd.addFamily(new HColumnDescriptor(Bytes.toBytes("cf2")));
        admin.modifyTable(tableName, htd);
        // 刪除該表tableName當中的特定的列簇
        // admin.deleteColumn(tableName, "cf3");
        System.out.println("修改爲功");
    }
    // list
    @Override
    public void getAllTables() throws Exception {
        TableName[] listTableNames = admin.listTableNames();
        for (TableName tn : listTableNames) {
            System.out.println(tn.toString());
        }
    }
    // put 'tablename','rowkey','familyname:key','value'
    @Override
    public void putData(String tableName, String rowKey, String familyName, String columnName, String value) throws Exception {
        // HTable table = new HTable(conf, tableName);
        Put put = new Put(Bytes.toBytes(rowKey));
        put.add(Bytes.toBytes(familyName), Bytes.toBytes(columnName), Bytes.toBytes(value));
        table.put(put);
        System.out.println("插入成功");
    }
    /**
     * @param tableName
     *            表名
     * @param rowKey
     *            rowkey
     * @param column1
     *            第一個列簇的key數組
     * @param value1
     *            第一個列簇的value數組,key數組和value數組長度必須同樣
     * @param column2
     *            第二列簇的key數組
     * @param value2
     *            第二個列簇的values數組, 同上同理
     * @throws Exception
     */
    @Override
    public void addData(String tableName, String rowKey, String[] column1, String[] value1, String[] column2, String[] value2) throws Exception {
        List<Put> puts = new ArrayList<Put>();
        for (int i = 0; i < column1.length; i++) {
            Put put = new Put(Bytes.toBytes(rowKey));
            put.add(Bytes.toBytes(FAMILY1), Bytes.toBytes(column1[i]), Bytes.toBytes(value1[i]));
            puts.add(put);
        }
        for (int i = 0; i < column2.length; i++) {
            Put put = new Put(Bytes.toBytes(rowKey));
            put.add(Bytes.toBytes(FAMILY2), Bytes.toBytes(column2[i]), Bytes.toBytes(value2[i]));
            puts.add(put);
        }
        table.put(puts);
        System.out.println("插入一堆數據成功");
    }
    // get 'tablename','rowkey'
    @Override
    public Result getResult(String tableName, String rowKey) throws Exception {
        Get get = new Get(Bytes.toBytes(rowKey));
        Result result = table.get(get);
        return result;
    }
    @Override
    public Result getResult(String tableName, String rowKey, String familyName, String columnName) throws Exception {
        Get get = new Get(Bytes.toBytes(rowKey));
        get.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName));
        Result result = table.get(get);
        return result;
    }
    @Override
    public ResultScanner getResultScann(String tableName) throws Exception {
        Scan scan = new Scan();
        ResultScanner scanner = table.getScanner(scan);
        // ResultScanner scanner = table.getScanner(Bytes.toBytes(FAMILY2));
        // ResultScanner scanner = table.getScanner(Bytes.toBytes(FAMILY1),
        // Bytes.toBytes("name1"));
        return scanner;
    }
    @Override
    public ResultScanner getResultScann(String tableName, Scan scan) throws Exception {
        return table.getScanner(scan);
    }
    @Override
    public Result getResultByColumn(String tableName, String rowKey, String familyName, String columnName) throws Exception {
        return null;
    }
    // get 'person','p001',{COLUMNS => 'cf1:name', VERSIONS => 3}
    @Override
    public Result getResultByVersion(String tableName, String rowKey, String familyName, String columnName, int versions) throws Exception {
        Get get = new Get(Bytes.toBytes(rowKey));
        get.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName));
        get.setMaxVersions(versions);
        Result result = table.get(get);
        return result;
    }
    public ResultScanner getResultByVersion(String rowKey, String familyName, String columnName, int versions) throws Exception {
        Scan scan = new Scan(Bytes.toBytes(rowKey), Bytes.toBytes(rowKey));
        scan.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName));
        scan.setMaxVersions(versions);
        ResultScanner scanner = table.getScanner(scan);
        return scanner;
    }
    @Override
    public void deleteColumn(String tableName, String rowKey, String falilyName, String columnName) throws Exception {
    }
    @Override
    public void deleteColumn(String tableName, String rowKey) throws Exception {
    }
    @Override
    public void disableTable(String tableName) throws Exception {
        admin.disableTable(tableName);
    }
    @Override
    public void dropTable(String tableName) throws Exception {
        try {
            admin.deleteTable(tableName);
        } catch (Exception e) {
            // e.printStackTrace();
            disableTable(tableName);
            admin.deleteTable(tableName);
            System.out.println("ssssssss");
        } finally {
            boolean tableExists = admin.tableExists(Bytes.toBytes(tableName));
            System.out.println(tableExists ? "刪除失敗" : "刪除成功");
        }
    }
}

 

 
 分區優化
  • 集羣若是內存較大就不使用交換分區,若是內存不大可使用部分交換分區
    • 若是須要關閉內存空間將改值設置爲0
    • Sysctl  -w vm.swappiness=0
    • 若是內存空間不太充足的話,能夠將該參數調小
    • Sysctl  -w vm.swappiness=5
    • 也能夠在Linux系統中手動釋放交換空間命令:
    • Linux swapoff命令用於關閉系統交換區(swap area), -a表明all
    • swapoff -a   所有關閉交換空間
    • swapon -a   所有開啓交換空間
    • 完成交換空間釋放
參數優化
   服務端
  • hbase.regionserver.handler.count:rpc請求的線程數量,默認值是10,生產環境建議使用100,也不是越大越好,特別是當請求內容很大的時候,好比scan/put幾M的數據,會佔用過多的內存,有可能致使頻繁的GC,甚至出現內存溢出。
  • hbase.master.distributed.log.splitting:默認值爲true,建議設爲false。關閉hbase的分佈式日誌切割,在log須要replay時,由master來負責重放
  • hbase.regionserver.hlog.splitlog.writer.threads:默認值是3,建議設爲10,日誌切割所用的線程數
  • hbase.snapshot.enabled:快照功能,默認是false(不開啓),建議設爲true,特別是對某些關鍵的表,定時用快照作備份是一個不錯的選擇。
  • hbase.hregion.max.filesize:默認是10G, 若是任何一個column familiy裏的StoreFile超過這個值, 那麼這個Region會一分爲二,由於region分裂會有短暫的region下線時間(一般在5s之內),爲減小對業務端的影響,建議手動定時分裂,能夠設置爲60G。
  • hbase.hregion.majorcompaction:hbase的region主合併的間隔時間,默認爲1天,建議設置爲0,禁止自動的major主合併,major合併會把一個store下全部的storefile重寫爲一個storefile文件,在合併過程當中還會把有刪除標識的數據刪除,在生產集羣中,主合併能持續數小時之久,爲減小對業務的影響,建議在業務低峯期進行手動或者經過腳本或者api按期進行major合併。
  • hbase.hregion.memstore.flush.size:默認值128M,單位字節,一旦有memstore超過該值將被flush,若是regionserver的jvm內存比較充足(16G以上),能夠調整爲256M。
  • hbase.hregion.memstore.block.multiplier:默認值2,若是一個memstore的內存大小已經超過hbase.hregion.memstore.flush.size *  hbase.hregion.memstore.block.multiplier,則會阻塞該memstore的寫操做,爲避免阻塞,建議設置爲5,若是太大,則會有OOM的風險。若是在regionserver日誌中出現"Blocking updates for '<threadName>' on region <regionName> : memstore size <多少M> is >= than blocking <多少M> size"的信息時,說明這個值該調整了。
  • hbase.hstore.compaction.min:默認值爲3,若是任何一個store裏的storefile總數超過該值,會觸發默認的合併操做,能夠設置5~8,在手動的按期major compact中進行storefile文件的合併,減小合併的次數,不過這會延長合併的時間,之前的對應參數爲 hbase.hstore.compactionThreshold
  • hbase.hstore.compaction.max:默認值爲10,一次最多合併多少個storefile,避免OOM。
  • hbase.hstore.blockingStoreFiles:默認爲7,若是任何一個store(非.META.表裏的store)的storefile的文件數大於該值,則在flush memstore前先進行split或者compact,同時把該region添加到flushQueue,延時刷新,這期間會阻塞寫操做直到compact完成或者超過hbase.hstore.blockingWaitTime(默認90s)配置的時間,能夠設置爲30,避免memstore不及時flush。當regionserver運行日誌中出現大量的「Region <regionName> has too many store files; delaying flush up to 90000ms"時,說明這個值須要調整了
  • hbase.regionserver.global.memstore.upperLimit:默認值0.4,regionserver全部memstore佔用內存在總內存中的upper比例,當達到該值,則會從整個regionserver中找出最須要flush的region進行flush,直到總內存比例降到該數如下,採用默認值便可。
  • hbase.regionserver.global.memstore.lowerLimit:默認值0.35,採用默認值便可。
  • hbase.regionserver.thread.compaction.small:默認值爲1,regionserver作Minor Compaction時線程池裏線程數目,能夠設置爲5。
  • hbase.regionserver.thread.compaction.large:默認值爲1,regionserver作Major Compaction時線程池裏線程數目,能夠設置爲8。
  • hbase.regionserver.lease.period:默認值60000(60s),客戶端鏈接regionserver的租約超時時間,客戶端必須在這個時間內彙報,不然則認爲客戶端已死掉。這個最好根據實際業務狀況進行調整
  • hfile.block.cache.size:默認值0.25,regionserver的block cache的內存大小限制,在偏向讀的業務中,能夠適當調大該值,須要注意的是hbase.regionserver.global.memstore.upperLimit的值和hfile.block.cache.size的值之和必須小於0.8。
  • dfs.socket.timeout:默認值60000(60s),建議根據實際regionserver的日誌監控發現了異常進行合理的設置,好比咱們設爲900000,這個參數的修改須要同時更改hdfs-site.xml
  • dfs.datanode.socket.write.timeout:默認480000(480s),有時regionserver作合併時,可能會出現datanode寫超時的狀況,480000 millis timeout while waiting for channel to be ready for write,這個參數的修改須要同時更改hdfs-site.xml
  • jvm和垃圾收集參數:
  • export HBASE_REGIONSERVER_OPTS="-Xms36g -Xmx36g -Xmn1g -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=15 -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/logs/gc-$(hostname)-hbase.log"因爲咱們服務器內存較大(96G),咱們給一部分regionserver的jvm內存開到64G,到如今爲止,尚未發生過一次full gc,hbase在內存使用控制方面確實下了很多功夫,好比各類blockcache的實現,細心的同窗能夠看源碼。、
Client端
  • hbase.client.write.buffer:默認爲2M,寫緩存大小,推薦設置爲5M,單位是字節,固然越大佔用的內存越多,此外測試過設爲10M下的入庫性能,反而沒有5M好。
  • hbase.client.pause:默認是1000(1s),若是你但願低延時的讀或者寫,建議設爲200,這個值一般用於失敗重試,region尋找等。
  • hbase.client.retries.number:默認值是10,客戶端最多重試次數,能夠設爲11,結合上面的參數,共重試時間71s。
  • hbase.ipc.client.tcpnodelay:默認是false,建議設爲true,關閉消息緩衝。
  • hbase.client.scanner.caching:scan緩存,默認爲1,避免佔用過多的client和rs的內存,通常1000之內合理,若是一條數據太大,則應該設置一個較小的值,一般是設置業務需求的一次查詢的數據條數。
  • 若是是掃描數據對下次查詢沒有幫助,則能夠設置scan的setCacheBlocks爲false,避免使用緩存。
  • table用完需關閉,關閉scanner。
  • 限定掃描範圍:指定列簇或者指定要查詢的列,指定startRow和endRow。
  • 使用Filter可大量減小網絡消耗。
  • 經過Java多線程入庫和查詢,並控制超時時間。後面會共享下個人hbase單機多線程入庫的代碼。
  • 建表注意事項:開啓壓縮合理的設計rowkey進行預分區開啓bloomfilter 。
ZooKeeper調優
  • 1.zookeeper.session.timeout:默認值3分鐘,不可配置過短,避免session超時,hbase中止服務,線上生產環境因爲配置爲1分鐘,若是太長,當regionserver掛掉,zk還得等待這個超時時間(已有patch修復),從而致使master不能及時對region進行遷移。
  • 2.zookeeper數量:建議5個或者7個節點。給每一個zookeeper 4G左右的內存,最好有獨立的磁盤。3.hbase.zookeeper.property.maxClientCnxns:zk的最大鏈接數,默認爲300,無需調整。
  • 4.設置操做系統的swappiness爲0,則在物理內存不夠的狀況下才會使用交換分區,避免GC回收時會花費更多的時間,當超過zk的session超時時間則會出現regionserver宕機的誤報。
HDFS調優
  • 1.dfs.name.dir:namenode的數據存放地址,能夠配置多個,位於不一樣的磁盤並配置一個nfs遠程文件系統,這樣namenode的數據能夠有多個備份
  • 2.dfs.namenode.handler.count:namenode節點RPC的處理線程數,默認爲10,能夠設置爲60
  • 3.dfs.datanode.handler.count:datanode節點RPC的處理線程數,默認爲3,能夠設置爲30
  • 4.dfs.datanode.max.xcievers:datanode同時處理文件的上限,默認爲256,能夠設置爲8192
其餘
  • 列族名、column名、rowkey均會存儲到hfile中,所以這幾項在設計表結構時都儘可能短些。
  • regionserver的region數量不要過1000,過多的region會致使產生不少memstore,可能會致使內存溢出,也會增長major compact的耗時。
相關文章
相關標籤/搜索