自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

圖文詳解:內(nèi)存總是不夠,我靠HBase說服了Leader為新項目保駕護航

數(shù)據(jù)庫 其他數(shù)據(jù)庫
最近在工作中用到了 Hbase 這個數(shù)據(jù)庫,也順便做了關于 Hbase 的知識記錄來分享給大家。其實 Hbase的內(nèi)容體系真的很多很多,這里介紹的是小羽認為在工作中會用到的一些技術點,希望可以幫助到大家。

 最近在工作中用到了 Hbase 這個數(shù)據(jù)庫,也順便做了關于 Hbase 的知識記錄來分享給大家。其實 Hbase的內(nèi)容體系真的很多很多,這里介紹的是小羽認為在工作中會用到的一些技術點,希望可以幫助到大家。

可以這么說互聯(lián)網(wǎng)都是建立在形形色色的數(shù)據(jù)庫之上的,現(xiàn)在主流的數(shù)據(jù)庫有這么幾種:以 MySQL 為代表的關系型數(shù)據(jù)庫以及其分布式解決方案,以 Redis 為代表的緩存數(shù)據(jù)庫,以 ES 為代表的檢索數(shù)據(jù)庫,再就是分布式持久化 KV 數(shù)據(jù)庫。而在開源領域,尤其是國內(nèi),HBase 幾乎是分布式持久化KV數(shù)據(jù)庫的首選方案。HBase 應用的業(yè)務場景非常之多,比如用戶畫像、實時(離線)推薦、實時風控、社交Feed流、商品歷史訂單、社交聊天記錄、監(jiān)控系統(tǒng)以及用戶行為日志等等。

前言

我們每一個人無論使用什么科技產(chǎn)品,都會產(chǎn)生大量的數(shù)據(jù),而這些數(shù)據(jù)的存儲和查詢對于小型數(shù)據(jù)庫來說其實是很難滿足我們的需求的,因此出現(xiàn)了 HBase 分布式大數(shù)據(jù)。HBase 是一個構建在 Hadoop 文件系統(tǒng)之上的面向列的數(shù)據(jù)庫管理系統(tǒng)。HBase 是一種類似于 Google’s Big Table 的數(shù)據(jù)模型,它是 Hadoop 生態(tài)系統(tǒng)的一部分,它將數(shù)據(jù)存儲在 HDFS 上,客戶端可以通過 HBase 實現(xiàn)對 HDFS 上數(shù)據(jù)的隨機訪問。它主要有以下特性:

不支持復雜的事務,只支持行級事務,即單行數(shù)據(jù)的讀寫都是原子性的;

由于是采用 HDFS 作為底層存儲,所以和 HDFS 一樣,支持結構化、半結構化和非結構化的存儲;

支持通過增加機器進行橫向擴展;

支持數(shù)據(jù)分片;

支持 RegionServers 之間的自動故障轉移;

易于使用的 Java 客戶端 API;

支持 BlockCache 和布隆過濾器;

過濾器支持謂詞下推。

HBase 原理

概念

HBase 是分布式、面向列的開源數(shù)據(jù)庫(其實準確的說是面向列族)。HDFS 為 Hbase 提供可靠的底層數(shù)據(jù)存儲服務,MapReduce 為 Hbase 提供高性能的計算能力,Zookeeper 為 Hbase 提供穩(wěn)定服務和 Failover 機制,因此我們說 Hbase 是一個通過大量廉價的機器解決海量數(shù)據(jù)的高速存儲和讀取的分布式數(shù)據(jù)庫解決方案。

列式存儲

我們先來看一下之前的關系型數(shù)據(jù)庫的按行來存儲的。如下圖:


可以看到只有第一行 ID:1 小羽的這一行的數(shù)據(jù)都填了,小娜和小智的數(shù)據(jù)都沒有填完。在我們的行結構中,都是固定的,每一行都一樣,就算不填,也要空著,不能沒有。

來看一下使用了非關系型數(shù)據(jù)庫的按列存儲的效果圖:


可以看到之前小羽的一列數(shù)據(jù)對應到了小羽現(xiàn)在的一行數(shù)據(jù),原來小羽的七列數(shù)據(jù)變成了現(xiàn)在的七行。之前的七行數(shù)據(jù)在一行,共用過一個主鍵 ID:1 。在列式存儲里,變成了七行,每一行都有一個主鍵與其對應,也就是為什么小羽的主鍵 ID:1 重復了七次。這樣排列最大的好處就是,我們對于不需要的數(shù)據(jù)就不需要添加,會大大節(jié)省我們的空間資源。因為查詢中的選擇規(guī)則是通過列來定義的,整個數(shù)據(jù)庫是自動索引化的。

NoSQL和關系型數(shù)據(jù)庫對比

對比如下圖:


RDBMS 與 Hbase 對比

Hbase 是根據(jù)列族來存儲數(shù)據(jù)的。列族下面可以有非常多的列,列族在創(chuàng)建表的時候就必須指定。為了加深對 Hbase 列族的理解,下面是簡單的關系型數(shù)據(jù)庫的表和 Hbase 數(shù)據(jù)庫的表:


主要區(qū)別:


HBase 架構

Hbase 是由 Client、Zookeeper、Master、HRegionServer、HDFS 等幾個核心體系組成。


Client

Client 使用 HBase 的 RPC 機制與 HMaster、HRegionServer 進行通信。Client 與 HMaster 進行管理類通信,與 HRegion Server 進行數(shù)據(jù)操作類通信。

Zookeeper

Hbase 通過 Zookeeper 來做 master 的高可用、RegionServer 的監(jiān)控、元數(shù)據(jù)的入口以及集群配置的維護等工作。具體工作如下:

1. 通過 Zoopkeeper 來保證集群中只有 1 個 master 在運行,如果 master 異常,會通過競爭機制產(chǎn)生新的 master 提供服務

2. 通過 Zoopkeeper 來監(jiān)控 RegionServer 的狀態(tài),當 RegionSevrer 有異常的時候,通過回調(diào)的形式通知 Master RegionServer 上下限的信息

3. 通過 Zoopkeeper 存儲元數(shù)據(jù)的統(tǒng)一入口地址。

客戶端在使用 hbase 的時候,需要添加 zookeeper 的 ip 地址和節(jié)點路徑,建立起與zookeeper的連接,建立連接的方式如下面的代碼所示:

  1. Configuration configuration = HBaseConfiguration.create(); 
  2. configuration.set("hbase.zookeeper.quorum""XXXX.XXX.XXX"); 
  3. configuration.set("hbase.zookeeper.property.clientPort""2181"); 
  4. configuration.set("zookeeper.znode.parent""XXXXX"); 
  5. Connection connection = ConnectionFactory.createConnection(configuration); 

Hmaster

master 節(jié)點的主要職責如下:

1. 為 RegionServer 分配 Region

2. 維護整個集群的負載均衡

3. 維護集群的元數(shù)據(jù)信息,發(fā)現(xiàn)失效的 Region,并將失效的 Region 分配到正常RegionServer 上當 RegionSever 失效的時候,協(xié)調(diào)對應 Hlog 的拆分

HRegionServer

HRegionServer 內(nèi)部管理了一系列 HRegion 對象,每個 HRegion 對應 Table中的一個 ColumnFamily 的存儲,即一個 Store 管理一個 Region 上的一個列族(CF)。每個 Store 包含一個 MemStore 和 0 到多個 StoreFile。Store 是 HBase 的存儲核心,由 MemStore 和 StoreFile 組成。

HLog

數(shù)據(jù)在寫入時,首先寫入預寫日志(Write Ahead Log),每個 HRegionServer 服務的所有 Region 的寫操作日志都存儲在同一個日志文件中。數(shù)據(jù)并非直接寫入 HDFS,而是等緩存到一定數(shù)量再批量寫入,寫入完成后在日志中做標記。

MemStore

MemStore 是一個有序的內(nèi)存緩存區(qū),用戶寫入的數(shù)據(jù)首先放入 MemStore,當 MemStore 滿了以后 Flush 成一個 StoreFile(存儲時對應為 File),當 StoreFile 數(shù)量增到一定閥值,觸發(fā) Compact 合并,將多個 StoreFile 合并成一個 StoreFile。StoreFiles 合并后逐步形成越來越大的 StoreFile,當 Region 內(nèi)所有 StoreFiles(Hfile) 的總大小超過閥值(hbase.hregion.max.filesize)即觸發(fā)分裂 Split,把當前的 Region Split 分成 2 個 Region,父 Region 下線,新 Spilt 出的 2 個孩子 Region 被 HMaster 分配到合適的 HRegionServer 上,使得原先 1 個 Region 的壓力得以分流到 2 個 Region 上。

Region 尋址方式

通過 zookeeper.META,主要有以下幾步:

1. Client 請求 ZK 獲取.META.所在的 RegionServer 的地址。

2. Client 請求.META.所在的 RegionServer 獲取訪問數(shù)據(jù)所在的 RegionServer 地址,client 會將.META.的相關信息 cache 下來,以便下一次快速訪問。

3. Client 請求數(shù)據(jù)所在的 RegionServer,獲取所需要的數(shù)據(jù)。

HDFS

HDFS 為 Hbase 提供最終的底層數(shù)據(jù)存儲服務,同時為 Hbase 提供高可用(Hlog 存儲在HDFS)的支持。

HBase 組件

Column Family 列族

Column Family 又叫列族,Hbase 通過列族劃分數(shù)據(jù)的存儲,列族下面可以包含任意多的列,實現(xiàn)靈活的數(shù)據(jù)存取。Hbase 表的創(chuàng)建的時候就必須指定列族。就像關系型數(shù)據(jù)庫創(chuàng)建的時候必須指定具體的列是一樣的。Hbase 的列族不是越多越好,官方推薦的是列族最好小于或者等于 3。我們使用的場景一般是 1 個列族。

Rowkey

Rowkey 的概念和 mysql 中的主鍵是完全一樣的,Hbase 使用 Rowkey 來唯一的區(qū)分某一行的數(shù)據(jù)。Hbase 只支持 3 種查詢方式:基于 Rowkey 的單行查詢,基于 Rowkey 的范圍掃描,全表掃描。

Region 分區(qū)

Region:Region 的概念和關系型數(shù)據(jù)庫的分區(qū)或者分片差不多。Hbase 會將一個大表的數(shù)據(jù)基于 Rowkey 的不同范圍分配到不同的 Region 中,每個 Region 負責一定范圍的數(shù)據(jù)訪問和存儲。這樣即使是一張巨大的表,由于被切割到不同的 region,訪問起來的時延也很低。


TimeStamp 多版本

TimeStamp 是實現(xiàn) Hbase 多版本的關鍵。在 Hbase 中使用不同的 timestame 來標識相同 rowkey 行對應的不同版本的數(shù)據(jù)。在寫入數(shù)據(jù)的時候,如果用戶沒有指定對應的 timestamp,Hbase 會自動添加一個 timestamp,timestamp 和服務器時間保持一致。在Hbase 中,相同 rowkey 的數(shù)據(jù)按照 timestamp 倒序排列。默認查詢的是最新的版本,用戶可通過指定 timestamp 的值來讀取舊版本的數(shù)據(jù)。

Hbase 寫邏輯

Hbase 寫入流程

主要有三個步驟:

1. Client 獲取數(shù)據(jù)寫入的 Region 所在的 RegionServer

2. 請求寫 Hlog, Hlog 存儲在 HDFS,當 RegionServer 出現(xiàn)異常,需要使用 Hlog 來恢復數(shù)據(jù)。

3. 請求寫 MemStore,只有當寫 Hlog 和寫 MemStore 都成功了才算請求寫入完成。MemStore 后續(xù)會逐漸刷到 HDFS 中。


MemStore 刷盤

為了提高 Hbase 的寫入性能,當寫請求寫入 MemStore 后,不會立即刷盤。而是會等到一定的時候進行刷盤的操作。具體是哪些場景會觸發(fā)刷盤的操作呢?總結成如下的幾個場景:

1. 這個全局的參數(shù)是控制內(nèi)存整體的使用情況,當所有 memstore 占整個 heap 的最大比例的時候,會觸發(fā)刷盤的操作。這個參數(shù)是hbase.regionserver.global.memstore.upperLimit,默認為整個 heap 內(nèi)存的 40%。但這并不意味著全局內(nèi)存觸發(fā)的刷盤操作會將所有的 MemStore 都進行輸盤,而是通過另外一個參數(shù) hbase.regionserver.global.memstore.lowerLimit 來控制,默認是整個 heap 內(nèi)存的 35%。當 flush 到所有 memstore 占整個 heap 內(nèi)存的比率為35%的時候,就停止刷盤。這么做主要是為了減少刷盤對業(yè)務帶來的影響,實現(xiàn)平滑系統(tǒng)負載的目的。

2. 當 MemStore 的大小達到 hbase.hregion.memstore.flush.size 大小的時候會觸發(fā)刷盤,默認 128M 大小

3. 前面說到 Hlog 為了保證 Hbase 數(shù)據(jù)的一致性,那么如果 Hlog 太多的話,會導致故障恢復的時間太長,因此 Hbase 會對 Hlog 的最大個數(shù)做限制。當達到 Hlog 的最大個數(shù)的時候,會強制刷盤。這個參數(shù)是 hase.regionserver.max.logs,默認是 32 個。

4. 可以通過 hbase shell 或者 java api 手工觸發(fā) flush 的操作。

5. 在正常關閉 RegionServer 會觸發(fā)刷盤的操作,全部數(shù)據(jù)刷盤后就不需要再使用 Hlog 恢復數(shù)據(jù)。

6. 當 RegionServer 出現(xiàn)故障的時候,其上面的 Region 會遷移到其他正常的 RegionServer 上,在恢復完 Region 的數(shù)據(jù)后,會觸發(fā)刷盤,當刷盤完成后才會提供給業(yè)務訪問。

HBase 中間層

Phoenix 是 HBase 的開源 SQL 中間層,它允許你使用標準 JDBC 的方式來操作 HBase 上的數(shù)據(jù)。在 Phoenix 之前,如果你要訪問 HBase,只能調(diào)用它的 Java API,但相比于使用一行 SQL 就能實現(xiàn)數(shù)據(jù)查詢,HBase 的 API 還是過于復雜。Phoenix 的理念是 we put sql SQL back in NOSQL,即你可以使用標準的 SQL 就能完成對 HBase 上數(shù)據(jù)的操作。同時這也意味著你可以通過集成 Spring Data JPA 或 Mybatis 等常用的持久層框架來操作 HBase。

其次 Phoenix 的性能表現(xiàn)也非常優(yōu)異,Phoenix 查詢引擎會將 SQL 查詢轉換為一個或多個 HBase Scan,通過并行執(zhí)行來生成標準的 JDBC 結果集。它通過直接使用 HBase API 以及協(xié)處理器和自定義過濾器,可以為小型數(shù)據(jù)查詢提供毫秒級的性能,為千萬行數(shù)據(jù)的查詢提供秒級的性能。同時 Phoenix 還擁有二級索引等 HBase 不具備的特性,因為以上的優(yōu)點,所以 Phoenix 成為了 HBase 最優(yōu)秀的 SQL 中間層。

HBase 安裝使用

下載 HBase 壓縮包,首先解壓

  1. tar -zxvf hbase-0.98.6-hadoop2-bin.tar.gz 

打開 hbase-env.sh 文件配置 JAVA_HOME:

  1. export JAVA_HOME=/opt/modules/jdk1.7.0_79 

配置 hbase-site.xml:

  1. <configuration> 
  2.  <property> 
  3.     <name>hbase.rootdir</name
  4.     <value>hdfs://hadoop-senior.shinelon.com:8020/hbase</value> 
  5.   </property> 
  6.   <property> 
  7.     <name>hbase.cluster.distributed</name
  8.     <value>true</value> 
  9.   </property> 
  10.   <property> 
  11.     <name>hbase.zookeeper.quorum</name
  12.     <value>hadoop-senior.shinelon.com</value> 
  13.   </property> 
  14. </configuration> 

將上面的主機名換為自己的主機名,就可以啟動HBase了。Web 頁面訪問如下:

 

HBase 命令

下面是小羽整理的一些關于 Hbase 的經(jīng)常會使用到的命令:


HBase API 使用

API 如下

  1. package com.initialize; 
  2. import org.apache.hadoop.conf.Configuration; 
  3. import org.apache.hadoop.hbase.HBaseConfiguration; 
  4. import org.apache.hadoop.hbase.HColumnDescriptor; 
  5. import org.apache.hadoop.hbase.HTableDescriptor; 
  6. import org.apache.hadoop.hbase.TableName; 
  7. import org.apache.hadoop.hbase.client.Admin; 
  8. import org.apache.hadoop.hbase.client.Connection
  9. import org.apache.hadoop.hbase.client.ConnectionFactory; 
  10. import org.apache.hadoop.hbase.regionserver.BloomType; 
  11. import org.junit.Before; 
  12. import org.junit.Test; 
  13. import java.io.IOException; 
  14. /** 
  15.  *   
  16.  *  1、構建連接 
  17.  *  2、從連接中取到一個表DDL操作工具admin 
  18.  *  3、admin.createTable(表描述對象); 
  19.  *  4、admin.disableTable(表名); 
  20.  * 5、admin.deleteTable(表名); 
  21.  * 6、admin.modifyTable(表名,表描述對象);  
  22.  * 
  23.  */ 
  24. public class HbaseClientDemo { 
  25.  
  26.     Connection conn = null
  27.  
  28.     @Before 
  29.     public void getConn() throws IOException { 
  30.         //構建一個連接對象 
  31.         Configuration conf = HBaseConfiguration.create();//會自動加載hbase-site.xml 
  32.         conf.set("hbase.zookeeper.quorum","n1:2181,n2:2181,n3:2181"); 
  33.  
  34.         conn = ConnectionFactory.createConnection(conf); 
  35.     } 
  36.  
  37.     /** 
  38.      * DDL 
  39.      * 創(chuàng)建表 
  40.      */ 
  41.     @Test 
  42.     public void testCreateTable() throws  Exception{ 
  43.  
  44.         //從連接中構造一個DDL操作器 
  45.         Admin admin = conn.getAdmin(); 
  46.  
  47.         //創(chuàng)建一個標定義描述對象 
  48.         HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("user_info")); 
  49.  
  50.         //創(chuàng)建列族定義描述對象 
  51.         HColumnDescriptor hColumnDescriptor_1 = new HColumnDescriptor("base_info"); 
  52.         hColumnDescriptor_1.setMaxVersions(3); 
  53.  
  54.         HColumnDescriptor hColumnDescriptor_2 = new HColumnDescriptor("extra_info"); 
  55.  
  56.         //將列族定義信息對象放入表定義對象中 
  57.         hTableDescriptor.addFamily(hColumnDescriptor_1); 
  58.         hTableDescriptor.addFamily(hColumnDescriptor_2); 
  59.  
  60.         //用ddl操作器對象:admin來創(chuàng)建表 
  61.         admin.createTable(hTableDescriptor); 
  62.  
  63.         //關閉連接 
  64.         admin.close(); 
  65.         conn.close(); 
  66.  
  67.     } 
  68.  
  69.     /** 
  70.      * 刪除表 
  71.      */ 
  72.     @Test 
  73.     public void testDropTable() throws  Exception{ 
  74.  
  75.         Admin admin = conn.getAdmin(); 
  76.  
  77.         //停用表 
  78.         admin.disableTable(TableName.valueOf("user_info")); 
  79.         //刪除表 
  80.         admin.deleteTable(TableName.valueOf("user_info")); 
  81.  
  82.         admin.close(); 
  83.         conn.close(); 
  84.     } 
  85.  
  86.     /** 
  87.      * 修改表定義--添加一個列族 
  88.      */ 
  89.     @Test 
  90.     public void testAlterTable() throws  Exception{ 
  91.  
  92.         Admin admin = conn.getAdmin(); 
  93.  
  94.         //取出舊的表定義信息 
  95.         HTableDescriptor tableDescriptor = admin.getTableDescriptor(TableName.valueOf("user_info")); 
  96.  
  97.         //新構造一個列族定義 
  98.         HColumnDescriptor hColumnDescriptor = new HColumnDescriptor("other_info"); 
  99.         hColumnDescriptor.setBloomFilterType(BloomType.ROWCOL);//設置該列族的布隆過濾器類型 
  100.  
  101.         //將列族定義添加到表定義對象中 
  102.         tableDescriptor.addFamily(hColumnDescriptor); 
  103.  
  104.         //將修改過的表定義交給admin去提交 
  105.         admin.modifyTable(TableName.valueOf("user_info"), tableDescriptor); 
  106.  
  107.         admin.close(); 
  108.         conn.close(); 
  109.     } 

示例如下

  1. package com.initialize; 
  2. import org.apache.hadoop.conf.Configuration; 
  3. import org.apache.hadoop.hbase.Cell; 
  4. import org.apache.hadoop.hbase.CellScanner; 
  5. import org.apache.hadoop.hbase.HBaseConfiguration; 
  6. import org.apache.hadoop.hbase.TableName; 
  7. import org.apache.hadoop.hbase.client.*; 
  8. import org.apache.hadoop.hbase.util.Bytes; 
  9. import org.junit.Before; 
  10. import org.junit.Test; 
  11. import java.io.IOException; 
  12. import java.util.ArrayList; 
  13. import java.util.Iterator; 
  14.  
  15. public class HbaseClientDML { 
  16.  
  17.     Connection conn = null
  18.  
  19.     @Before 
  20.     public void getConn() throws IOException { 
  21.         //構建一個連接對象 
  22.         Configuration conf = HBaseConfiguration.create();//會自動加載hbase-site.xml 
  23.         conf.set("hbase.zookeeper.quorum","n1:2181,n2:2181,n3:2181"); 
  24.  
  25.         conn = ConnectionFactory.createConnection(conf); 
  26.     } 
  27.  
  28.  
  29.     /** 
  30.      * 增,改:put來覆蓋 
  31.      */ 
  32.     @Test 
  33.     public void testPut() throws  Exception{ 
  34.  
  35.         //獲取一個操作指定表的table對象,進行DML操作 
  36.         Table table = conn.getTable(TableName.valueOf("user_info")); 
  37.  
  38.         //構造要插入的數(shù)據(jù)為一個Put類型(一個put對象只能對應一個rowkey)的對象 
  39.         Put put = new Put(Bytes.toBytes("001")); 
  40.         put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"),Bytes.toBytes("小羽")); 
  41.         put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("18")); 
  42.         put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("成都")); 
  43.  
  44.         Put put2 = new Put(Bytes.toBytes("002")); 
  45.         put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("小娜")); 
  46.         put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("17")); 
  47.         put2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("成都")); 
  48.  
  49.         ArrayList<Put> puts = new ArrayList<>(); 
  50.         puts.add(put); 
  51.         puts.add(put2); 
  52.  
  53.         //插進去 
  54.         table.put(puts); 
  55.  
  56.         table.close(); 
  57.         conn.close(); 
  58.     } 
  59.  
  60.     /*** 
  61.      * 循環(huán)插入大量數(shù)據(jù) 
  62.      */ 
  63.     @Test 
  64.     public void testManyPuts() throws Exception{ 
  65.  
  66.         Table table = conn.getTable(TableName.valueOf("user_info")); 
  67.         ArrayList<Put> puts = new ArrayList<>(); 
  68.  
  69.         for(int i=0;i<10000;i++){ 
  70.             Put put = new Put(Bytes.toBytes(""+i)); 
  71.             put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("usernaem"), Bytes.toBytes("小羽" +i)); 
  72.             put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes((18+i) + "")); 
  73.             put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("成都")); 
  74.  
  75.             puts.add(put); 
  76.         } 
  77.  
  78.         table.put(puts); 
  79.     } 
  80.  
  81.     /** 
  82.      * 刪 
  83.      */ 
  84.     @Test 
  85.     public void testDelete() throws Exception{ 
  86.         Table table = conn.getTable(TableName.valueOf("user_info")); 
  87.  
  88.         //構造一個對象封裝要刪除的數(shù)據(jù)信息 
  89.         Delete delete1 = new Delete(Bytes.toBytes("001")); 
  90.  
  91.         Delete delete2 = new Delete(Bytes.toBytes("002")); 
  92.         delete2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr")); 
  93.  
  94.         ArrayList<Delete> dels = new ArrayList<>(); 
  95.         dels.add(delete1); 
  96.         dels.add(delete2); 
  97.  
  98.         table.delete(dels); 
  99.  
  100.         table.close(); 
  101.         conn.close(); 
  102.     } 
  103.  
  104.     /** 
  105.      * 查 
  106.      */ 
  107.     @Test 
  108.     public void  testGet() throws Exception{ 
  109.  
  110.         Table table = conn.getTable(TableName.valueOf("user_info")); 
  111.  
  112.         Get get = new Get("002".getBytes()); 
  113.  
  114.         Result result = table.get(get); 
  115.  
  116.         //從結果中取用戶指定的某個key和value的值 
  117.         byte[] value = result.getValue("base_info".getBytes(), "age".getBytes()); 
  118.         System.out.println(new String(value)); 
  119.  
  120.         System.out.println("======================"); 
  121.  
  122.         //遍歷整行結果中的所有kv單元格 
  123.         CellScanner cellScanner = result.cellScanner(); 
  124.         while(cellScanner.advance()){ 
  125.             Cell cell = cellScanner.current(); 
  126.  
  127.             byte[] rowArray = cell.getRowArray();//本kv所屬行鍵的字節(jié)數(shù)組 
  128.             byte[] familyArray = cell.getFamilyArray();//列族名的字節(jié)數(shù)組 
  129.             byte[] qualifierArray = cell.getQualifierArray();//列名的字節(jié)數(shù)組 
  130.             byte[] valueArray = cell.getValueArray();//value字節(jié)數(shù)組 
  131.              
  132.             System.out.println("行鍵:" + new String(rowArray, cell.getRowOffset(), cell.getRowLength())); 
  133.             System.out.println("列族名:" + new String(familyArray, cell.getFamilyOffset(), cell.getFamilyLength())); 
  134.             System.out.println("列名:" + new String(qualifierArray, cell.getQualifierOffset(), cell.getQualifierLength())); 
  135.             System.out.println("value:" + new String(valueArray, cell.getValueOffset(), cell.getValueLength())); 
  136.  
  137.         } 
  138.         table.close(); 
  139.         conn.close(); 
  140.  
  141.     } 
  142.  
  143.  
  144.     /** 
  145.      * 按行鍵范圍查詢數(shù)據(jù) 
  146.      */ 
  147.     @Test 
  148.     public void testScan() throws Exception{ 
  149.  
  150.         Table table = conn.getTable(TableName.valueOf("user_info")); 
  151.  
  152.         //包含起始行鍵,不包含結束行鍵,但是如果真的是想要查詢出末尾的那個行鍵,可以在尾行鍵上拼接一個不可見的字符(\000) 
  153.         Scan scan = new Scan("10".getBytes(), "10000\001".getBytes()); 
  154.  
  155.         ResultScanner scanner =table.getScanner(scan); 
  156.  
  157.         Iterator<Result> iterator = scanner.iterator(); 
  158.  
  159.         while(iterator.hasNext()){ 
  160.  
  161.             Result result =iterator.next(); 
  162.             //遍歷整行結果中的所有kv單元格 
  163.             CellScanner cellScanner = result.cellScanner(); 
  164.             while(cellScanner.advance()){ 
  165.                 Cell cell = cellScanner.current(); 
  166.  
  167.                 byte[] rowArray = cell.getRowArray();//本kv所屬行鍵的字節(jié)數(shù)組 
  168.                 byte[] familyArray = cell.getFamilyArray();//列族名的字節(jié)數(shù)組 
  169.                 byte[] qualifierArray = cell.getQualifierArray();//列明的字節(jié)數(shù)組 
  170.                 byte[] valueArray = cell.getValueArray();//value字節(jié)數(shù)組 
  171.  
  172.                 System.out.println("行鍵:" + new String(rowArray, cell.getRowOffset(), cell.getRowLength())); 
  173.                 System.out.println("列族名:" + new String(familyArray, cell.getFamilyOffset(), cell.getFamilyLength())); 
  174.                 System.out.println("列名:" + new String(qualifierArray, cell.getQualifierOffset(), cell.getQualifierLength())); 
  175.                 System.out.println("value:" + new String(valueArray, cell.getValueOffset(), cell.getValueLength())); 
  176.             } 
  177.             System.out.println("----------------------"); 
  178.         } 
  179.     } 
  180.  
  181.     @Test 
  182.     public void test(){ 
  183.         String a = "000"
  184.         String b = "000\0"
  185.  
  186.         System.out.println(a); 
  187.         System.out.println(b); 
  188.  
  189.         byte[] bytes = a.getBytes(); 
  190.         byte[] bytes2 = b.getBytes(); 
  191.          
  192.         System.out.println(""); 
  193.     } 

HBase 應用場景

對象存儲系統(tǒng)

HBase MOB(Medium Object Storage),中等對象存儲是 hbase-2.0.0 版本引入的新特性,用于解決 hbase 存儲中等文件(0.1m~10m)性能差的問題。這個特性適合將圖片、文檔、PDF、小視頻存儲到 Hbase 中。

OLAP 的存儲

Kylin 的底層用的是 HBase 的存儲,看中的是它的高并發(fā)和海量存儲能力。kylin 構建 cube 的過程會產(chǎn)生大量的預聚合中間數(shù)據(jù),數(shù)據(jù)膨脹率高,對數(shù)據(jù)庫的存儲能力有很高要求。

Phoenix 是構建在 HBase 上的一個 SQL 引擎,通過 phoenix 可以直接調(diào)用 JDBC 接口操作 Hbase,雖然有 upsert 操作,但是更多的是用在 OLAP 場景,缺點是非常不靈活。

時序型數(shù)據(jù)

openTsDB 應用,記錄以及展示指標在各個時間點的數(shù)值,一般用于監(jiān)控的場景,是 HBase 上層的一個應用。

用戶畫像系統(tǒng)

動態(tài)列,稀疏列的特性。用于描述用戶特征的維度數(shù)是不定的且可能會動態(tài)增長的(比如愛好,性別,住址等),不是每個特征維度都會有數(shù)據(jù)。

消息/訂單系統(tǒng)

強一致性,良好的讀性能,hbase 可以保證強一致性。

feed 流系統(tǒng)存儲

feed 流系統(tǒng)具有讀多寫少、數(shù)據(jù)模型簡單、高并發(fā)、波峰波谷式訪問、持久化可靠性存儲、消息排序這些特點,比如說 HBase 的 rowKey 按字典序排序正好適用于這個場景。

Hbase 優(yōu)化

預先分區(qū)

默認情況下,在創(chuàng)建 HBase 表的時候會自動創(chuàng)建一個 Region 分區(qū),當導入數(shù)據(jù)的時候,所有的 HBase 客戶端都向這一個 Region 寫數(shù)據(jù),直到這個 Region 足夠大了才進行切分。一種可以加快批量寫入速度的方法是通過預先創(chuàng)建一些空的 Regions,這樣當數(shù)據(jù)寫入 HBase 時,會按照 Region 分區(qū)情況,在集群內(nèi)做數(shù)據(jù)的負載均衡。

Rowkey 優(yōu)化

HBase 中 Rowkey 是按照字典序存儲,因此,設計 Rowkey 時,要充分利用排序特點,將經(jīng)常一起讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問的數(shù)據(jù)放在一塊。

此外,Rowkey 若是遞增的生成,建議不要使用正序直接寫入 Rowkey,而是采用 reverse 的方式反轉 Rowkey,使得 Rowkey 大致均衡分布,這樣設計有個好處是能將 RegionServer 的負載均衡,否則容易產(chǎn)生所有新數(shù)據(jù)都在一個 RegionServer 上堆積的現(xiàn)象,這一點還可以結合 table 的預切分一起設計。

減少列族數(shù)量

不要在一張表里定義太多的 ColumnFamily。目前 Hbase 并不能很好的處理超過 2~3 個 ColumnFamily 的表。因為某個 ColumnFamily 在 flush 的時候,它鄰近的 ColumnFamily 也會因關聯(lián)效應被觸發(fā) flush,最終導致系統(tǒng)產(chǎn)生更多的 I/O。

緩存策略

創(chuàng)建表的時候,可以通過 HColumnDescriptor.setInMemory(true) 將表放到 RegionServer 的緩存中,保證在讀取的時候被 cache 命中。

設置存儲生命期

創(chuàng)建表的時候,可以通過 HColumnDescriptor.setTimeToLive(int timeToLive) 設置表中數(shù)據(jù)的存儲生命期,過期數(shù)據(jù)將自動被刪除。

硬盤配置

每臺 RegionServer 管理 10~1000 個 Regions,每個 Region 在 1~2G,則每臺 Server 最少要 10G,最大要1000*2G=2TB,考慮 3 備份,則要 6TB。方案一是用 3 塊 2TB 硬盤,二是用 12 塊 500G 硬盤,帶寬足夠時,后者能提供更大的吞吐率,更細粒度的冗余備份,更快速的單盤故障恢復。

分配合適的內(nèi)存給 RegionServer 服務

在不影響其他服務的情況下,越大越好。例如在 HBase 的 conf 目錄下的 hbase-env.sh 的最后添加 export HBASE_REGIONSERVER_OPTS="-Xmx16000m$HBASE_REGIONSERVER_OPTS”,其中 16000m 為分配給 RegionServer 的內(nèi)存大小。

寫數(shù)據(jù)的備份數(shù)

備份數(shù)與讀性能成正比,與寫性能成反比,且備份數(shù)影響高可用性。有兩種配置方式,一種是將 hdfs-site.xml拷貝到 hbase 的 conf 目錄下,然后在其中添加或修改配置項 dfs.replication 的值為要設置的備份數(shù),這種修改對所有的 HBase 用戶表都生效,另外一種方式,是改寫 HBase 代碼,讓 HBase 支持針對列族設置備份數(shù),在創(chuàng)建表時,設置列族備份數(shù),默認為 3,此種備份數(shù)只對設置的列族生效。

WAL(預寫日志)

可設置開關,表示 HBase 在寫數(shù)據(jù)前用不用先寫日志,默認是打開,關掉會提高性能,但是如果系統(tǒng)出現(xiàn)故障(負責插入的 RegionServer 掛掉),數(shù)據(jù)可能會丟失。配置 WAL 在調(diào)用 JavaAPI 寫入時,設置 Put 實例的 WAL,調(diào)用 Put.setWriteToWAL(boolean)。

批量寫

HBase 的 Put 支持單條插入,也支持批量插入,一般來說批量寫更快,節(jié)省來回的網(wǎng)絡開銷。在客戶端調(diào)用 JavaAPI 時,先將批量的 Put 放入一個 Put 列表,然后調(diào)用 HTable 的 Put(Put 列表) 函數(shù)來批量寫。

最后

在理解 HBase 時,可以發(fā)現(xiàn) HBase 的設計其實和 Elasticsearch 十分相似,如 HBase 的 Flush&Compact 機制等設計與 Elasticsearch 如出一轍,因此理解起來比較順利。

從本質上來說,HBase 的定位是分布式存儲系統(tǒng),Elasticsearch 是分布式搜索引擎,兩者并不等同,但兩者是互補的。HBase 的搜索能力有限,只支持基于 RowKey 的索引,其它二級索引等高級特性需要自己開發(fā)。因此,有一些案例是結合 HBase 和 Elasticsearch 實現(xiàn)存儲 + 搜索的能力。通過 HBase 彌補 Elasticsearch 存儲能力的不足,通過 Elasticsearch 彌補 HBase 搜索能力的不足。

其實,不只是 HBase 和 Elasticsearch。任何一種分布式框架或系統(tǒng),它們都有一定的共性,不同之處在于各自的關注點不同。小羽的感受是,在學習分布式中間件時,應先弄清其核心關注點,再對比其它中間件,提取共性和特性,進一步加深理解。

 

責任編輯:姜華 來源: 淺羽的IT小屋
相關推薦

2012-09-12 09:40:36

云服務GIS技術彈性云計算

2015-08-19 10:06:21

2013-12-09 16:16:29

初志科技數(shù)據(jù)動車

2014-07-01 10:07:56

2012-06-25 16:57:07

2011-12-16 11:11:24

戴爾

2010-06-14 23:32:04

綜合布線機場西蒙

2015-12-16 17:54:33

E店寶

2014-05-22 10:29:11

eLTE無線華為

2013-09-09 12:29:28

企業(yè)郵箱海外通郵

2016-03-16 11:08:19

Zenlayer

2013-02-01 16:48:16

2012-02-13 10:46:37

TEMTivoliIBM

2015-09-23 10:12:58

2012-11-19 20:22:40

2015-04-30 15:43:10

eLTE第53屆世乒賽華為

2011-01-04 15:37:44

2012-11-13 18:24:03

LinOTPApache2一次性密碼

2025-02-11 08:23:41

點贊
收藏

51CTO技術棧公眾號