一、Hbase集群安装与常见问题解决
首先说明,要使用hbase是需要先安装hadoop和zookeeper的(也可以使用自带的但是不建议),参考[zookeeper集群安装]
[hadoop集群安装]
我用的是三台机器,mini1,mini2,mini3
Hbase的安装流程
1、将hbase上传到hadoop集群,我这里上传的是hbase-0.99.2-bin.tar.gz。然后解压并且重命名为hbase,为了方便管理我讲hbase移动到了apps目录下。(上传、解压、重命名)
[root@mini1 ~]# tar –zxvf hbase-0.99.2-bin.tar.gz
[root@mini1 ~]# mv hbase-0.99.2 hbase
[root@mini1 ~]# mv hbase apps
[root@mini1 ~]# cd apps/
[root@mini1 apps]# ll
总用量 20
drwxr-xr-x. 8 root root 4096 10月 19 15:15 apache-flume-1.6.0-bin
drwxrwxr-x. 10 hadoop hadoop 4096 9月 30 22:04 hadoop-2.6.4
drwxr-xr-x. 7 root root 4096 10月 30 00:20 hbase
drwxr-xr-x. 8 root root 4096 10月 17 12:38 hive
drwxr-xr-x. 10 root root 4096 10月 29 23:21 zookeeper-3.4.6
2、修改环境变量
在编辑模式下,最后面添加
[root@mini1 apps]# vi /etc/profile
...
export HBASE_HOME=/root/apps/hbase
export PATH=$PATH:$HBASE_HOME/bin
[root@mini1 apps]#source /etc/profile
在机器mini2,mini3上也执行2操作修改环境变量
3、修改配置文件
修改的是 hbase-env.sh,hbase-site.xml, regionservers
我们现在文本编辑器里面修改再上传覆盖就好了
hbase-env.sh添加内容如下
第一行指定jdk位置
第二行指定额外的classpath元素
第三行是关于jvm的可以不需要
第四行,true表示使用hbase自带的zookeeper,false表示使用自己的zookeeper(推荐,至少趁此学一下zookeeper,相信学了hbase后面的storm跟spark也会去学,后面很多都要使用zookeeper,一劳永逸的事)。
export JAVA_HOME=/heima32/jdk1.7.0_55/
export JAVA_CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export HBASE_OPTS="-XX:+UseConcMarkSweepGC"
export HBASE_MANAGES_ZK=false
hbase-site.xml内容如下
<configuration>
<property>
<name>hbase.master</name>
<value>mini1:60000</value>
<description>指定hbase的主节点与端口号</description>
</property>
<property>
<name>hbase.master.maxclockskew</name>
<value>180000</value>
<description>时间同步允许的时间差</description>
</property>
<property>
<name>hbase.rootdir</name>
<value>hdfs://mini1:9000/hbase</value>
<description>hbase共享目录,持久化hbase数据,这个需要看你hadoop的核心文件里面配置的是不是mini1:9000不是的话得改成自己的hadoop里面写的</description>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
<description>是否是分布式的,当然</description>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>mini1,mini2,mini3</value>
<description>指定zookeeper,我的zookeeper集群就是在这三台机器的</description>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/root/hbase/tmp/zookeeper</value>
<description>zookeeper配置信息快照的位置,目录会自己创建的</description>
</property>
</configuration>
regionservers里面添加从节点机器名
mini2
mini3
然后将三个文件上传到hbase的conf目录下
4、将按照配置好的hbase上传到其他机器
这里是mini1配置好了,发送到mini2和mini3机器
[root@mini1 ~]# scp -r /root/apps/hbase mini2:/root/apps
[root@mini1 ~]# scp -r /root/apps/hbase mini3:/root/apps
5、启动hbase
进入到mini1的hbase的bin目录下启动
启动完了使用jsp命令查看是否启动起来了
[root@mini1 bin]# ./start-hbase.sh
starting master, logging to /root/apps/hbase/logs/hbase-root-master-mini1.out
mini2: starting regionserver, logging to /root/apps/hbase/bin/../logs/hbase-root-regionserver-mini2.out
mini3: starting regionserver, logging to /root/apps/hbase/bin/../logs/hbase-root-regionserver-mini3.out
[root@mini1 bin]# jps
1593 ResourceManager
7133 HMaster
4550 QuorumPeerMain
2831 NameNode
7310 Jps
注:启动起来的前提需要启动hadoop和zookeeper,最后启动的话,那么mini1机器上会多出HMaster进程,mini2和mini3上多出了HRegionServer。
常见问题解决
这些问题都是我遇到的
下面的是自己的hosts文件,三台机器一样
[root@mini1 bin]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost.jinbm
192.168.25.127 mini1
192.168.25.129 mini2
192.168.25.130 mini3
1、如果启动不起来,查看日志出现下面的异常
Cause:
org.apache.hadoop.hbase.ClockOutOfSyncException: org.apache.hadoop.hbase.ClockOutOfSyncException:
Server mini2,16020,1508608510960 has been rejected; Reported time is too far out of sync with master.
Time difference of 428987358ms > max allowed of 180000ms
出现这个问题是当时我mini2这台机器和mini1的时间差太多了记得是4天,明显大于配置文件中写的时间同步允许的时间差也就是180000ms。
同步时间即可
ntpdate没有的话安装就好了
每台机器都像下面这样执行
[root@mini1 bin]# ntpdate
-bash: ntpdate: command not found
[root@mini1 bin]# yum install ntp
...
[root@mini1 bin]# ntpdate
30 Oct 06:21:25 ntpdate[10542]: no servers can be used, exiting
[root@mini1 bin]# ntpdate -u ntp.api.bz
29 Oct 22:22:37 ntpdate[10543]: step time server 115.28.122.198 offset -28798.299112 sec
[root@mini1 bin]# date
2017年 10月 29日 星期日 22:22:44 CST
在使用ntpdate同步时间时出现以下错误:
ntpdate[46700]: no server suitable for synchronization found
没有找到好的解决方案,只能换另外一个工具来完成时间同步。
这里使用的Centos6.5,yum源使用的是阿里云的镜像。
yum install -y rdate
安装完毕后,使用下述命令即可。
rdate -s time-b.nist.gov
然后启动出现的问题如下
[root@mini1 bin]# ./start-hbase.sh
starting master, logging to /root/apps/hbase/logs/hbase-root-master-mini1.out
root@mini3's password: root@mini2's password:
mini3: starting regionserver, logging to /root/apps/hbase/bin/../logs/hbase-root-regionserver-mini3.out
这个时候我们只能输一个密码,所以有一个肯定是启动不了的。
解决办法:设置mini1在mini2和mini3上免密登录。
免密登录,先生存秘钥,在mini2和mini3上授权认证通过。
[root@mini1 ~]# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
52:1e:06:fa:7e:01:3d:a6:1d:bd:cb:19:08:86:cc:ca root@mini1
The key's randomart image is:
+--[ RSA 2048]----+
| . |
| o o o . |
| = + O . |
| . . o X = . |
| E + S o |
| . . o + |
| . . + |
| . |
| |
+-----------------+
[root@mini1 ~]# cd .ssh/
[root@mini1 .ssh]# ll
总用量 12
-rw-------. 1 root root 1675 10月 30 00:31 id_rsa
-rw-r--r--. 1 root root 392 10月 30 00:31 id_rsa.pub
-rw-r--r--. 1 root root 2359 10月 13 04:33 known_hosts
[root@mini1 .ssh]# ssh-copy-id mini2
root@mini2's password:
Now try logging into the machine, with "ssh 'mini2'", and check in:
.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
[root@mini1 .ssh]# ssh-copy-id mini3
root@mini3's password:
Now try logging into the machine, with "ssh 'mini3'", and check in:
.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
测试免密登录
[root@mini1 .ssh]# ssh mini2
Last login: Mon Oct 30 00:25:33 2017 from mini1
[root@mini2 ~]#
再次启动OK
[root@mini1 bin]# ./start-hbase.sh
starting master, logging to /root/apps/hbase/logs/hbase-root-master-mini1.out
mini2: starting regionserver, logging to /root/apps/hbase/bin/../logs/hbase-root-regionserver-mini2.out
mini3: starting regionserver, logging to /root/apps/hbase/bin/../logs/hbase-root-regionserver-mini3.out
测试HRegionServer上下线
单独启动HRegionServer节点:
启动集群中所有的regionserver
./hbase-daemons.sh start regionserver
启动某个regionserver
./hbase-daemon.sh start regionserver
二、javaapi 访问 hbase
Hbase介绍
HBASE是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群。
HBASE的目标是存储并处理大型的数据,更具体来说是仅需使用普通的硬件配置,就能够处理由成千上万的行和列所组成的大型数据。
HBASE是Google Bigtable的开源实现,但是也有很多不同之处。比如:Google Bigtable利用GFS作为其文件存储系统,HBASE利用Hadoop HDFS作为其文件存储系统;Google运行MAPREDUCE来处理Bigtable中的海量数据,HBASE同样利用Hadoop MapReduce来处理HBASE中的海量数据;Google Bigtable利用Chubby作为协同服务,HBASE利用Zookeeper作为对应。
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>0.99.2</version>
</dependency>
1、创建表
public class HBaseTest {
Configuration config = null;
private Connection connection = null;
private Table table = null;
@Before
public void init() throws Exception {
config = HBaseConfiguration.create();// 配置
config.set("hbase.zookeeper.quorum", "192.168.25.127,192.168.25.129,192.168.25.130");// zookeeper地址
config.set("hbase.zookeeper.property.clientPort", "2181");// zookeeper端口
connection = ConnectionFactory.createConnection(config);
table = connection.getTable(TableName.valueOf("user1"));
}
/**
* 创建表
* @throws Exception
*/
@Test
public void testCreateTable() throws Exception{
//创建表管理类
HBaseAdmin admin = new HBaseAdmin(config);
//创建表描述类
TableName tableName = TableName.valueOf("user2");
HTableDescriptor descriptor = new HTableDescriptor(tableName);
//创建列族描述类
HColumnDescriptor info1 = new HColumnDescriptor("info1");
//列族加入表中
descriptor.addFamily(info1);
HColumnDescriptor info2 = new HColumnDescriptor("info2");
descriptor.addFamily(info2);
//创建表
admin.createTable(descriptor);
}
}
如果执行之后发现程序卡住不动,或者过了很久之后出现下面的异常
org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed after attempts=35, exceptions:
...
Caused by: org.apache.hadoop.hbase.MasterNotRunningException: com.google.protobuf.ServiceException: java.net.UnknownHostException: unknown host: mini1
...
Caused by: com.google.protobuf.ServiceException: java.net.UnknownHostException: unknown host: mini1
...
首先确保关闭了hadoop的安全模式,然后linux下的ip地址跟主机名必须对应,最后windows下的ip地址跟主机名也要对应,我这在linux下/etc/hosts文件中
[root@mini1 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost.jinbm
192.168.25.127 mini1
192.168.25.129 mini2
192.168.25.130 mini3
但是当时出现windows下的hosts文件没有配置后三个映射导致出现上面的异常。
执行之后去hbase集群查看
user2表已经创建
hbase(main):002:0> list
TABLE
user1
user2
2 row(s) in 0.0130 seconds
=> ["user1", "user2"]
2、删除表
删除表跟shell命令一样,也是要先disable表之后才能删除
@Test
public void testDeleteTable() throws Exception{
HBaseAdmin admin = new HBaseAdmin(config);
admin.disableTable("user2");
admin.deleteTable("user2");
}
3、单条插入(修改)
/**
* 向表中插入数据
* 单条插入(包括修改)
* @throws Exception
*/
@Test
public void testPut() throws Exception{
//rowkey
Put put = new Put(Bytes.toBytes("1234"));
//列族,列,值
put.add(Bytes.toBytes("info1"), Bytes.toBytes("gender"), Bytes.toBytes("1"));
put.add(Bytes.toBytes("info2"), Bytes.toBytes("name"), Bytes.toBytes("wangwu"));
table.put(put);
//提交
table.flushCommits();
}
查看
hbase(main):008:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info2:age, timestamp=1509315527064, value=18
1234 column=info2:name, timestamp=1509315500250, value=zhangsan
12345 column=info2:age, timestamp=1509315533683, value=18
12345 column=info2:name, timestamp=1509315548481, value=lisi
2 row(s) in 0.1030 seconds
hbase(main):009:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:gender, timestamp=1509315890353, value=1
1234 column=info2:age, timestamp=1509315527064, value=18
1234 column=info2:name, timestamp=1509315890353, value=wangwu
12345 column=info2:age, timestamp=1509315533683, value=18
12345 column=info2:name, timestamp=1509315548481, value=lisi
2 row(s) in 0.0440 seconds
发现添加了一条数据和修改了一条数据
4、批量插入数据
Table有2个重载的方法,一个是table.put(Put put)也就是单条插入,一个是table.put(list)list泛型是Put,这就是批量插入。
/**
* 向表中插入数据
* 多条插入,使用list
* @throws Exception
*/
@Test
public void testPut2() throws Exception{
//可以通过将自动刷新设置为false来激活缓冲区
table.setAutoFlushTo(false);
//设置数据将被写入的缓冲区大小
table.setWriteBufferSize(534534534);
List<Put> putList = new ArrayList<>();
for (int i=20;i<=30;i++){
//rowkey
Put put = new Put(Bytes.toBytes("jbm_"+i));
//列族,列,值
put.add(Bytes.toBytes("info1"), Bytes.toBytes("age"), Bytes.toBytes(i));
put.add(Bytes.toBytes("info1"), Bytes.toBytes("name"), Bytes.toBytes("lucy"+i));
putList.add(put);
}
table.put(putList);
//提交
table.flushCommits();
}
执行之后查看
hbase(main):010:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:gender, timestamp=1509315890353, value=1
1234 column=info2:age, timestamp=1509315527064, value=18
1234 column=info2:name, timestamp=1509315890353, value=wangwu
12345 column=info2:age, timestamp=1509315533683, value=18
12345 column=info2:name, timestamp=1509315548481, value=lisi
jbm_20 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x14
jbm_20 column=info1:name, timestamp=1509316527223, value=lucy20
jbm_21 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x15
jbm_21 column=info1:name, timestamp=1509316527223, value=lucy21
jbm_22 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x16
jbm_22 column=info1:name, timestamp=1509316527223, value=lucy22
jbm_23 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x17
jbm_23 column=info1:name, timestamp=1509316527223, value=lucy23
jbm_24 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x18
jbm_24 column=info1:name, timestamp=1509316527223, value=lucy24
jbm_25 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x19
jbm_25 column=info1:name, timestamp=1509316527223, value=lucy25
jbm_26 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x1A
jbm_26 column=info1:name, timestamp=1509316527223, value=lucy26
jbm_27 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x1B
jbm_27 column=info1:name, timestamp=1509316527223, value=lucy27
jbm_28 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x1C
jbm_28 column=info1:name, timestamp=1509316527223, value=lucy28
jbm_29 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x1D
jbm_29 column=info1:name, timestamp=1509316527223, value=lucy29
jbm_30 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x1E
jbm_30 column=info1:name, timestamp=1509316527223, value=lucy30
5、修改数据
/**
* 修改数据
* @throws Exception
*/
@Test
public void testUpdate() throws Exception{
Put put = new Put(Bytes.toBytes("1234"));
put.add(Bytes.toBytes("info2"), Bytes.toBytes("name"), Bytes.toBytes("tom"));
table.put(put);
table.flushCommits();
}
执行之后查看
hbase(main):010:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:gender, timestamp=1509315890353, value=1
1234 column=info2:age, timestamp=1509315527064, value=18
1234 column=info2:name, timestamp=1509315890353, value=wangwu
...
hbase(main):013:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:gender, timestamp=1509315890353, value=1
1234 column=info2:age, timestamp=1509315527064, value=18
1234 column=info2:name, timestamp=1509316978243, value=tom
发现wangwu已被改为了tom
6、删除整行数据
删除rowkey为1234整行数据
/**
* 删除数据
* @throws Exception
*/
@Test
public void testDeleteData() throws Exception{
Delete delete = new Delete(Bytes.toBytes("1234"));
table.delete(delete);
table.flushCommits();
}
7、单条查询
/**
* 单条查询
* @throws Exception
*/
@Test
public void testGetSingle() throws Exception{
//rowkey
Get get = new Get(Bytes.toBytes("12345"));
Result result = table.get(get);
//列族,列名
byte[] name = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age"));
System.out.println(Bytes.toString(name));
System.out.println(Bytes.toString(age));
}
执行后hbase查看和控制台查看
hbase(main):001:0> scan 'user1'
12345 column=info2:age, timestamp=1509315533683, value=18
12345 column=info2:name, timestamp=1509315548481, value=lisi
...
控制台输出
lisi
18
8、多条查询
这里叫做扫描更适合吧,先用全表扫描,和命令行的scan ‘表名’一样
/**
* 多条查询
* 全表扫描
* @throws Exception
*/
@Test
public void testGetMany() throws Exception{
Scan scan = new Scan();
//字典序 类似于分页
scan.setStartRow(Bytes.toBytes("jbm_20"));
scan.setStopRow(Bytes.toBytes("jbm_30"));
ResultScanner resultScanner = table.getScanner(scan);
for (Result result : resultScanner) {
//Single row result of a Get or Scan query. Result
//Result 一次获取一个rowkey对应的记录
//列族,列名
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age"));
System.out.print(Bytes.toString(name)+",");
System.out.print(Bytes.toInt(age));
System.out.println();
}
}
执行控制台输出结果
lucy20,20
lucy21,21
lucy22,22
lucy23,23
lucy24,24
lucy25,25
lucy26,26
lucy27,27
lucy28,28
lucy29,29
9、Hbase过滤器
1)、列值过滤器
SingleColumnValueFilter
过滤列值的相等、不等、范围等
/**
* 全表扫描过滤器
* 列值过滤器
* @throws Exception
*/
@Test
public void testFilter() throws Exception{
Scan scan = new Scan();
//列值过滤器
SingleColumnValueFilter columnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"),
Bytes.toBytes("name"), CompareOp.EQUAL, Bytes.toBytes("lisi"));
//设置过滤器
scan.setFilter(columnValueFilter);
//获取结果集
ResultScanner resultScanner = table.getScanner(scan);
for (Result result : resultScanner) {
byte[] name = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age"));
System.out.print(Bytes.toString(name)+",");
System.out.print(Bytes.toString(age));
System.out.println();
}
}
执行查看输出
hbase(main):001:0> scan 'user1'
ROW COLUMN+CELL
12345 column=info2:age, timestamp=1509315533683, value=18
12345 column=info2:name, timestamp=1509315548481, value=lisi
jbm_20 column=info1:age, timestamp=1509316527223, value=\x00\x00\x00\x14
...
控制台输出
lisi,18
2)、rowkey过滤器
RowFilter 通过正则,过滤rowKey值。
/**
* 全表扫描过滤器
* rowkey过滤
* @throws Exception
*/
@Test
public void testRowkeyFilter() throws Exception{
Scan scan = new Scan();
//rowkey过滤器
//匹配以jbm开头的
RowFilter filter = new RowFilter(CompareOp.EQUAL, new RegexStringComparator("^jbm"));
//设置过滤器
scan.setFilter(filter);
//获取结果集
ResultScanner resultScanner = table.getScanner(scan);
for (Result result : resultScanner) {
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age"));
System.out.print(Bytes.toString(name)+",");
System.out.print(Bytes.toInt(age));
System.out.println();
}
}
控制台输出
lucy20,20
lucy21,21
lucy22,22
lucy23,23
lucy24,24
lucy25,25
lucy26,26
lucy27,27
lucy28,28
lucy29,29
lucy30,30
3)、列名前缀过滤器
ColumnPrefixFilter列名前缀过滤
/**
* 全表扫描过滤器
* 列名前缀过滤
* @throws Exception
*/
@Test
public void testColumnPrefixFilter() throws Exception{
Scan scan = new Scan();
//列名前缀过滤器 列名前缀为na(注:不是指值的前缀)
ColumnPrefixFilter filter = new ColumnPrefixFilter(Bytes.toBytes("na"));
//设置过滤器
scan.setFilter(filter);
//获取结果集
ResultScanner resultScanner = table.getScanner(scan);
for (Result result : resultScanner) {
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age"));
if(name!=null){
System.out.print(Bytes.toString(name)+" ");
}
if(age!=null){
System.out.print(age);
}
System.out.println();
}
}
从输出结果就能看到,只会拿到name列,age列是拿不到的
lucy20
lucy21
lucy22
lucy23
lucy24
lucy25
lucy26
lucy27
lucy28
lucy29
lucy30
4)、过滤器集合
/**
* 全表扫描过滤器
* 过滤器集合
* @throws Exception
*/
@Test
public void testFilterList() throws Exception{
Scan scan = new Scan();
//过滤器集合:MUST_PASS_ALL(and),MUST_PASS_ONE(or)
FilterList filterList = new FilterList(Operator.MUST_PASS_ALL);
//ROWKEY过滤器
RowFilter rowFilter = new RowFilter(CompareOp.EQUAL, new RegexStringComparator("^jbm"));
//列值过滤器 age大于25
SingleColumnValueFilter columnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"),
Bytes.toBytes("age"), CompareOp.GREATER, Bytes.toBytes(25));
filterList.addFilter(columnValueFilter);
filterList.addFilter(rowFilter);
//设置过滤器
scan.setFilter(filterList);
//获取结果集
ResultScanner resultScanner = table.getScanner(scan);
for (Result result : resultScanner) {
byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name"));
byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age"));
if(name!=null){
System.out.print(Bytes.toString(name)+" ");
}
if(age!=null){
System.out.print(Bytes.toInt(age)+" ");
}
System.out.println();
}
}
输出
lucy26 26
lucy27 27
lucy28 28
lucy29 29
lucy30 30
更多的过滤器用法
https://blog.csdn.net/m0_37739193/article/details/73615016
三、Hbase命令
hbase提供了一个shell的终端给用户交互
[root@mini1 bin]# ./hbase shell
退出使用quit或者ctrl+c即可。
注:需要关闭hadoop的安全模式不然进行一些操作,比如scan会卡住
进入到hadoop的bin目录下
[root@mini1 bin]# hadoop dfsadmin -safemode leave
接下来可以使用hbase命令来进行操作了
1、创建表
create ‘表名’,’列族1’,’列族2’,…’列族n’
create 'user1','info1','info2'
2、查看所有表
list
hbase(main):002:0> list
1 row(s) in 1.4540 seconds
=> ["user1"]
3、描述表
describe ‘表名’
hbase(main):014:0> describe 'user1'
Table user1 is ENABLED
COLUMN FAMILIES DESCRIPTION
{NAME => 'info1', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => 'FOREVER', KE
EP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
{NAME => 'info2', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => 'FOREVER', KE
EP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
2 row(s) in 0.0790 seconds
4、删除表
注:删除表之前需要先使表disable不然直接删除会报错,会提示先disable 表
disable ‘表名’
drop ‘表名’
hbase(main):004:0> disable 'user1'
0 row(s) in 1.4110 seconds
hbase(main):005:0> drop 'user1'
0 row(s) in 0.2330 seconds
hbase(main):006:0> list
TABLE
0 row(s) in 0.0450 seconds
=> []
5、判断表是否存在
hbase(main):013:0> exists 'user1'
Table user1 does exist
0 row(s) in 0.0980 seconds
6、向表中添加记录
create ‘表名’,’rowkey’(键),’列族:列名’,’值’
hbase(main):011:0> put 'user1','1234','info1:name','zhangsan'
0 row(s) in 0.1310 seconds
7、扫描表
扫描整张表
hbase(main):015:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:name, timestamp=1509303796190, value=zhangsan
1 row(s) in 0.0630 seconds
注:hbase没有直接修改操作,但是可以覆盖,只要rowkey跟列族列名一致就会覆盖
比如这里要修改上面插入数据的info:name为’lisi’
hbase(main):002:0> put 'user1','1234','info1:name','list'
0 row(s) in 0.1470 seconds
hbase(main):003:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:name, timestamp=1509304915052, value=list
但是如果rowkey相同,列族相同只要列名不同就只会添加而不会覆盖
比如插入年龄为18岁
hbase(main):004:0> put 'user1','1234','info1:age','18'
0 row(s) in 0.0500 seconds
hbase(main):005:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:age, timestamp=1509305017108, value=18
1234 column=info1:name, timestamp=1509304915052, value=list
8、查询记录数
注:rowkey相同的话只算一条
count ‘表名’
hbase(main):008:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:age, timestamp=1509305121972, value=18
1234 column=info1:name, timestamp=1509304915052, value=list
12345 column=info1:age, timestamp=1509305156151, value=24
12345 column=info2:name, timestamp=1509305221477, value=lucy
2 row(s) in 0.0460 seconds
hbase(main):009:0> count 'user1'
2 row(s) in 0.0540 seconds
=> 2
9、查询
获取某个列族
获取某个列族的列
hbase(main):010:0> get 'user1','1234','info1'
COLUMN CELL
info1:age timestamp=1509305121972, value=18
info1:name timestamp=1509304915052, value=list
2 row(s) in 0.0970 seconds
hbase(main):011:0> get 'user1','1234','info1:name'
COLUMN CELL
info1:name timestamp=1509304915052, value=list
1 row(s) in 0.0700 seconds
查询某个列,某个时间戳版本的值
get 'user1', '1234bbb', {COLUMN => 'info1:username',TIMESTAMP => 1538014481194}
Hbase中多版本(version)数据获取办法
参考https://blog.csdn.net/liuchuanhong1/article/details/53895234/
···
alter 'user',{NAME=>'info1',VERSIONS=>3}
get 'user','1234',{COLUMN=>'info1:name',VERSIONS=>3}
···
9、删除记录
可以删除一个列族的一个列记录
delete ‘表名’ ,‘rowkey’ , ‘列族:列’
也可以删除一整行
deleteall ‘表名’,’rowkey;
hbase(main):012:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:age, timestamp=1509305121972, value=18
1234 column=info1:name, timestamp=1509304915052, value=list
12345 column=info1:age, timestamp=1509305156151, value=24
12345 column=info2:name, timestamp=1509305221477, value=lucy
2 row(s) in 0.0930 seconds
hbase(main):013:0> delete 'user1','1234','info1:name'
0 row(s) in 0.1160 seconds
hbase(main):014:0> scan 'user1'
ROW COLUMN+CELL
1234 column=info1:age, timestamp=1509305121972, value=18
12345 column=info1:age, timestamp=1509305156151, value=24
12345 column=info2:name, timestamp=1509305221477, value=lucy
2 row(s) in 0.0430 seconds
hbase(main):018:0> deleteall 'user1','1234'
0 row(s) in 0.0130 seconds
hbase(main):019:0> scan 'user1'
ROW COLUMN+CELL
12345 column=info1:age, timestamp=1509305156151, value=24
12345 column=info2:name, timestamp=1509305221477, value=lucy
1 row(s) in 0.0570 seconds
10、清空表
truncate ‘表名’
hbase(main):025:0> truncate 'user1'
Truncating 'user1' table (it may take a while):
- Disabling table...
- Truncating table...
0 row(s) in 1.5680 seconds
hbase(main):026:0> list
TABLE
user1
1 row(s) in 0.1010 seconds
=> ["user1"]
hbase(main):027:0> scan 'user1'
ROW COLUMN+CELL
0 row(s) in 0.0400 seconds
四、HTable基本概念
从一个示例说起
传统的关系型数据库想必大家都不陌生,我们将以一个简单的例子来说明使用RDBMS和HBase各自的解决方式及优缺点。
以博文为例,RDBMS的表设计如下:
为了方便理解,我们以一些数据示例下
上面的例子,我们用HBase可以按以下方式设计
同样为了方便理解,我们以一些数据示例下,同时用红色标出了一些关键概念,后面会解释
HTable一些基本概念
- Row key
行主键, HBase不支持条件查询和Order by等查询,读取记录只能按Row key(及其range)或全表扫描,因此Row key需要根据业务来设计以利用其存储排序特性(Table按Row key字典序排序如1,10,100,11,2)提高性能。
- Column Family(列族)
在表创建时声明,每个Column Family为一个存储单元。在上例中设计了一个HBase表blog,该表有两个列族:article和author。
- Column(列)
HBase的每个列都属于一个列族,以列族名为前缀,如列article:title和article:content属于article列族,author:name和author:nickname属于author列族。
Column不用创建表时定义即可以动态新增,同一Column Family的Columns会群聚在一个存储单元上,并依Column key排序,因此设计时应将具有相同I/O特性的Column设计在一个Column Family上以提高性能。
- Timestamp
HBase通过row和column确定一份数据,这份数据的值可能有多个版本,不同版本的值按照时间倒序排序,即最新的数据排在最前面,查询时默认返回最新版本。如上例中row key=1的author:nickname值有两个版本,分别为1317180070811对应的“一叶渡江”和1317180718830对应的“yedu”(对应到实际业务可以理解为在某时刻修改了nickname为yedu,但旧值仍然存在)。Timestamp默认为系统当前时间(精确到毫秒),也可以在写入数据时指定该值。
- Value
每个值通过4个键唯一索引,tableName+RowKey+ColumnKey+Timestamp=>value,例如上例中{tableName=’blog’,RowKey=’1’,ColumnName=’author:nickname’,Timestamp=’ 1317180718830’}索引到的唯一值是“yedu”。
- 存储类型
TableName 是字符串
RowKey 和 ColumnName 是二进制值(Java 类型 byte[])
Timestamp 是一个 64 位整数(Java 类型 long)
value 是一个字节数组(Java类型 byte[])。
五、Hbase存储详解
Hbase是bigtable的开源山寨版本。是建立的hdfs之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统。
它介于nosql和RDBMS之间,仅能通过主键(row key)和主键的range来检索数据,仅支持单行事务。主要用来存储非结构化和半结构化的松散数据。
与hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。
Hbase中的表一般有这样的特点:
1 大:一个表可以有上亿行,上百万列
2 面向列:面向列(族)的存储和权限控制,列(族)独立检索。
3 稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
下面一幅图是Hbase在Hadoop Ecosystem中的位置。
本页无标题
二、逻辑视图
Hbase以表的形式存储数据。表有行和列组成。列划分为若干个列族(row family)
Row Key
Row Key
与nosql数据库们一样,row key是用来检索记录的主键。访问Hbase table中的行,只有三种方式:
1 通过单个row key访问
2 通过row key的range
3 全表扫描
Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在Hbase内部,row key保存为字节数组。
存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。
注意:
字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整形的自然序,行键必须用0作左填充。
行的一次读写是原子操作 (不论一次读写多少列)。
列族
Hbase表中的每个列,都归属与某个列族。列族是表的chema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如courses:history,courses:math
都属于courses 这个列族。
时间戳
Hbase中通过row和columns确定的为一个存贮单元称为cell。每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由Hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
Cell
由{row key, column, version} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。
三、物理存储
1 已经提到过,Table中的所有行都按照row key的字典序排列。
2 Table 在行的方向上分割为多个Hregion。
本页无标题
3 region按大小分割的,每个表一开始只有一个region,随着数据不断插入表,region不断增大,当增大到一个阀值的时候,Hregion就会等分会两个新的Hregion。当table中的行不断增多,就会有越来越多的Hregion。
本页无标题
4 HRegion是Hbase中分布式存储和负载均衡的最小单元。最小单元就表示不同的Hregion可以分布在不同的HRegion server上。但一个Hregion是不会拆分到多个server上的。
5 HRegion虽然是分布式存储的最小单元,但并不是存储的最小单元。
5 HRegion虽然是分布式存储的最小单元,但并不是存储的最小单元。
事实上,HRegion由一个或者多个Store组成,每个store保存一个columns family。
每个Strore又由一个memStore和0至多个StoreFile组成。如图:
StoreFile以HFile格式保存在HDFS上。
本页无标题
Hbase基本组件说明:
Client
包含访问HBase的接口,并维护cache来加快对HBase的访问,比如region的位置信息
Master
为Region server分配region
负责Region server的负载均衡
发现失效的Region server并重新分配其上的region
管理用户对table的增删改查操作
Region Server
Regionserver维护region,处理对这些region的IO请求
Regionserver负责切分在运行过程中变得过大的region
Zookeeper作用
通过选举,保证任何时候,集群中只有一个master,Master与RegionServers 启动时会向ZooKeeper注册
存贮所有Region的寻址入口
实时监控Region server的上线和下线信息。并实时通知给Master
存储HBase的schema和table元数据
默认情况下,HBase 管理ZooKeeper 实例,比如, 启动或者停止ZooKeeper
Zookeeper的引入使得Master不再是单点故障
HBase容错性
Master容错:Zookeeper重新选择一个新的Master
无Master过程中,数据读取仍照常进行;
无master过程中,region切分、负载均衡等无法进行;
RegionServer容错:定时向Zookeeper汇报心跳,如果一旦时间内未出现心跳,Master将该RegionServer上的Region重新分配到其他RegionServer上,失效服务器上“预写”日志由主服务器进行分割并派送给新的RegionServer
Zookeeper容错:Zookeeper是一个可靠地服务,一般配置3或5个Zookeeper实例
HBase存储数据其底层使用的是HDFS来作为存储介质,HBase的每一张表对应的HDFS目录上的一个文件夹,文件夹名以HBase表进行命名(如果没有使用命名空间,则默认在default目录下),在表文件夹下存放在若干个Region命名的文件夹,Region文件夹中的每个列簇也是用文件夹进行存储的,每个列簇中存储就是实际的数据,以HFile的形式存在。路径格式如下:
/hbase/data/default/<tbl_name>/<region_id>/<cf>/<hfile_id>