早些時候學習hadoop的技術,我一直對里面兩項技術倍感困惑,一個是zookeeper,一個就是Hbase了。現在有機會專職做大數據相關的項目,終於看到了HBase實戰的項目,也因此有機會搞懂Hbase原理。
首先來點實在的東西,假如我們已經在服務器上部署好了Hbase應用,作為客戶端或者說的具體點,本地開發環境如何編寫程序和服務端的Hbase進行交互了?
下面我將展示這些,首先看工程的結構圖,如下圖所示:

接下來我們將hbase應用下lib文件夾里所有jar包都導入到工程lib目錄下,還要把conf目錄下的hbase-site.xml下載下來放置在conf目錄里,這里我還將hbase項目里的log4j.properties文件放置到了項目的根目錄下,這樣在我們運行程序時候,控制台打印的日志也會更加的詳細,下面是HBaseStudy.java的源代碼:
package cn.com.hbasetest;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HBaseStudy {
public final static Logger logger = LoggerFactory.getLogger(HBaseStudy.class);
/* 構建Configuration,這里就是hbase-site.xml解析出來的對象,這里我還指定了本地讀取文件的方式 */
static Configuration hbaseConf = HBaseConfiguration.create();
static {
hbaseConf.addResource("conf/hbase-site.xml");
}
/**
* 插入數據
* @throws IOException
*/
public void putTableData() throws IOException {
HTable tbl = new HTable(hbaseConf, "xsharptable001");
Put put = new Put(Bytes.toBytes("xrow01"));
put.add(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol01"), Bytes.toBytes("xvalue01"));
put.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"), Bytes.toBytes("xvalue02"));
put.addImmutable(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol03"), Bytes.toBytes("xvalue03"));
tbl.put(put);
}
/**
* 插入多行數據
* @throws IOException
*/
public void putTableDataRow() throws IOException {
HTable tbl = new HTable(hbaseConf, "xsharptable001");
Put put = new Put(Bytes.toBytes("xrow02"));
put.add(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol01"), Bytes.toBytes("xvalue012"));
put.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"), Bytes.toBytes("xvalue022"));
put.addImmutable(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"), Bytes.toBytes("xvalue032"));
tbl.put(put);
put = new Put(Bytes.toBytes("xrow03"));
put.add(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol01"), Bytes.toBytes("xvalue0213"));
put.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"), Bytes.toBytes("xvalue0123"));
put.addImmutable(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol03"), Bytes.toBytes("xvalue0223"));
tbl.put(put);
put = new Put(Bytes.toBytes("xrow04"));
put.add(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol01"), Bytes.toBytes("xvalue0334"));
put.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"), Bytes.toBytes("xvalue0224"));
put.addImmutable(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol03"), Bytes.toBytes("xvalue0334"));
put.addImmutable(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol04"), Bytes.toBytes("xvalue0334"));
tbl.put(put);
}
/**
* 查詢hbase表里的數據
* @throws IOException
*/
public void getTableData() throws IOException {
HTable table = new HTable(hbaseConf, "xsharptable001");
Get get = new Get(Bytes.toBytes("xrow01"));
get.addFamily(Bytes.toBytes("xcolfam01"));
Result result = table.get(get);
byte[] bs = result.getValue(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"));
// ============查詢結果:xvalue02
logger.info("============查詢結果:" + Bytes.toString(bs));
}
/**
* 創建hbase的表
*
* @throws MasterNotRunningException
* @throws ZooKeeperConnectionException
* @throws IOException
*/
public void createTable() throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
HBaseAdmin admin = new HBaseAdmin(hbaseConf);
if (admin.tableExists(Bytes.toBytes("xsharptable001"))) {
logger.info("===============:表已經存在!failure!");
} else {
TableName tableName = TableName.valueOf(Bytes.toBytes("xsharptable001"));
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
HColumnDescriptor hcol = new HColumnDescriptor(Bytes.toBytes("xcolfam01"));
tableDesc.addFamily(hcol);
admin.createTable(tableDesc);
logger.info("==============:表創建成功了!Success!");
}
}
/**
* 通過scan掃描數據,相當於關系數據的游標
*
* @throws IOException
*/
public void scanTableData() throws IOException {
HTable tbl = new HTable(hbaseConf, "xsharptable001");
Scan scanAll = new Scan();
ResultScanner scannerAll = tbl.getScanner(scanAll);
for (Result resAll : scannerAll) {
/*
* 打印出來的結果: 2016-06-14 15:46:10,723 INFO [main]
* hbasetest.HBaseStudy: ======ScanAll
* :keyvalues={xrow01/xcolfam01:xcol01/1465885252556/Put
* /vlen=8/seqid=0,
* xrow01/xcolfam01:xcol02/1465885252556/Put/vlen=8/seqid=0,
* xrow01/xcolfam01:xcol03/1465885252556/Put/vlen=8/seqid=0}
* 2016-06-14 15:46:10,723 INFO [main] hbasetest.HBaseStudy:
* ======ScanAll
* :keyvalues={xrow02/xcolfam01:xcol01/1465887392414/Put
* /vlen=9/seqid=0,
* xrow02/xcolfam01:xcol02/1465887392414/Put/vlen=9/seqid=0}
* 2016-06-14 15:46:10,723 INFO [main] hbasetest.HBaseStudy:
* ======ScanAll
* :keyvalues={xrow03/xcolfam01:xcol01/1465887392428/Put
* /vlen=10/seqid=0,
* xrow03/xcolfam01:xcol02/1465887392428/Put/vlen=10/seqid=0,
* xrow03/xcolfam01:xcol03/1465887392428/Put/vlen=10/seqid=0}
* 2016-06-14 15:46:10,723 INFO [main] hbasetest.HBaseStudy:
* ======ScanAll
* :keyvalues={xrow04/xcolfam01:xcol01/1465887392432/Put
* /vlen=10/seqid=0,
* xrow04/xcolfam01:xcol02/1465887392432/Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol03/1465887392432/Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol04/1465887392432/Put/vlen=10/seqid=0}
*/
logger.info("======ScanAll:" + resAll);
}
scannerAll.close();
Scan scanColFam = new Scan();
scanColFam.addFamily(Bytes.toBytes("xcolfam01"));
ResultScanner scannerColFam = tbl.getScanner(scanColFam);
for (Result resColFam : scannerColFam) {
/*
* 2016-06-14 15:50:54,690 INFO [main] hbasetest.HBaseStudy:
* ======scannerColFam
* :keyvalues={xrow01/xcolfam01:xcol01/1465885252556
* /Put/vlen=8/seqid=0,
* xrow01/xcolfam01:xcol02/1465885252556/Put/vlen=8/seqid=0,
* xrow01/xcolfam01:xcol03/1465885252556/Put/vlen=8/seqid=0}
* 2016-06-14 15:50:54,690 INFO [main] hbasetest.HBaseStudy:
* ======scannerColFam
* :keyvalues={xrow02/xcolfam01:xcol01/1465887392414
* /Put/vlen=9/seqid=0,
* xrow02/xcolfam01:xcol02/1465887392414/Put/vlen=9/seqid=0}
* 2016-06-14 15:50:54,690 INFO [main] hbasetest.HBaseStudy:
* ======scannerColFam
* :keyvalues={xrow03/xcolfam01:xcol01/1465887392428
* /Put/vlen=10/seqid=0,
* xrow03/xcolfam01:xcol02/1465887392428/Put/vlen=10/seqid=0,
* xrow03/xcolfam01:xcol03/1465887392428/Put/vlen=10/seqid=0}
* 2016-06-14 15:50:54,690 INFO [main] hbasetest.HBaseStudy:
* ======scannerColFam
* :keyvalues={xrow04/xcolfam01:xcol01/1465887392432
* /Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol02/1465887392432/Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol03/1465887392432/Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol04/1465887392432/Put/vlen=10/seqid=0}
*/
logger.info("======scannerColFam:" + resColFam);
}
scannerColFam.close();
Scan scanRow = new Scan();
scanRow.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol02"))
.addColumn(Bytes.toBytes("xcolfam01"), Bytes.toBytes("xcol04")).setStartRow(Bytes.toBytes("xrow03"))
.setStopRow(Bytes.toBytes("xrow05"));
ResultScanner scannerRow = tbl.getScanner(scanRow);
for (Result resRow : scannerRow) {
/*
* 2016-06-14 15:57:29,449 INFO [main] hbasetest.HBaseStudy:
* ======scannerRow
* :keyvalues={xrow03/xcolfam01:xcol02/1465887392428/
* Put/vlen=10/seqid=0} 2016-06-14 15:57:29,449 INFO [main]
* hbasetest.HBaseStudy:
* ======scannerRow:keyvalues={xrow04/xcolfam01
* :xcol02/1465887392432/Put/vlen=10/seqid=0,
* xrow04/xcolfam01:xcol04/1465887392432/Put/vlen=10/seqid=0}
*/
logger.info("======scannerRow:" + resRow);
}
scannerRow.close();
}
public static void main(String[] args) {
HBaseStudy hb = new HBaseStudy();
/*try {
hb.createTable();
hb.putTableData();
} catch (Exception e) {
e.printStackTrace();
}*/
try {
// hb.getTableData();
// hb.putTableDataRow();
hb.scanTableData();
} catch (Exception e) {
e.printStackTrace();
}
}
}
這段代碼寫得匆忙,示例並沒有好好設計,不過代碼我是測試過,完全可以正常運行,下面幾站截圖是我通過hbase shell查詢測試結果,如下圖所示:
圖一:

這里我通過describe命令查看表的基本信息。
圖二:

這里我使用scan命令進行全表掃描。
圖三:

當我插入更多數據時候使用scan命令進行全表掃描。
本篇文章不會着重講解hbase的javaAPI,其實本實例里也只是使用了少量的API,不過在我選擇的API里我想體現的是hbase里表(table),行(rowkey),列族(family)和列(column)之間的關系。
創建表我們要先定義好表名,列族,插入數據我們首先是插入行,然后根據列族定義列接下來添加數據,這些都是按照hbase設計規范進行操作的,下面就是關鍵所在了:我們是如何查詢數據的。
對於查詢的Get操作,我們構造Get對象時候就是使用rowkey進行,scan可以進行全表掃描,也可以根據列族查詢,還可以使用rowkey的范圍限定scan掃描數據的范圍,不管從什么角度進行查詢,我們可以總結出hbase做查詢時候都會跟rowkey和列族相關,hbase的javaAPI並沒有再去提供更多查詢手段,因此我們可以得出在提升hbase查詢效率的因素里rowkey和列族必然承擔了很重要的作用。
大數據時代的數據量是超大規模的,傳統的關系數據庫已經很難存儲和管理這些數據了,為了存儲海量數據,我們有了HDFS,它可以把成千上萬台服務器上的硬盤聚集成一塊超級大的硬盤,為了讓這些數據產生價值,我們有了mapreduce,它可以計算這個超大硬盤的數據,面對這么大的數據量我們還有一個迫切的需求那就是如何快速檢索出我們想要的數據,而這個功能就是由hbase來承擔。
那么如此海量數據快速檢索技術原理又是怎樣的呢?我覺得原理很簡單就是索引技術。Hbase通過rowkey來區別不同類型數據,通過列族把經常需要一起被查詢出來的數據放在一起,例如我們如果要做一個電商平台的交易記錄業務表設計,對於電商平台下的商戶他其實只需要查詢出自己的交易信息,而不會去關心其他商戶的交易信息,那么我們就可以把商戶號作為rowkey,每一個商戶的交易的信息我們就放在一個列族里,商戶號這樣的信息就像數據在硬盤上的門牌號,我們一傳入這個值做查詢,hbase就能快速找到數據存儲的位置,這就是hbase能快速檢索到數據的原理。
上面講到的原理只是業務抽象的角度來說,在hbase底層它就是根據上面說到的這些原理來設計的,hbase里面有region的概念,region是一個數據集合,那么什么樣的數據會放置到某一個region里呢?hbase是根據rowkey來把同一類的數據放置在一個region里,rowkey下面就是列族,列族對應的底層存儲就是hfile,hfile放置在rowkey對應的region下,所以當我們查詢時候我們很容易通過業務規則找到我們設計好的rowkey,找到了rowkey就找到region,那么region下存儲的hfile列族信息也就可以全部查詢出來了。
Rowkey其實就是hbase的索引,也可以說是hbase官方給出的唯一索引,因此很多資料里說hbase只有一級索引,這個一級索引就指的就是rowkey,因此如何設計rowkey就是一門大學問了,時常我們一行數據不能滿足我們復雜的查詢要求,我們需要跨行就像scan那么掃描多行數據,而region里的行都是按照一定順序排列的,這個順序就是字典順序,這個我在以前一篇文章里提到過,所以碰到這種情況,我們一般會通過md5將key散列,這樣相鄰的數據行會排列在一起,底層存儲數據時候也會存儲在同一個地方(相同region)或者是相互靠近的地方(相鄰region),這樣也就可以提升查詢的效率。
Hbase內部有兩張表一個是-ROOT-表和.META.表,客戶端程序就是像我上面給出的示例程序首先訪問zookeeper,通過zookeeper獲取含有-ROOT-的region服務器名,通過-ROOT-的region服務器可以查詢到.META.表里行鍵rowkey對應的region位置,而-ROOT-和.META.客戶端訪問后就會緩存起來。
其實hbase的表設計本身非常簡單,對外接口也沒有關系數據庫那么豐富,我最近學習hbase,覺得hbase基本都沒有關系數據庫里那些計算函數,可見hbase只是提供一種能快速檢索海量數據的一種計算模型而已。
本文就到此為止了,好記性不如爛筆頭,寫寫文章是對自己學習的總結,也是留一個備忘和將來的遺忘做斗爭了。
