1、Hbase的特点
可用作hadoop数据库,提供分布式可伸缩大型数据存储。用户能随机、实时读写数据。存储十亿行 x 百万列数量级数据。是版本化、非关系型数据库。面向列存储,table是按row排序。
Feature
----------------
Linear and modular scalability. //线性模块化扩展方式。
Strictly consistent reads and writes. //严格一致性读写
Automatic and configurable sharding of tables //自动可配置表切割
Automatic failover support between RegionServers. //区域服务器之间自动容灾(只需要在用作hbase节点的服务器上开启两个HMaster进程就可实现自动容灾,zk节点下会有:/hbase/backup-master)
Convenient base classes for backing Hadoop MapReduce jobs with Apache HBase tables. //
Easy to use Java API for client access. //java API
Block cache and Bloom Filters for real-time queries //块缓存和布隆过滤器用于实时查询
Query predicate push down via server side Filters //通过服务器端过滤器实现查询预测
Thrift gateway and a REST-ful Web service that supports XML, Protobuf, and binary data encoding options //
Extensible jruby-based (JIRB) shell //
Support for exporting metrics via the Hadoop metrics subsystem to files or Ganglia; or via JMX //可视化
面向列数据库。
2、搭建Hbase集群
0.选择安装的主机
s201 ~ s204
1.jdk
略
2.hadoop
略
3.用tar命令解压Hbase压缩包
略
4.配置环境变量
略
5.验证安装是否成功
$>hbase version
6.配置hbase模式
6.1)本地模式
[hbase/conf/hbase-env.sh]
EXPORT JAVA_HOME=/soft/jdk
[hbase/conf/hbase-site.xml]
...
<property>
<name>hbase.rootdir</name>
<value>file:/home/hadoop/HBase/HFiles</value>
</property>
5.2)伪分布式
[hbase/conf/hbase-env.sh]
EXPORT JAVA_HOME=/soft/jdk
[hbase/conf/hbase-site.xml]
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property
<property>
<name>hbase.rootdir</name>
<value>hdfs://localhost:8030/hbase</value>
</property>
5.3)完全分布式
[hbase/conf/hbase-env.sh]
export JAVA_HOME=/soft/jdk
export HBASE_MANAGES_ZK=false
[hbse-site.xml]
<!-- 使用完全分布式 -->
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 指定hbase数据在hdfs上的存放路径 -->
<property>
<name>hbase.rootdir</name>
<value>hdfs://s201:8020/hbase</value>
</property>
<!-- 配置zk地址 -->
<property>
<name>hbase.zookeeper.quorum</name>
<value>s201:2181,s202:2181,s203:2181</value>
</property>
<!-- zk的本地目录 -->
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/home/centos/zookeeper</value>
</property>
7.配置regionservers
[hbase/conf/regionservers]
s202
s203
s204
8.启动hbase集群(s201)
$>start-hbase.sh
9.登录hbase的webui
http://s201:16010
3、Hbase的命令操作
hbase shell操作
------------------
$>hbase shell //登录shell终端.
$hbase>help //帮助菜单
$hbase>help 'list_namespace' //查看特定的命令帮助
$hbase>list_namespace //列出名字空间(名字空间相当于数据库)
$hbase>list_namespace_tables 'defalut' //列出名字空间(数据库)
$hbase>create_namespace 'ns1' //创建名字空间
$hbase>help 'create'
$hbase>create 'ns1:t1','f1' //创建表,指定空间下
$hbase>put 'ns1:t1','row1','f1:id',100 //插入数据(名字空间:表名,行名,列族名:列名,值)
$hbase>put 'ns1:t1','row1','f1:name','tom' //
$hbase>get 'ns1:t1','row1' //查询指定row
$hbase>scan 'ns1:t1' //扫描表
$hbase>flush 'ns1:t1' //清理内存数据到磁盘。
$hbase>count 'ns1:t1' //统计函数
$hbase>disable 'ns1:t1' //删除表之前需要禁用表
$hbase>drop 'ns1:t1' //删除表
$hbase>scan 'hbase:meta' //查看元数据表,所有的表信息都在这里存储
$hbase>split 'ns1:t1' //切割表,默认是10G切割
$hbase>split '' //切割区域
$hbase>merge_region '第一个区域的md5值' '第二个区域的md5值' //合并区域
$hbase>move '(64d1269c1776c4207e4346a495cbfcda)需要移动区域的md5值' 's2,16020,1529736825974(主机名,端口号,时间戳)' //移动区域到s2主机上
有关区域的md5值以及主机信息都可以在metadata上看到
4、通过编程API访问Hbase
1.创建hbase模块
2.添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ctgu</groupId>
<artifactId>HbaseDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>---------开始复制jar包到共享目录下----------</echo>
<delete file="J:\Program\java\hadoop\jar\HdfsDemo-1.0-SNAPSHOT.jar"></delete>
<copy file="target/HdfsDemo-1.0-SNAPSHOT.jar" toFile="J:\Program\java\hadoop\jar\HdfsDemo.jar">
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
复制hbase集群的hbase-site.xml文件到模块的src/main/resources目录下。
3.编程实现
cn.ctgu.hbasedemo.test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import java.io.IOException;
/**
*
*/
public class TestCRUD {
@Test
public void put() throws Exception {
//创建conf对象
Configuration conf = HBaseConfiguration.create();
//通过连接工厂创建连接对象
Connection conn = ConnectionFactory.createConnection(conf);
//通过连接查询tableName对象
TableName tname = TableName.valueOf("ns1:t1");
//获得table
Table table = conn.getTable(tname);
//通过bytes工具类创建字节数组(将字符串)
byte[] rowid = Bytes.toBytes("row3");
//创建put对象
Put put = new Put(rowid);
byte[] f1 = Bytes.toBytes("f1");
byte[] id = Bytes.toBytes("id") ;
byte[] value = Bytes.toBytes(102);
put.addColumn(f1,id,value);
//执行插入
table.put(put);
}
//查询
@Test
public void get() throws Exception {
//创建conf对象
Configuration conf = HBaseConfiguration.create();
//通过连接工厂创建连接对象
Connection conn = ConnectionFactory.createConnection(conf);
//通过连接查询tableName对象
TableName tname = TableName.valueOf("ns1:t1");
//获得table
Table table = conn.getTable(tname);
//通过bytes工具类创建字节数组(将字符串)
byte[] rowid = Bytes.toBytes("row3");
Get get = new Get(Bytes.toBytes("row3"));
Result r = table.get(get);
byte[] idvalue = r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
System.out.println(Bytes.toInt(idvalue));
}
}
百万数据批量插入
//百万数据写入的优化:关闭写前日志、关闭自动清理缓冲区
@Test
public void bigdataput() throws Exception {
DecimalFormat format=new DecimalFormat();
format.applyPattern("0000000");
long start = System.currentTimeMillis();
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t1");
HTable table = (HTable) conn.getTable(tname);
//不要自动清理缓冲区
table.setAutoFlush(false);
for (int i = 4; i < 1000000; i++) {
Put put = new Put(Bytes.toBytes("row" + format.format(i)));
//关闭写前日志
put.setWriteToWAL(false);
put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("id"), Bytes.toBytes(i));
put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("name"), Bytes.toBytes("tom" + i));
put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("age"), Bytes.toBytes(i % 100));
table.put(put);
//没写入2000条数据手动清理缓冲区
if (i % 2000 == 0) {
table.flushCommits();
}
}
//
table.flushCommits();
System.out.println(System.currentTimeMillis() - start);
}
//rowkey格式化,避免出现row=8出现在row=10008的后面,因为在hbase中是按字节数组存储的,比较rowkey的时候是按位进行比较的,所以会
//出现上面那种情况,将前面填充0
@Test
public void formatNum(){
DecimalFormat format=new DecimalFormat();
format.applyPattern("0000000");
System.out.println(format.format(8));//结果为:0000008
}
创建表时指定列族的版本数,该列族的所有列都具有相同数量版本
shell命令操作
$hbase>create 'ns1:t3',{NAME=>'f1',VERSIONS=>3} //创建表时,指定列族的版本数。
$hbase>get 'ns1:t3','row1',{COLUMN=>'f1',VERSIONS=>4} //检索的时候,查询多少版本。
API实现
@Test
public void getWithVersions() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t3");
Table table = conn.getTable(tname);
Get get = new Get(Bytes.toBytes("row1"));
//检索所有版本
get.setMaxVersions();
Result r = table.get(get);
List<Cell> cells = r.getColumnCells(Bytes.toBytes("f1"), Bytes.toBytes("name"));
for(Cell c : cells){
String f = Bytes.toString(c.getFamily());//获取列族
String col = Bytes.toString(c.getQualifier());//获取列名
long ts = c.getTimestamp();//获取时间戳
String val = Bytes.toString(c.getValue());//获取值
System.out.println(f + "/" + col + "/" + ts + "=" + val);
}
}
原生扫描
1.原生扫描
$hbase>scan 'ns1:t3',{COLUMN=>'f1',RAW=>true,VERSIONS=>10} //包含标记了delete的数据
2.删除数据
$hbase>delete 'nd1:t3','row1','f1:name',148989875645 //删除数据,标记为删除.小于该删除时间的数据都作废。
3.TTL
time to live ,存活时间。
影响所有的数据,包括没有删除的数据。
超过该时间,原生扫描也扫不到数据。
$hbase>create 'ns1:tx' , {NAME=>'f1',TTL=>10,VERSIONS}
4.KEEP_DELETED_CELLS
删除key之后,数据是否还保留。
$hbase>create 'ns1:tx' , {NAME=>'f1',TTL=>10,VERSIONS,KEEP_DELETED_CELLS=>true}
API操作
//创建名字空间(即数据库)
@Test
public void createNameSpace() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection();
Admin admin=conn.getAdmin();
//创建名字空间描述符
NamespaceDescriptor nsd=NamespaceDescriptor.create("ns2").build();
admin.createNamespace(nsd);
}
@Test
public void listNameSpace() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection();
Admin admin=conn.getAdmin();
//获取名字空间描述符
NamespaceDescriptor[]ns=admin.listNamespaceDescriptors();
for(NamespaceDescriptor n:ns){
System.out.println(n.getName());
}
}
//创建表
@Test
public void createTable() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
//获取管理员对象
Admin admin=conn.getAdmin();
//创建表名对象
TableName tableName=TableName.valueOf("ns2:t2");
//创建表描述符对象
HTableDescriptor tb1=new HTableDescriptor(tableName);
//创建列族描述符
HColumnDescriptor col=new HColumnDescriptor("f1");
tb1.addFamily(col);
admin.createTable(tb1);
System.out.println("over");
}
//禁用表和删除表,表只有先禁用才能删除
@Test
public void disableTable() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
Admin admin=conn.getAdmin();
//禁用表
admin.disableTable(TableName.valueOf("ns2:t2"));
//启用表
//admin.enableTable(TableName.valueOf("ns2:t2"));
//删除表
admin.deleteTable(TableName.valueOf("ns2:t2"));
}
//删除数据
@Test
public void deleteData() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tname=TableName.valueOf("ns1:t1");
Table table=conn.getTable(tname);
Delete del=new Delete(Bytes.toBytes("row0001"));
del.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("id"));
del.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("name"));
table.delete(del);
System.out.println("over");
}
//扫描数据,避免扫描全部数据,因为数据量很大,耗时长,设置起始结束row或者表
@Test
public void scan() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tnam=TableName.valueOf("ns1:t1");
Table table=conn.getTable(tnam);
Scan scan=new Scan();
scan.setStartRow(Bytes.toBytes("row5000"));//设置起始行
scan.setStopRow(Bytes.toBytes("row8000"));//设置结束行
ResultScanner rs=table.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();//r是行
byte[]name=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("name"));
System.out.println(Bytes.toString(name));
}
}
//动态遍历
@Test
public void scan2() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tnam=TableName.valueOf("ns1:t1");
Table table=conn.getTable(tnam);
Scan scan=new Scan();
scan.setStartRow(Bytes.toBytes("row5000"));//设置起始行
scan.setStopRow(Bytes.toBytes("row8000"));//设置结束行
ResultScanner rs=table.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();//r是行
Map<byte[],byte[]>map=r.getFamilyMap(Bytes.toBytes("f1"));//获取每一列族
for(Map.Entry<byte[],byte[]>entrySet:map.entrySet()){
String col=Bytes.toString(entrySet.getKey());
String val=Bytes.toString(entrySet.getValue());
System.out.println(col+":"+val+",");
}
System.out.println();
}
}
@Test
public void scan3() throws Exception {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t1");
Table table = conn.getTable(tname);
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("row5000"));
scan.setStopRow(Bytes.toBytes("row8000"));
ResultScanner rs = table.getScanner(scan);
Iterator<Result> it = rs.iterator();
while (it.hasNext()) {
Result r = it.next();
//得到一行的所有map,key=f1,value=Map<Col,Map<Timestamp,value>>
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = r.getMap();
//
for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> entry : map.entrySet()){
//得到列族
String f = Bytes.toString(entry.getKey());
Map<byte[], NavigableMap<Long, byte[]>> colDataMap = entry.getValue();//列数据
for(Map.Entry<byte[], NavigableMap<Long, byte[]>> ets : colDataMap.entrySet() ){
String c = Bytes.toString(ets.getKey());//列
Map<Long, byte[]> tsValueMap = ets.getValue();
for(Map.Entry<Long,byte[]> e : tsValueMap.entrySet()){
Long ts = e.getKey() ;//时间戳
String value = Bytes.toString(e.getValue());//值
System.out.print(f+":"+c+":"+ts+"=" +value + ",");
}
}
}
System.out.println();
}
}
缓存和批处理
开启服务器端扫描器缓存
a)表层面(全局)
<property>
<name>hbase.client.scanner.caching</name>
<!-- 缓存整数最大值 -->
<value>2147483647</value>
<source>hbase-default.xml</source>
</property>
b)操作层面
//设置量,即扫描一次获取到多少数据
scan.setCaching(10);
扫描器缓存
面向行级别的,每次推送多少行,而不是单独提取一行,降低交互的次数,提高效率。
API操作
//扫描器缓存
@Test
public void getScanCache() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t1");
Scan scan = new Scan();
scan.setCaching(5000);//设置缓存大小
Table t = conn.getTable(tname);
ResultScanner rs = t.getScanner(scan);
long start = System.currentTimeMillis() ;
Iterator<Result> it = rs.iterator();
while(it.hasNext()){
Result r = it.next();//一次获取5000行数据
System.out.println(r.getColumnLatestCell(Bytes.toBytes("f1"), Bytes.toBytes("name")));
}
System.out.println(System.currentTimeMillis() - start);
}
批量扫描是面向列级别
控制每次next()服务器端返回的列的个数。
scan.setBatch(5); //每次next返回5列。
/**
* 测试缓存和批处理
*/
@Test
public void testBatchAndCaching() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t7");
Scan scan = new Scan();
scan.setCaching(2);//设置缓存为2行
scan.setBatch(4);//设置批为4,即列为4
Table t = conn.getTable(tname);
ResultScanner rs = t.getScanner(scan);
Iterator<Result> it = rs.iterator();
while (it.hasNext()) {
Result r = it.next();
System.out.println("========================================");
//得到一行的所有map,key=f1,value=Map<Col,Map<Timestamp,value>>
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> map = r.getMap();
//
for (Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> entry : map.entrySet()) {
//得到列族
String f = Bytes.toString(entry.getKey());
Map<byte[], NavigableMap<Long, byte[]>> colDataMap = entry.getValue();
for (Map.Entry<byte[], NavigableMap<Long, byte[]>> ets : colDataMap.entrySet()) {
String c = Bytes.toString(ets.getKey());
Map<Long, byte[]> tsValueMap = ets.getValue();
for (Map.Entry<Long, byte[]> e : tsValueMap.entrySet()) {
Long ts = e.getKey();
String value = Bytes.toString(e.getValue());
System.out.print(f + "/" + c + "/" + ts + "=" + value + ",");
}
}
}
System.out.println();
}
}
过滤器
//测试行过滤器
@Test
public void testFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//过滤出行小于等于100的行
RowFilter rowFilter=new RowFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new BinaryComparator(Bytes.toBytes("row0100")));
scan.setFilter(rowFilter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
System.out.println(Bytes.toString(r.getRow()));
}
}
//测试行过滤器
@Test
public void testFamilyFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//过滤出列族小于等于f2的列族
FamilyFilter filter=new FamilyFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new BinaryComparator(Bytes.toBytes("f2")));
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
System.out.println(f1id+":"+f2id);
}
}
//测试列过滤器
@Test
public void testColFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//过滤出列为id的列
QualifierFilter filter=new QualifierFilter(CompareFilter.CompareOp.EQUAL,new BinaryComparator(Bytes.toBytes("id")));
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
* 测试ValueFilter(值过滤器)
* 过滤value的值,含有指定的子字符串
*
*
* */
@Test
public void testValueFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//过滤出含有"1.1"字串的值
ValueFilter filter=new ValueFilter(CompareFilter.CompareOp.EQUAL,new SubstringComparator("1.1"));
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
//测试依赖列过滤器
@Test
public void testDepFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//该过滤器尝试找到该f2列族所在的addr列,并返回该addr不等于beijing的全部键值对。
DependentColumnFilter filter=new DependentColumnFilter(Bytes.toBytes("f2"),
Bytes.toBytes("addr"),
true,//为true删除,false不删除
CompareFilter.CompareOp.NOT_EQUAL,
new BinaryComparator(Bytes.toBytes("beijing"))
);
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
* 单列值value过滤
* 如果value不满足,整行过滤掉
*
* */
@Test
public void testSingleFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//该过滤器尝试找到该f2列族所在列name不为tom2.2的行,如果为tom2.2则整行过滤掉
SingleColumnValueFilter filter=new SingleColumnValueFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("name"),
CompareFilter.CompareOp.NOT_EQUAL,
new BinaryComparator(Bytes.toBytes("tom2.2"))
);
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
* 单列值排除过滤器
* 去掉过滤器使用的列,对列值进行过滤
*
* */
@Test
public void testSingleColumnValueFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//该过滤器过滤掉(即删除)当列name为tom2.2的整列
SingleColumnValueExcludeFilter filter=new SingleColumnValueExcludeFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("name"),
CompareFilter.CompareOp.EQUAL,
new BinaryComparator(Bytes.toBytes("tom2.2"))
);
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
*前缀过滤,是rowkey过滤
* */
@Test
public void testPrefixFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//该过滤器过滤出rowkey包含row22的所有列
PrefixFilter filter=new PrefixFilter(Bytes.toBytes("row22"));
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
*分页过滤器,在region上扫描时,对每次page设置的大小
* 返回给客户端涉及到每个结果的合并
* 假如该表分为3个区,每个分页设置为10,则返回30行数据
* */
@Test
public void testPageFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//该过滤器使得表的每个区返回10行
PageFilter filter=new PageFilter(10);
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
*KeyOnly过滤器,只提取key,丢弃value
* */
@Test
public void testKeyOnlyFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//丢弃value,只要key,如果参数为空,则打印出value为null
KeyOnlyFilter filter=new KeyOnlyFilter();
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
/*
*
*列分页过滤器,过滤指定范围列
* */
@Test
public void testColumnPageFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Scan scan=new Scan();
//过滤掉前两列,然后取后两列
ColumnPaginationFilter filter=new ColumnPaginationFilter(2,2);
scan.setFilter(filter);
Table t=conn.getTable(tableName);
ResultScanner rs=t.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
Result r=it.next();
byte[]f1id=r.getValue(Bytes.toBytes("f1"),Bytes.toBytes("id"));
byte[]f2id=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("id"));
byte[]f2name=r.getValue(Bytes.toBytes("f2"),Bytes.toBytes("name"));
System.out.println(f1id+":"+f2id+":"+f2name);
}
}
//组合过滤器
/* 相当于下面的sql语句
select * from t7 where ((age <= 13) and (name like '%t')
or
(age > 13) and (name like 't%'))*/
@Test
public void testComboFilter() throws IOException {
Configuration conf = HBaseConfiguration.create();
Connection conn = ConnectionFactory.createConnection(conf);
TableName tname = TableName.valueOf("ns1:t7");
Scan scan = new Scan();
//where ... f2:age <= 13
SingleColumnValueFilter ftl = new SingleColumnValueFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("age"),
CompareFilter.CompareOp.LESS_OR_EQUAL,
new BinaryComparator(Bytes.toBytes("13"))
);
//where ... f2:name like %t
SingleColumnValueFilter ftr = new SingleColumnValueFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("name"),
CompareFilter.CompareOp.EQUAL,
new RegexStringComparator("^t")//匹配正则表达式
);
//ft
FilterList ft = new FilterList(FilterList.Operator.MUST_PASS_ALL);
ft.addFilter(ftl);
ft.addFilter(ftr);
//where ... f2:age > 13
SingleColumnValueFilter fbl = new SingleColumnValueFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("age"),
CompareFilter.CompareOp.GREATER,
new BinaryComparator(Bytes.toBytes("13"))
);
//where ... f2:name like %t
SingleColumnValueFilter fbr = new SingleColumnValueFilter(
Bytes.toBytes("f2"),
Bytes.toBytes("name"),
CompareFilter.CompareOp.EQUAL,
new RegexStringComparator("t$")
);
//ft
FilterList fb = new FilterList(FilterList.Operator.MUST_PASS_ALL);
fb.addFilter(fbl);
fb.addFilter(fbr);
FilterList fall = new FilterList(FilterList.Operator.MUST_PASS_ONE);
fall.addFilter(ft);
fall.addFilter(fb);
scan.setFilter(fall);
Table t = conn.getTable(tname);
ResultScanner rs = t.getScanner(scan);
Iterator<Result> it = rs.iterator();
while (it.hasNext()) {
Result r = it.next();
byte[] f1id = r.getValue(Bytes.toBytes("f1"), Bytes.toBytes("id"));
byte[] f2id = r.getValue(Bytes.toBytes("f2"), Bytes.toBytes("id"));
byte[] f1name = r.getValue(Bytes.toBytes("f1"), Bytes.toBytes("name"));
byte[] f2name = r.getValue(Bytes.toBytes("f2"), Bytes.toBytes("name"));
System.out.println(f1id + " : " + f2id + " : " + Bytes.toString(f1name) + " : " + Bytes.toString(f2name));
}
}
计数器
$hbase>incr 'ns1:t8','row1','f1:click',1//incr 设置计数器,每次加1
$hbase>get_counter 'ns1:t8','row1','f1:click'
API编程实现
/*
*
*测试计数器
* */
@Test
public void testIncr() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tableName=TableName.valueOf("ns1:t5");
Table t=conn.getTable(tableName);
Increment incr=new Increment(Bytes.toBytes("row1"));
incr.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("daily"),1);
incr.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("weekly"),10);
incr.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("monthly"),100);
t.increment(incr);
}
5、Hbase架构原理
5.1 Hbase写入过程
hbase是基于hdfs的,相同列族的数据存放在一个文件中。分为表数据存储目录和WAL目录,WAL(write ahead log,写前日志),写入数据的时候会先进行写前日志的记录,以便宕机的时候能恢复相关数据。
[表数据的存储目录结构构成]
hdfs://s201:8020/hbase/data/${名字空间}/${表名}/${区域名称}/${列族名称}/${文件名}
[WAL目录结构构成]
hdfs://s201:8020/hbase/WALs/${区域服务器名称,主机名,端口号,时间戳}/
5.2 client端交互过程
0.hbase集群启动时,master负责分配区域到指定区域服务器。
1.联系zk,找出meta表所在rs(regionserver)
/hbase/meta-region-server
2.定位row key,找到对应region server(Hbase的查询过程是三级定位,所以速度会很快,能实现实时)
3.将超过MemStore的数据通过HFile缓存在本地磁盘。
4.联系RegionServer
5.HRegionServer负责open HRegion对象,为每个列族创建Store对象,Store包含多个StoreFile实例,他们是对HFile的轻量级封装。每个Store还对应了一个MemStore,用于内存存储数据。
HMaster在功能上主要负责Table表和HRegion的管理工作,具体包括:
1、管理用户对Table表的增、删、改、查操作;
2、管理HRegion服务器的负载均衡,调整HRegion分布;
3、在HRegion分裂后,负责新HRegion的分配;
4、在HRegion服务器停机后,负责失效HRegion服务器上的HRegion迁移。
hbase会切割文件,默认大小是10G进行切割。
<property>
<name>hbase.hregion.max.filesize</name>
<value>10737418240</value>
<source>hbase-default.xml</source>
</property>
6、hbase和hadoop的ha集成
1.在hbase-env.sh文件添加hadoop配置文件目录到HBASE_CLASSPATH环境变量并分发到其他几台Hbase节点上.
[/soft/hbase/conf/hbase-env.sh]
export HBASE_CLASSPATH=$HBASE_CLASSPATH:/soft/hadoop/etc/hadoop
2.在hbase/conf/目录下创建到hadoop的hdfs-site.xml符号连接。
$>ln -s /soft/hadoop/etc/hadoop/hdfs-site.xml /soft/hbase/conf/hdfs-site.xml
3.修改hbase-site.xml文件中hbase.rootdir的目录值。
[/soft/hbase/conf/hbase-site.xml]
<property>
<name>hbase.rootdir</name>
<value>hdfs://mycluster/hbase</value>
</property>
4.将以上步骤的配置文件分发处理。
7、拆分风暴
7.1 什么叫拆分风暴
当一个region里的存储文件增长到大于配置的hbase.hregion.max.filesize大小(默认为10G)或者在列族
层面配置的大小时,region会被一分为二,region服务器通过在父region中创建splits目录来完成
这个过程,接下来关闭该region,此后这个region不再接受任何请求。
当往hbase中写数据的时候有可能多个region同时达到10G,则它们会同时切割,这就可能导致服务器工作瞬间增大,这就是所谓的切割风暴。
7.2 预防手段——预先切割
创建表时,预先对表进行切割。切割线是rowkey。
$hbase>create 'ns1:t2','f1',SPLITS=>['row3000','row6000']
8、协处理器(coprocessor),相当于一个拦截器
批处理的,等价于存储过程或者触发器
[Observer]
观察者,类似于触发器,基于事件。发生动作时,回调相应方法。
RegionObserver //RegionServer区域观察者
MasterObserver //Master节点。
WAlObserver //
[Endpoint]
终端,类似于存储过程。
1、关闭hbase集群并配置
[hbase-site.xml]
<property>
<name>hbase.coprocessor.region.classes</name>
<value>coprocessor.RegionObserverExample, coprocessor.AnotherCoprocessor</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>coprocessor.MasterObserverExample</value>
</property>
<property>
<name>hbase.coprocessor.wal.classes</name>
<value>coprocessor.WALObserverExample, bar.foo.MyWALObserver</value>
</property>
2、自定义观察者
package cn.ctgu.hbasedemo.coprocessor;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Durability;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
/*
*
* 自定义区域观察者
*
* */
public class MyRegionObserver extends BaseRegionObserver{
private void outInfo(String str){
try {
FileWriter fw = new FileWriter("/home/hadoop/coprocessor.txt",true);
fw.write(str + "\r\n");
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void start(CoprocessorEnvironment e) throws IOException {
super.start(e);
outInfo("MyRegionObserver.start()");
}
public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
super.preOpen(e);
outInfo("MyRegionObserver.preOpen()");
}
public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
super.postOpen(e);
outInfo("MyRegionObserver.postOpen()");
}
public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
super.preGetOp(e, get, results);
String rowkey = Bytes.toString(get.getRow());
outInfo("MyRegionObserver.preGetOp() : rowkey = " + rowkey);
}
public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
super.postGetOp(e, get, results);
String rowkey = Bytes.toString(get.getRow());
outInfo("MyRegionObserver.postGetOp() : rowkey = " + rowkey);
}
public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
super.prePut(e, put, edit, durability);
String rowkey = Bytes.toString(put.getRow());
outInfo("MyRegionObserver.prePut() : rowkey = " + rowkey);
}
public void postPut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
super.postPut(e, put, edit, durability);
String rowkey = Bytes.toString(put.getRow());
outInfo("MyRegionObserver.postPut() : rowkey = " + rowkey);
}
public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete, WALEdit edit, Durability durability) throws IOException {
super.preDelete(e, delete, edit, durability);
String rowkey = Bytes.toString(delete.getRow());
outInfo("MyRegionObserver.preDelete() : rowkey = " + rowkey);
}
public void postDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete, WALEdit edit, Durability durability) throws IOException {
super.postDelete(e, delete, edit, durability);
String rowkey = Bytes.toString(delete.getRow());
outInfo("MyRegionObserver.postDelete() : rowkey = " + rowkey);
}
}
3、注册协处理器并分发
<property>
<name>hbase.coprocessor.region.classes</name>
<value>cn.ctgu.hbasedemo.coprocessor.MyRegionObserver</value>
</property>
4、导出jar包
5、复制jar到共享目录,分发到jar到hbase集群的hbase lib目录下.
开启hbase集群会执行
outInfo("MyRegionObserver.start()");
outInfo("MyRegionObserver.preOpen()");
集群开启之后会执行
outInfo("MyRegionObserver.postOpen()");
获取操作之前会执行
outInfo("MyRegionObserver.preGetOp() : rowkey = " + rowkey);
获取操作之后执行
outInfo("MyRegionObserver.postGetOp() : rowkey = " + rowkey);
执行查询动作之前执行
outInfo("MyRegionObserver.prePut() : rowkey = " + rowkey);
执行查询动作之后执行
outInfo("MyRegionObserver.postPut() : rowkey = " + rowkey);
删除动作之前执行
outInfo("MyRegionObserver.preDelete() : rowkey = " + rowkey);
删除动作之后执行
outInfo("MyRegionObserver.postDelete() : rowkey = " + rowkey);
9、热点问题
所谓的热点问题就是数据分布不均匀,有些RegionServer上存储着大量数据,有些RegionServer上则存储着较少数据,导致负载不均衡。
为了解决热点问题,需要将数据均匀分散,对rowkey进行分区编号就是解决热点问题的手段之一,它是通过对md5值就行改造,将数据均匀分散在多个RegionServer上。
案例——通话记录优化
1.创建表
create 'ns1:calllogs','f1'
2.创建单元测试
package cn.ctgu.hbasedemo.test;
import hbasedemo.coprocessor.Util;
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.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.regionserver.BloomType;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
/*
*
* 通话日志
* */
public class TestCallLog {
/*
*
* rowkey结构设计:
*
* 区号,主叫或被叫,时间,方向0-主 1-被,对方号码,时长
* xx callerid time direction calleid duration
*
* */
@Test
public void put() throws Exception {
Configuration conf= HBaseConfiguration.create();
Connection conn= ConnectionFactory.createConnection(conf);
TableName tname=TableName.valueOf("ns1:calllogs");
Table table=conn.getTable(tname);
String callerId="13845450000";
String calleeId="13989891111";
SimpleDateFormat sdf=new SimpleDateFormat();
sdf.applyPattern("yyyyMMddHHmmss");
String callTimes=sdf.format(new Date());
int duration=100;
DecimalFormat dff=new DecimalFormat();
dff.applyPattern("00000");
String durstr=dff.format(duration);
//区域00-99,substring(0,6)这是将同一个号码的相同年月份的数据作为一个rowkey
int hash=(callerId+callTimes.substring(0,6)).hashCode();
hash=(hash&Integer.MAX_VALUE)%100;//防止出现负数
//hash区域号
DecimalFormat df=new DecimalFormat();
df.applyPattern("00");
String regNo=df.format(hash);
//拼接rowkey
//xx callerid time direction calleid duration
String rowkey=regNo+","+callerId+","+callTimes+","+"0,"+calleeId+","+durstr;
byte[]rowid= Bytes.toBytes(rowkey);
Put put=new Put(rowid);
put.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("callerPos"),Bytes.toBytes("河北"));
put.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("callerPos"),Bytes.toBytes("河南"));
//执行插入
table.put(put);
System.out.println("over");
}
//插入多条通话记录
@Test
public void puts() throws Exception {
for(int i=0;i<10;i++){
put();
Thread.sleep(1000);
}
}
//打印通话记录,查询的时候只需要查询rowkey即可
@Test
public void printCallLogs() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
TableName tname=TableName.valueOf("ns1:calllogs");
Table table=conn.getTable(tname);
Scan scan=new Scan();
String callerId="13845450000";
String month="201806";
String regNo= Util.getRegNo(callerId,month);
String startKey=regNo+","+callerId+","+month;
scan.setStartRow(Bytes.toBytes(startKey));
String stopKey=regNo+","+callerId+","+"201807";
scan.setStopRow(Bytes.toBytes(stopKey));
ResultScanner rs=table.getScanner(scan);
Iterator<Result>it=rs.iterator();
while(it.hasNext()){
String row=Bytes.toString(it.next().getRow());
System.out.println(row);
}
System.out.println("over");
}
//测试布隆过滤器,
@Test
public void createBloomFilter() throws Exception {
Configuration conf=HBaseConfiguration.create();
Connection conn=ConnectionFactory.createConnection(conf);
Admin admin=conn.getAdmin();
TableName tableName=TableName.valueOf("ns1:t10");
HTableDescriptor tbl=new HTableDescriptor(tableName);
HColumnDescriptor col=new HColumnDescriptor("f1");
col.setBloomFilterType(BloomType.ROW);
tbl.addFamily(col);
admin.createTable(tbl);
System.out.println("over");
}
}
3.创建协处理器
Util.java
package hbasedemo.coprocessor;
import java.text.DecimalFormat;
public class Util {
public static String getRegNo(String callerId,String callTime){
//区域00-99
int hash=(callerId+callTime.substring(0,6)).hashCode();
hash=(hash & Integer.MAX_VALUE)%100;
//hash区域号
DecimalFormat df=new DecimalFormat();
df.applyPattern("00");
String regNo=df.format(hash);
return regNo;
}
}
CalleeLogRegionObserver.java
package hbasedemo.coprocessor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.FileWriter;
import java.io.IOException;
/*
*
* 自定义区域观察者
*
* */
public class CalleeLogRegionObserver extends BaseRegionObserver{
//postPut是在put执行完后执行的
public void postPut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
FileWriter fw=new FileWriter("/home/hadoop/regionLog.txt",true);
super.postPut(e,put,edit,durability);
//
String tableName0=TableName.valueOf("ns1:calllogs").getNameAsString();
//得到当前的tableName对象
String tableName1=e.getEnvironment().getRegion().getRegionInfo().getTable().getNameAsString();
fw.write(tableName1+"\r\n");
if(!tableName0.equals(tableName1)){
return;
}
//得到主叫的rowkey
//xx callerid time direction calleid duration
//被叫:calleid,time
//通话记录不仅需要在主叫的区域上写入,在被叫的区域上也得写入(这个工作交由协处理器完成)
String rowkey=Bytes.toString(put.getRow());
String[]arr=rowkey.split(",");
if(arr[3].equals("1")){
return;
}
String hash=Util.getRegNo(arr[4],arr[2]);
String newRowKey=hash+","+arr[4]+","+arr[2]+",1,"+arr[1]+","+arr[5];
Put newPut=new Put(Bytes.toBytes(newRowKey));
newPut.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("dummy"),Bytes.toBytes("no"));
TableName tn=TableName.valueOf("ns1:calllogs");
Table t=e.getEnvironment().getTable(tn);
fw.write(t.getName().getNameAsString()+"\r\n");
t.put(newPut);
fw.close();
}
}
4.配置hbase-site.xml并分发
<property>
<name>hbase.coprocessor.region.classes</name>
<value>hbasedemo.coprocessor.CalleeLogRegionObserver</value>
</property>
5.启动hbase集群.
10、phonix
phonix是建立在Hbase基础之上的,将hbase复杂的操作转化为熟悉的mysql操作的一种工具。
1.安装phonix
a)下载apache-phoenix-4.10.0-HBase-1.2-bin.tar.gz
b)tar
c)复制xxx-server.jar到hbase的lib目录,并且分发,删除以前的phonixjar包。
d)重启hbase
2.使用phonix的命令行程序
phonix/bin/$> ./sqlline.py s202 //连接的是zk服务器
$phonix>!tables
$phonix>!help //查看帮助
3、SQL Client安装
a)下载squirrel-sql-3.7.1-standard.jar
该文件是安装文件,执行的安装程序。
$>jar -jar squirrel-sql-3.7.1-standard.jar
$>下一步...
b)复制phoenix-4.10.0-HBase-1.2-client.jar到SQuerrel安装目录的lib下(c:\myprograms\squirrel)。
c)启动SQuirrel(GUI)
定位安装目录->执行squirrel-sql.bat
d)打开GUI界面
d)在左侧的边栏选中"Drivers"选项卡,
点击 "+" ->
URL:jdbc:phoenix:192.168.231.202
Driverclass:org.apache.phoenix.jdbc.PhoenixDriver
jdbc:phoenix: s202
d)测试。
4.SQLLine客户端操作
//建表
$jdbc:phoenix>create table IF NOT EXISTS test.Person (IDCardNum INTEGER not null primary key, Name varchar(20),Age INTEGER);
//插入数据
$jdbc:phoenix>UPSERT INTO test.PERSON(IDCardNum , Name,Age) VALUES (1,'tom',12);
//删除数据
$jdbc:phoenix>delete from test.persion where idcardnum = 1 ;
//更新数据
//upsert into test.PERSON(IDCardNum , Name,Age) VALUES (1,'tom',12);
将hbase的表影射到hive上,使用hive的查询语句
CREATE TABLE t11(key string, name string) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf:name")
TBLPROPERTIES("hbase.table.name" = "ns1:t11");
select count(*) from mydb.t11 ;
5. hbase bulk load
导出hbase表文件到hdfs
1.复制hbase的jar文件和metrices-core-xxx.jar文件到hadoop类路径下.
$>cd /soft/hbase/lib
$>ls | grep hbase | cp `xargs` /soft/hadoop/shared/hadoop/common/lib
$>ls | grep metric-core | cp `xargs`/soft/hadoop/shared/hadoop/common/lib
2.执行hbase-server-VERSION.jar下的MR程序,导出hbase的数据到hdfs中。
$>hadoop jar hbase-server-VERSION.jar export ns1:calllogs outtt
大批量导入hbase hfile文件到hbase的表中。
[原理]
通过hbase提供的MR程序(改程序位于hbase-server-VERSION.jar中),将从其他hbase集群中的表复制出来的目录结构上传到自己的hdfs上,并将该目录指定为mr程序的输入目录,直接导入到自己的hbase表中。
[过程]
1.复制hbase在hdfs上的表一级目录到自己的hdfs文件系统。
2.通过hbase的completebulkload命令实现数据加载。
hadoop jar hbase-server-1.2.3.jar completebulkload /hbase/data/ns1/mytable000 ns1:calllogs
将关系型数据库数据(mysql、oracle)导入到hbase中。