文章目錄
由於文章內容較多,常需要用到目錄索引,友情提示:頁面右側有個快捷展開目錄的功能,瀏覽過程中可以隨時查看目錄。
HBase入門認識
HBase介紹
- HBase是參考google的bigtable的一個開源產品,建立在hdfs之上的一個提供高可靠性、高性能、列存儲、可伸縮、實時讀寫的數據庫系統。
- 是一種介於nosql和RDBMs之間的一種數據庫系統,僅支持通過rowkey和range進行數據的檢索,主要存儲非結構化數據和半結構化數據。
- HBase和Hadoop一樣,目標是通過橫向擴展,添加普通機器來增加存儲性能和計算性能。
- HBase特點:大(一個表可以有上億行以及百萬級的行)、面向行存儲、稀疏(由於null不佔用存儲空間,所有表結果可以設計的非常稀疏)。
HBase
- HBase使用Zookeeper進行集羣節點管理,當然HBase自身集成了一個ZK系統,不過一般情況在實際生產環境中不使用。
- HBase由master和regionserver兩類節點(如果使用HBase自帶的zk服務,那麼還有HQuorumPeer進程)。
- Hbase支持提供backup master進行master備份。其中master節點負責和zk進行通信以及存儲regionserver的相關位置信息,regionserver節點實現具體對數據的操作,最終數據存儲在hdfs上。
HBase架構
RegionServer集羣結構
HBase邏輯存儲結構
HBase物理存儲結構
key:rowkey:列簇:列名:時間戳
HBase安裝
前期準備
- HBase有三種安裝方式,第一種獨立模式,第二種是分佈式模式(集成zookeeper),第三種是分佈式模式(獨立zookeeper)。
- 安裝步驟:
- 安裝jdk,至少1.6(版本u18除外)。
- 安裝ssh免密碼登錄。
- 修改hostname和hosts,hbase通過hostname獲取ip地址。
- Hadoop安裝。
- 生成環境集羣(NTP + ulimit&nproc + hdfs的dfs.datanode.max.xcievers)
- hbase下載安裝
HBase安裝步驟
- 下載hbase,選擇版本hbase-0.98.6-cdh5.3.6,下載地址http://archive.cloudera.com/cdh5/cdh/5/。
- 解壓壓縮包到目錄/home/hadoop/bigdater/下。
- 在hbase的根目錄下創建一個文件夾hbase來存儲臨時文件和pid等。默認/tmp。
- 修改配置信息{hbase.home}/conf/hbase-env.sh文件。
<configuration>
<property>
<name>hbase.rootdir</name>
<value>hdfs://oda.com/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.tmp.dir</name>
<value>/home/jlu/bd/hbase-0.98.6-cdh5.3.6/hbase/tmp</value>
</property>
</configuration>
-
指定regionserver節點hostname,修改文件regionservers。
-
創建到hdfs-site.xml的軟連接(即在$HADOOP_HOME/etc/hadoop下創建hbase-site.xml的軟連接)或者配置連接hdfs的配置信息(推薦)。
-
添加hbase相關信息到環境變量中(vi ~/.bash_profile)。
-
啓動hbase集羣並驗證。
**大寫的ps.**爲防止後期編程,hadoop缺少jar包,建議在hadoop-env.sh上添加如下配置
Hbase啓動
- 啓動集羣命令: start-hbase.sh
- 停止集羣命令: stop-hbase.sh
- 單獨啓動/停止進程命令: (單獨的啓動master或者regionserver)
hbase-daemon.sh (start|stop) (master|regionserver|zookeeper)
hbase-daemons.sh (start|stop) (regionserver|zookeeper)
HBase驗證
驗證分爲四種方式:
- jsp查看是否有hbase的正常啓動。
- web界面查看是否啓動成功。http://192.168.162.121:60010/
- shell命令客戶端查看是否啓動成功。
- 查看hbase是否安裝成功,查看hdfs文件下是否有hbase的文件夾。
備份master
- 如果需要使用hbase的多master結構,那麼需要在conf文件夾下添加backup-masters文件,然後一行一個主機名,和regionservers是一樣的;
- 或者在hbase-env.sh中添加變量HBASE_BACKUP_MASTERS,對應value爲backup-masters存儲路徑(啓動命令一樣)。
HBase命令
下文演示中使用的數據
create 'users','f'
put 'users', 'row1', 'f:id', '1'
put 'users', 'row1', 'f:name', 'zhangsan'
put 'users', 'row1', 'f:age', 18
put 'users', 'row1', 'f:phone', '021-11111111'
put 'users', 'row1', 'f:email', '[email protected]'
put 'users', 'row2', 'f:id', '2'
put 'users', 'row2', 'f:name', 'lisi'
put 'users', 'row2', 'f:email', '[email protected]'
put 'users', 'row2', 'f:address', 'shanghai'
put 'users', 'row3', 'f:id', '3'
put 'users', 'row3', 'f:name', 'lili'
put 'users', 'row3', 'f:age', 25
put 'users', 'row3', 'f:country', 'china'
put 'users', 'row3', 'f:email', '[email protected]'
put 'users', 'row4', 'f:id', '4'
put 'users', 'row4', 'f:name', 'user4'
put 'users', 'row5', 'f:id', '5'
put 'users', 'row5', 'f:name', 'user5'
put 'users', 'row6', 'f:id', '6'
put 'users', 'row6', 'f:name', 'user6'
put 'users', 'row7', 'f:id', '7'
put 'users', 'row7', 'f:name', 'user7'
put 'users', 'row8', 'f:id', '8'
put 'users', 'row8', 'f:name', 'user8'
put 'users', 'row9', 'f:id', '9'
put 'users', 'row9', 'f:name', 'user9'
put 'users', 'row10', 'f:id', '10'
put 'users', 'row10', 'f:name', 'user10'
HBase 命令介紹
- HBase命令主要分爲兩大類,
- 第一類是指操作hbase表的相關的shell命令;
- 第二類是提供hbase其他相關服務的命令。
- 第一類命令全部在****hbase** shell命令中,那麼第二類命令主要以thrift/thrift2等服務爲主**。
HBase Shell命令介紹
- HBase的Shell命令是以JRuby爲核心編寫的,主要分爲DDL和DML兩大類,除此兩類之外還有一起其他的命令運維相關的命令,比如snapshots等。
- 當我們進入hbase的shell命令客戶端的時候,我們可以通過help命令查看幫助信息,也可以通過help命令查看具體命令的使用方法。
status命令
- 作用:查看hbase集羣狀態信息。
- 參數:simple(簡潔),summary(概述),detailed(詳細,具體到表);默認是summary。
命名空間namespace相關命令
介紹
- 作用:類似關係型數據庫中的database,作用是將hbase的表按照業務作用分割開,有益於維護。Hbase默認有兩個命名空間,分別是hbase和default。其中hbase命名空間存儲hbase自身的表信息,default存儲用戶創建的表。
- 命令:
- create_namespace(創建命名空間),
- alter_namespace(命名空間修改),
- describe_namespace(顯示命名空間描述信息),
- drop_namespace(刪除命名空間,前提是該命名空間是空的),
- list_namespace(顯示所有命名空間),
- list_namespace_tables(顯示對於命名空間中的table名稱)。
create_namespace命令
- 作用:創建命名空間。
- 示例:create_namespace ‘bigdater’, {‘comment’=>‘this is ourself namespace’,‘keyname’=>‘valuename’}
drop_namespace命令
- 作用:刪除指定命名空間;注意刪除的命名空間內不能有table存在,也就是說只能刪除空的namespace。
- 示例:drop_namespace ‘bigdater’
describe_namespace命令
- 作用:顯示命名空間的相關信息。
- 示例:describe_namespace ‘bigdater’
list_namespace命令
- 作用:顯示所有存在的命名空間。
- 示例:list_namespace或者list_namespace ‘regex_str’
list命令
- 作用:顯示hbase表名稱,類似mysql中的show tables;
- 可以通過指定命名空間來查看對應命名空間中的表,默認是顯示所有用戶表,也支持模糊匹配。類似命令list_namespace_tables查看對應命名空間內有那些表。
創建hbase用戶表
- 命令格式:create ‘[namespace_name:]table_name’, ‘family_name_1’,…‘family_name_n’(family_name_1表示列簇名)
- 如果不給定namespace的名稱,默認創建在default命名空間中。
- 示例:create ‘bigdater:test’,‘f’
刪除用戶表
- 刪除用戶表之前需要將表設置爲disable的,然後纔可以刪除。
- 其實在hbase中如果需要對已有表進行ddl操作,均需要將其disable,在ddl操作完成後,再進行enable操作即可。
- 命令格式:
- disable ‘[namespace_name:]table_name’
- drop ‘[namespace_name:]table_name’
- 示例:
- disable ‘bigdater:test’
- drop ‘bigdater:test’
- 命令格式:
put命令
- 在默認命名空間中創建users表,然後在這個表的基礎上進行操作。hbase的put命令是進行數據添加的命令。
命令格式:
put ‘[namespace_name:]table_name’, ‘rowkey’, ‘family:[column]’, ‘value’ [, timestamp] [, {ATTRIBUTES=>{‘mykey’=>‘myvalue’}, VISIBILITY=>‘PRIVATE|SECRET’}]
示例:put ‘users’,‘row1’,‘f:id’,‘1’
HbaseShell使用Java的Api
表賦給臨時變量
get命令
- get命令的作用是獲取對應表中對應rowkey的數據。默認獲取最新版本的全部列數據,可以通過時間戳指定版本信息,也可以指定獲取的列。
- 命令格式:
get ‘[namespace_name:]table_name’, ‘rowkey’ - 示例:get ‘users’,‘row1’
scan命令
- scan命令是hbase的另外一種檢索方式,是通過範圍查找hbase中的數據。默認情況下是獲取table的全部數據,可以通過指定column和filter等相關信息進行數據的過濾。
- 命令格式:
scan ‘[namespace_name:]table_name’ - 示例:scan ‘users’
scan-filter相關命令
-
scan提供多種filter命令,常用filter命令如下:ColumnPrefixFilter,MultipleColumnPrefixFilter,RowFilter,SingleColumnValueFilter,SingleColumnValueExcludeFilter等。
-
需要注意的是:在指定的value之前需要加’binary:’,比如:scan ‘users’,{FILTER=>“SingleColumnValueFilter(‘f’,‘id’,=,'binary:1)”}
-
MultipleColumnPrefixFilter匹配多個列前綴
-
RowFilter需要兩個參數
-
SingleColumnValueFilter最常用,需要的參數也比較多,圖中,因爲id存的是字符串類型,所以按字典序比較過濾
-
多個過濾條件結合使用
scan其他參數
- scan命令除了使用filter外,我們還可以定義我們需要的column,開始掃描的rowkey,結束掃描的rowkey,獲取行數等信息。
- eg:
- scan ‘users’, {COLUMN=>[‘f:id’,‘f:name’]}
- scan ‘users’, {STARTROW=>‘row1’,ENDROW=>‘row2’}
- scan ‘users’, {LIMIT=>1}
- 限制輸出三條記錄(row1開頭的都屬於一條)
- 指定開始rowkey並限制輸出三條記錄
- STARTROW和ENDROW
count命令
- count命令是統計hbase錶行數的一個命令,由於相當於一個內置的mapreduce程序,所以當數據量比較大的時候可以選擇使用協處理器方式計算行數。
- 命令格式:
- count ‘[namespace:]table_name’ [INTERVAL => 1000,] [CACHE => 10]
- 默認情況下INTERVAL是1000(間隔數),CACHE是10。
delete命令
- delete命令是刪除指定table的指定rowkey的指定列,也就是說delete命令適合刪除列的情況。
- 命令格式:
- delete ‘[namespace:]table_name’, ‘rowkey’, ‘family:column’
- 如果需要刪除當然rowkey的所有列數據,那麼可以使用deleteall命令。
truncate命令
- truncate命令的作用是清空數據庫,當我們數據庫中的數據比較多的時候,我們可以選擇該命令將數據庫清空。
- 命令格式: truncate ‘[namespace_name:]table_name’
describe命令
- 查看描述信息
- 也可以直接通過web界面查看
Java客戶端
Java客戶端
Java客戶端其實就是shell客戶端的一種實現,操作命令基本上就是shell客戶端命令的一個映射。Java客戶端使用的配置信息是被映射到一個HBaseConfiguration的實例對象中的,當使用該類的create方法創建實例對象的時候,會從classpath路徑下獲取hbase-site.xml文件並進行配置文件內容的讀取,同時會讀取hadoop的配置文件信息。也可以通過java代碼指定命令信息,只需要給定zk的相關環境變量信息即可。代碼如下:
Configuration config = HBaseConfiguration.create();
config.set("hbase.zookeeper.quorum", "hh1,hh2.."); // 第二個參數是主機名
HBaseAdmin
HBaseAdmin類是主要進行DDL操作相關的一個接口類,主要包括命名空間管理,用戶表管理。通過該接口我們可以創建、刪除、獲取用戶表,也可以進行用戶表的分割、緊縮等操作。
HTable,HTableDescriptor
HTable是hbase中的用戶表的一個映射的java實例,我們可以通過該類進行表數據的操作,包括數據的增刪查改,也就是在這裏我們可以類似shell中put,get和scan進行數據的操作。
HTableDescriptor是hbase用戶表的具體描述信息類,一般我們創建表獲取獲取(給定)表信息,就是通過該類進行的。
Put,Get,Scan,Delete
- Put類是專門提供插入數據的類。
- Get類是專門提供根據rowkey獲取數據的類。
- Scan是專門進行範圍查找的類。
- Delete是專門進行刪除的類。
HBase連接池
在web應用中,如果我們之間使用HTable來操作hbase,那麼在創建連接和關閉連接的時候,一定會浪費資源。那麼HBase提供了一個連接池的基礎,主要涉及到的類和接口包括:HConnection,HConnectionManager,HTableInterface,ExecutorService四個。
- 其中HConnection就是hbase封裝好的hbase連接池,
- HConnectionManager是管理連接池的一個類,
- HTableInterface是在類HTable的基礎上進行的一個接口抽象,
- ExecutorService是jdk的線程池對象。
Java客戶端編程
HBaseAdmin類詳細介紹
- 測試創建表
- 獲取表信息以及刪除表
HTable類和連接池詳細介紹
package com.jluzh.oda.hbase.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.HTableInterface;
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.filter.FilterList;
import org.apache.hadoop.hbase.filter.FilterList.Operator;
import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
import org.apache.hadoop.hbase.util.Bytes;
import com.jluzh.oda.hbase.util.HBaseUtil;
public class TestHTable {
static byte[] family = Bytes.toBytes("f");
public static void main(String[] args) {
Configuration conf = HBaseUtil.getHBaseConfiguration();
try {
// testUseHTable(conf);
testUseHbaseConnectionPool(conf);
} catch (IOException e) {
e.printStackTrace();
}
}
// 集中調用測試方法
static void testUseHTable(Configuration conf) throws IOException {
HTable hTable = new HTable(conf, "users");
try {
// testPut(hTable);
// testGet(hTable);
// testDelete(hTable);
testScan(hTable); // 參數是實現了接口的類實例
} finally {
hTable.close();
}
}
// 最好還是使用線程池
static void testUseHbaseConnectionPool(Configuration conf) throws IOException {
ExecutorService threads = Executors.newFixedThreadPool(10); // 10個線程
HConnection pool = HConnectionManager.createConnection(conf, threads);
HTableInterface hTable = pool.getTable("users");
try {
// testPut(hTable); // 參數是實現了接口的類實例
// testGet(hTable);
// testDelete(hTable);
testScan(hTable);
} finally {
hTable.close(); // 每次htable操作完 關閉 其實是放到pool中
pool.close(); // 最終的時候關閉
}
}
/**
* 測試scan
*
* @param hTable;接口
* @throws IOException
*/
static void testScan(HTableInterface hTable) throws IOException{
Scan scan = new Scan();
// 增加起始row key
scan.setStartRow(Bytes.toBytes("row1"));
scan.setStopRow(Bytes.toBytes("row5"));
// 使用指定布爾運算符求值的篩選器的有序列表
FilterList list = new FilterList(Operator.MUST_PASS_ALL); // 相當於AND
byte[][] prefixes = new byte[2][]; // 過濾前綴
prefixes[0] = Bytes.toBytes("id");
prefixes[1] = Bytes.toBytes("name");
// 創建多重列前綴過濾器實例
MultipleColumnPrefixFilter mcpf = new MultipleColumnPrefixFilter(prefixes);
list.addFilter(mcpf); // 添加過濾器
scan.setFilter(list); // scan操作設置過濾器
// Returns a scanner on the current table as specified
// by the {@link Scan} object.
ResultScanner rs = hTable.getScanner(scan);
Iterator<Result> iter = rs.iterator();
while (iter.hasNext()) {
// Single row result of a {@link Get} or {@link Scan} query.
Result result = iter.next();
printResult(result);
}
}
/**
* 打印result對象
*
* @param result
*/
static void printResult(Result result) {
System.out.println("*********************" + Bytes.toString(result.getRow())); // 獲取rowkey
//
// getMap(): 將族映射到其限定符和值的所有版本。
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = result.getMap();
// 遍歷map
for (Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> entry : map.entrySet()) {
String family = Bytes.toString(entry.getKey()); // 獲取family
for (Map.Entry<byte[], NavigableMap<Long, byte[]>> columnEntry : entry.getValue().entrySet()) {
String column = Bytes.toString(columnEntry.getKey()); // 獲取column
String value = "";
if ("age".equals(column)) {
value = "" + Bytes.toInt(columnEntry.getValue().firstEntry().getValue());
} else {
value = Bytes.toString(columnEntry.getValue().firstEntry().getValue());
}
System.out.println(family + ":" + column + ":" + value);
}
}
}
/**
* 測試put操作
*
* @param hTable
* @throws IOException
*/
static void testPut(HTableInterface hTable) throws IOException {
// 單個put
Put put = new Put(Bytes.toBytes("row1"));
put.add(Bytes.toBytes("f"), Bytes.toBytes("id"), Bytes.toBytes("11"));
put.add(Bytes.toBytes("f"), Bytes.toBytes("name"), Bytes.toBytes("zhangsan"));
put.add(Bytes.toBytes("f"), Bytes.toBytes("age"), Bytes.toBytes(27));
put.add(Bytes.toBytes("f"), Bytes.toBytes("phone"), Bytes.toBytes("021-11111111"));
put.add(Bytes.toBytes("f"), Bytes.toBytes("email"), Bytes.toBytes("[email protected]"));
hTable.put(put); // 執行put
// 同時put多個
Put put1 = new Put(Bytes.toBytes("row2"));
put1.add(Bytes.toBytes("f"), Bytes.toBytes("id"), Bytes.toBytes("2"));
put1.add(Bytes.toBytes("f"), Bytes.toBytes("name"), Bytes.toBytes("user2"));
Put put2 = new Put(Bytes.toBytes("row3"));
put2.add(Bytes.toBytes("f"), Bytes.toBytes("id"), Bytes.toBytes("3"));
put2.add(Bytes.toBytes("f"), Bytes.toBytes("name"), Bytes.toBytes("user3"));
Put put3 = new Put(Bytes.toBytes("row4"));
put3.add(Bytes.toBytes("f"), Bytes.toBytes("id"), Bytes.toBytes("4"));
put3.add(Bytes.toBytes("f"), Bytes.toBytes("name"), Bytes.toBytes("user4"));
List<Put> list = new ArrayList<Put>();
list.add(put1);
list.add(put2);
list.add(put3);
hTable.put(list); // 執行多個put
// 檢測put,條件成功就插入,要求rowkey是一樣的。
Put put4 = new Put(Bytes.toBytes("row5"));
put4.add(Bytes.toBytes("f"), Bytes.toBytes("id"), Bytes.toBytes("7"));
hTable.checkAndPut(Bytes.toBytes("row5"), Bytes.toBytes("f"), Bytes.toBytes("id"), null, put4);
System.out.println("插入成功");
}
/**
* 測試get命令
*
* @param hTable
* @throws IOException
*/
static void testGet(HTableInterface hTable) throws IOException {
// Create a Get operation for the specified row.
Get get = new Get(Bytes.toBytes("row1"));
// Single row result of a {@link Get} or {@link Scan} query.
Result result = hTable.get(get); // 執行get
// Get the latest version of the specified column.
byte[] buf = result.getValue(family, Bytes.toBytes("id")); // 獲取id
System.out.println("id:" + Bytes.toString(buf));
buf = result.getValue(family, Bytes.toBytes("age")); // 獲取age
System.out.println("age:" + Bytes.toInt(buf));
buf = result.getValue(family, Bytes.toBytes("name"));
System.out.println("name:" + Bytes.toString(buf));
buf = result.getRow();
System.out.println("row:" + Bytes.toString(buf));
}
/**
* 測試delete
*
* @param hTable
* @throws IOException
*/
static void testDelete(HTableInterface hTable) throws IOException {
// Create a Delete operation for the specified row.
Delete delete = new Delete(Bytes.toBytes("row3"));
// 刪除列
delete = delete.deleteColumn(family, Bytes.toBytes("id"));
// 直接刪除family
// delete.deleteFamily(family);
hTable.delete(delete); // 刪除rowkey(Delete delete = new Delete(Bytes.toBytes("row3"));)
System.out.println("刪除成功");
}
}