HBase

HBase 核心概念介绍及入门

Posted by leone on 2018-09-18

HBase

HBase 核心概念介绍及入门

什么是hbase?

HBase是一个开源的非关系型分布式数据库(NoSQL),它参考了谷歌的BigTable建模,实现的编程语言为 Java。它是Apache软件基金会的Hadoop项目的一部分,运行于HDFS文件系统之上,为 Hadoop 提供类似于BigTable 规模的服务。因此,它可以对稀疏文件提供极高的容错率。HBase在列上实现了BigTable论文提到的压缩算法、内存操作和布隆过滤器。HBase的表能够作为MapReduce任务的输入和输出,可以通过JavaAPI来访问数据,也可以通过REST、Avro或者Thrift的API来访问。
虽然最近性能有了显著的提升,HBase 还不能直接取代SQL数据库。如今,它已经应用于多个数据驱动型网站,包括 Facebook的消息平台。在 Eric Brewer的CAP理论中,HBase属于CP类型的系统。

HBASE表模型

1、hbase的表模型跟mysql之类的关系型数据库的表模型差别巨大
2、hbase的表模型中有:行的概念;但没有字段的概念
3、行中存的都是key-value对,每行中的key-value对中的key可以是各种各样,每行中的key-value对的数量也可以是各种各样

hbase表模型的要点

1、一个表,有表名
2、一个表可以分为多个列族(不同列族的数据会存储在不同文件中)
3、表中的每一行有一个“行键rowkey”,而且行键在表中不能重复
4、表中的每一对kv数据称作一个cell
5、hbase可以对数据存储多个历史版本(历史版本数量可配置)
6、整张表由于数据量过大,会被横向切分成若干个region(用rowkey范围标识),不同region的数据也存储在不同文件中

概念特性

HBASE与mysql、oralce、db2、sqlserver等关系型数据库不同,它是一个NoSQL数据库(非关系型数据库)HBASE相比于其他nosql数据库(mongodb、redis、cassendra、hazelcast)的特点:
Hbase的表数据存储在HDFS文件系统中从而,hbase具备如下特性:存储容量可以线性扩展; 数据存储的安全性可靠性极高!

  • Hbase的表模型与关系型数据库的表模型不同

  • Hbase的表没有固定的字段定义

  • Hbase的表中每行存储的都是一些key-value对

  • Hbase的表中有列族的划分,用户可以指定将哪些kv插入哪个列族

  • Hbase的表在物理存储上,是按照列族来分割的,不同列族的数据一定存储在不同的文件中

  • Hbase的表中的每一行都固定有一个行键,而且每一行的行键在表中不能重复

  • Hbase中的数据,包含行键,包含key,包含value,都是byte[]类型,hbase不负责为用户维护数据类型

  • HBASE对事务的支持很差

HBase 核心组件

master

  • 管理HRegionServer,实现其负载均衡。

  • 管理和分配HRegion,比如在HRegion split时分配新的HRegion;在HRegionServer退出时迁移其负责的HRegion到其他HRegionServer上。

  • Admin职能创建、删除、修改Table的定义。实现DDL操作(namespace和table的增删改,column familiy的增删改等)。

  • 管理namespace和table的元数据(实际存储在HDFS上)。

  • 权限控制(ACL)。

  • 监控集群中所有HRegionServer的状态(通过Heartbeat和监听ZooKeeper中的状态)。

region server

  • 管理自己所负责的region数据的读写。

  • 读写HDFS,管理Table中的数据。

  • Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)。

Zookeeper集群所起作用

  • 存放整个HBase集群的元数据以及集群的状态信息。

  • 实现HMaster主从节点的failover。

注: HMaster通过监听ZooKeeper中的Ephemeral节点(默认:/hbase/rs/*)来监控HRegionServer的加入和宕机。
在第一个HMaster连接到ZooKeeper时会创建Ephemeral节点(默认:/hbasae/master)来表示Active的HMaster,其后加进来的HMaster则监听该Ephemeral节点
如果当前Active的HMaster宕机,则该节点消失,因而其他HMaster得到通知,而将自身转换成Active的HMaster,在变为Active的HMaster之前,它会在/hbase/masters/下创建自己的Ephemeral节点。

HBase读写数据流程

1、在HBase 0.96以前,HBase有两个特殊的Table:-ROOT-和.META. 用来记录用户表的rowkey范围所在的的regionserver服务器;因而客户端读写数据时需要通过3次寻址请求来对数据所在的regionserver进行定位,效率低下;

2、而在HBase 0.96以后去掉了-ROOT- Table,只剩下这个特殊的目录表叫做Meta Table(hbase:meta),它存储了集群中所有用户HRegion的位置信息,而ZooKeeper的节点中(/hbase/meta-region-server)存储的则直接是这个Meta Table的位置,并且这个Meta Table如以前的-ROOT- Table一样是不可split的。这样,客户端在第一次访问用户Table的流程就变成了:
A.从ZooKeeper(/hbase/meta-region-server)中获取hbase:meta的位置(HRegionServer的位置),缓存该位置信息。
B.从HRegionServer中查询用户Table对应请求的RowKey所在的HRegionServer,缓存该位置信息。
C.从查询到HRegionServer中读取Row。

注:客户会缓存这些位置信息,然而第二步它只是缓存当前RowKey对应的HRegion的位置,因而如果下一个要查的RowKey不在同一个HRegion中,则需要继续查询hbase:meta所在的HRegion,然而随着时间的推移,客户端缓存的位置信息越来越多,以至于不需要再次查找hbase:meta Table的信息,除非某个HRegion因为宕机或Split被移动,此时需要重新查询并且更新缓存。

  • hbase:meta表存储了所有用户HRegion的位置信息:

  • Rowkey:tableName,regionStartKey,regionId,replicaId等;

  • info列族:这个列族包含三个列,他们分别是:

    • info:regioninfo列:

    • regionId,tableName,startKey,endKey,offline,split,replicaId;

    • info:server列:HRegionServer对应的server:port;

    • info:serverstartcode列:HRegionServer的启动时间戳。

region server 内部机制

  • WAL即Write Ahead Log,在早期版本中称为HLog,它是HDFS上的一个文件,如其名字所表示的,所有写操作都会先保证将数据写入这个Log文件后,才会真正更新MemStore,最后写入HFile中。WAL文件存储在/hbase/WALs/${HRegionServer_Name}的目录中

  • BlockCache是一个读缓存,即“引用局部性”原理(也应用于CPU,分空间局部性和时间局部性,空间局部性是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指某个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问),将数据预读取到内存中,以提升读的性能。

  • HRegion是一个Table中的一个Region在一个HRegionServer中的表达。一个Table可以有一个或多个Region,他们可以在一个相同的HRegionServer上,也可以分布在不同的HRegionServer上,一个HRegionServer可以有多个HRegion,他们分别属于不同的Table。HRegion由多个Store(HStore)构成,每个HStore对应了一个Table在这个HRegion中的一个Column Family,即每个Column Family就是一个集中的存储单元,因而最好将具有相近IO特性的Column存储在一个Column Family,以实现高效读取(数据局部性原理,可以提高缓存的命中率)。HStore是HBase中存储的核心,它实现了读写HDFS功能,一个HStore由一个MemStore 和0个或多个StoreFile组成。

  • MemStore是一个写缓存(In Memory Sorted Buffer),所有数据的写在完成WAL日志写后,会 写入MemStore中,由MemStore根据一定的算法将数据Flush到地层HDFS文件中(HFile),通常每个HRegion中的每个 Column Family有一个自己的MemStore。

  • HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。

  • FLUSH详述

    • 每一次Put/Delete请求都是先写入到MemStore中,当MemStore满后会Flush成一个新的StoreFile(底层实现是HFile),即一个HStore(Column Family)可以有0个或多个StoreFile(HFile)。

    • 当一个HRegion中的所有MemStore的大小总和超过了hbase.hregion.memstore.flush.size的大小,默认128MB。此时当前的HRegion中所有的MemStore会Flush到HDFS中。

    • 当全局MemStore的大小超过了hbase.regionserver.global.memstore.upperLimit的大小,默认40%的内存使用量。此时当前HRegionServer中所有HRegion中的MemStore都会Flush到HDFS中,Flush顺序是MemStore大小的倒序(一个HRegion中所有MemStore总和作为该HRegion的MemStore的大小还是选取最大的MemStore作为参考?有待考证),直到总体的MemStore使用量低于hbase.regionserver.global.memstore.lowerLimit,默认38%的内存使用量。

    • 当前HRegionServer中WAL的大小超过了
      hbase.regionserver.hlog.blocksize * hbase.regionserver.max.logs
      的数量,当前HRegionServer中所有HRegion中的MemStore都会Flush到HDFS中,
      Flush使用时间顺序,最早的MemStore先Flush直到WAL的数量少于
      hbase.regionserver.hlog.blocksize * hbase.regionserver.max.logs
      这里说这两个相乘的默认大小是2GB,查代码,hbase.regionserver.max.logs默认值是32,而hbase.regionserver.hlog.blocksize默认是32MB。但不管怎么样,因为这个大小超过限制引起的Flush不是一件好事,可能引起长时间的延迟

HBase 安装和使用

hbase安装

HBASE是一个分布式系统,其中有一个管理角色: HMaster(一般2台,一台active,一台backup)
其他的数据节点角色:HRegionServer(很多台,看数据容量)

首先,要有一个HDFS集群,并正常运行; regionserver应该跟hdfs中的datanode在一起
其次,还需要一个zookeeper集群,并正常运行
然后,安装HBASE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 下载
wget http://archive.apache.org/dist/hbase/hbase-1.2.9/hbase-1.2.9-bin.tar.gz

# 解压
tar -zxvf hbase-1.2.9-bin.tar.gz -C /xxx/xxx

# 修改$HBASE_HOME/bin/hbase-env.sh文件修改配置如下
export JAVA_HOME=/usr/local/jdk1.8.0_192
export HBASE_MANAGES_ZK=false

# 修改或新建$HBASE_HOME/bin/hbase-site.xml文件
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>

<!-- 指定hbase在HDFS上存储的路径 -->
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://node-1:9000/hbase</value>
  </property>

<!-- 指定hbase是分布式的 -->
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>

<!-- 指定zk的地址,多个用“,”分割 -->
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>node-2,node-3,node-4</value>
  </property>

</configuration>


# 修改 $HBASE_HOME/bin/regionservers文件(配置的是regionserver节点)
node-2
node-3
node-4


# 启动集群

$HBASE_HOME/bin/start-hbase.sh
启动完后,还可以在集群中找任意一台机器启动一个备用的master
$HBASE_HOME/bin/hbase-daemon.sh start master
新启的这个master会处于backup状态

# 启动hbase的命令行客户端

$HBASE_HOME/bin/hbase shell
Hbase> list // 查看表
Hbase> status // 查看集群状态
Hbase> version // 查看集群版本

常用shell命令

command 操作 描述
alter alter ‘t_test’,‘f4’ 修改列族(column family)模式
count count ‘t_test’ 统计表中行的数量
create create ‘t_test’,{NAME=>‘f1’,VERSION=>2},{NAME=>‘f2’,VERSION=>2} 创建表
describe describe ‘t_test’ 显示表相关的详细信息
delete delete ‘t_test’,‘r1’,‘f1:c1’ 删除指定对象的值(可以为表,行,列对应的值,另外也可以指定时间戳的值)
deleteall deleteall ‘t_test’,‘r1’ 删除指定行的所有元素值
disable disable ‘t_test’ 使表无效
drop drop ‘t_test’ 删除表(需要先禁用表)
enable enable ‘t_test’ 使表有效
exists exists ‘t_test’ 测试表是否存在
exit exit 退出hbase shell
get get ‘t_test’,‘r2’ 获取行或单元(cell)的值
list list 列出hbase中存在的所有表
put put ‘t_test’,‘r2’,‘f1:c1’,‘v2’ 向指向的表单元添加值
tools tools 列出hbase所支持的工具
scan scan ‘t_test’ 通过对表的扫描来获取对用的值
status status 返回hbase集群的状态信息
shutdown shutdown 关闭hbase集群(与exit不同)
version version 返回hbase版本信息
truncate truncate ‘t_person’ 清空表数据
desc desc ‘t_person’ 查看表结构

java api 操作hbase

  • pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?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">

<artifactId>bit-data-hbase</artifactId>
<groupId>com.andy</groupId>
<version>1.0.1.RELEASE</version>

<modelVersion>4.0.0</modelVersion>

<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.2.9</version>
</dependency>

<dependency>
<groupId>com.andy</groupId>
<artifactId>big-data-common</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
  • hbase java 客户端操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.regionserver.BloomType;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <p> HBase ddl 操作
*
* @author leone
* @since 2018-12-16
**/
public class HBaseClientTest {

private static final Logger logger = LoggerFactory.getLogger(HBaseCrudTest.class);

private Connection conn;

private Admin admin;

private String tableName = "t_person";

private String f1 = "f1";

private String f2 = "f2";

private String f3 = "f3";

@Before
public void init() throws Exception {
// 创建连接对象,会自动加载HBase配置文件 zookeeper集群的URL配置信息
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "node-2:2181,node-3:2181,node-4:2181");
conn = ConnectionFactory.createConnection(conf);
// 创建ddl描述对象
admin = conn.getAdmin();
}

/**
* 创建表
* create 't_person','f1','f2','f3'
*
* @throws Exception
*/
@Test
public void createTableTest() throws Exception {
// 创建表描述对象
HTableDescriptor table = new HTableDescriptor(TableName.valueOf(tableName));

// 创建列簇描述对象
HColumnDescriptor column1 = new HColumnDescriptor(f1);
// 设置保存数据的最大半本数量是3
column1.setMaxVersions(3);

HColumnDescriptor column2 = new HColumnDescriptor(f2);

table.addFamily(column1);
table.addFamily(column2);

admin.createTable(table);

admin.close();
conn.close();
}


/**
* 删除表
* disable 't_person'
* drop 't_person'
*
* @throws Exception
*/
@Test
public void dropTableTest() throws Exception {
// 先停用表
admin.disableTable(TableName.valueOf(tableName));

// 再删除表
admin.deleteTable(TableName.valueOf(tableName));

admin.close();
conn.close();
}


/**
* 修改表添加列簇
* alter 't_person','f4'
*
* @throws Exception
*/
@Test
public void modifyTableTest() throws Exception {
// 取出旧的的表的描述信息
HTableDescriptor table = admin.getTableDescriptor(TableName.valueOf(tableName));

HColumnDescriptor column = new HColumnDescriptor(f3);
// 设置布隆过滤器
column.setBloomFilterType(BloomType.ROWCOL);

table.addFamily(column);

admin.modifyTable(TableName.valueOf(tableName), table);

admin.close();
conn.close();
}

/**
* 查看表定义信息
* desc 't_person'
*
* @throws Exception
*/
@Test
public void descTableTest() throws Exception {
HTableDescriptor table = admin.getTableDescriptor(TableName.valueOf(tableName));
HColumnDescriptor[] columnFamilies = table.getColumnFamilies();
for (HColumnDescriptor hcd : columnFamilies) {
logger.info("HColumn: {}", Bytes.toString(hcd.getName()));
}
admin.close();
conn.close();
}
}
  • hbase CRUD测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import com.andy.common.utils.RandomValue;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* <p> HBase crud
*
* @author leone
* @since 2018-12-16
**/
public class HBaseCrudTest {

private static final Logger logger = LoggerFactory.getLogger(HBaseCrudTest.class);

private String tableName = "t_person";

private String f1 = "f1", f2 = "f2", f3 = "f3";

private Connection conn;

private Configuration conf;

@Before
public void init() throws Exception {
// 创建连接对象,会自动加载HBase配置文件
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "node-2:2181,node-3:2181,node-4:2181");
conn = ConnectionFactory.createConnection(conf);
}


/**
* DML 操作,向 HBase 插入数据
* put 't_person','r2','f1:c1','v1'
*
* @throws Exception
*/
@Test
public void insertTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));
List<Put> rows = new ArrayList<>();

for (int i = 1; i <= 20; i++) {
Put row = new Put(Bytes.toBytes(String.valueOf(i)));
row.addColumn(Bytes.toBytes(f1), Bytes.toBytes("name"), Bytes.toBytes(RandomValue.getName()));
row.addColumn(Bytes.toBytes(f1), Bytes.toBytes("age"), Bytes.toBytes(String.valueOf(RandomValue.getNumber(50))));
row.addColumn(Bytes.toBytes(f2), Bytes.toBytes("address"), Bytes.toBytes(RandomValue.getAddress()));
rows.add(row);
}
logger.info("size:{}", rows.size());
table.put(rows);
conn.close();
}

/**
* DML 操作,删除数据 HBase 中数据
* delete 't_person','r1','f1:c1'
*
* @throws Exception
*/
@Test
public void deleteTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));

Delete delete1 = new Delete(Bytes.toBytes("0"));

Delete delete2 = new Delete(Bytes.toBytes("1"));
delete2.addColumn(Bytes.toBytes(f1), Bytes.toBytes("name"));

List<Delete> deleteList = new ArrayList<>();
deleteList.add(delete1);
deleteList.add(delete2);

table.delete(deleteList);

conn.close();
}


/**
* DML 操作,删除数据 HBase 中数据
* deleteall 't_person','r1'
*
* @throws Exception
*/
@Test
public void deleteAllTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));

Delete delete = new Delete(Bytes.toBytes("3"));

table.delete(delete);

conn.close();
}

/**
* DML 操作,修改数据 HBase 中数据
*
* @throws Exception
*/
@Test
public void updateTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));

conn.close();
}


/**
* DML 操作 HBase 查询数据
* get 't_person','r2'
*
* @throws Exception
*/
@Test
public void getTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));
List<Get> gets = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
Get get = new Get(Bytes.toBytes(String.valueOf(i)));
gets.add(get);
}
Result[] results = table.get(gets);
for (Result result : results) {
CellScanner cellScanner = result.cellScanner();
while (cellScanner.advance()) {
Cell cell = cellScanner.current();
// 行键的字节数组
byte[] rowArray = cell.getRowArray();
// 列簇的字节数组
byte[] familyArray = cell.getFamilyArray();
// 列名的字节数组
byte[] qualifierArray = cell.getQualifierArray();
// value的字节数组
byte[] valueArray = cell.getValueArray();
logger.info("行键:{} \t 列簇:{} \t key:{} \t value:{}", new Object[]{new String(rowArray, cell.getRowOffset(), cell.getRowLength()), new String(familyArray, cell.getFamilyOffset(), cell.getFamilyLength()), new String(qualifierArray, cell.getQualifierOffset(), cell.getQualifierLength()), new String(valueArray, cell.getValueOffset(), cell.getValueLength())});
}
}

conn.close();
}

/**
* DML 操作 HBase 查询数据
* scan 't_person'
*
* @throws Exception
*/
@Test
public void scanTest() throws Exception {
// 获取指定表对象,进行dml操作
Table table = conn.getTable(TableName.valueOf(tableName));
// 可以指定开始行键和结束行键
Scan scan = new Scan(Bytes.toBytes("2"), Bytes.toBytes("3"));

ResultScanner result = table.getScanner(scan);

Iterator<Result> iterator = result.iterator();
while (iterator.hasNext()) {
Result rs = iterator.next();
CellScanner cellScanner = rs.cellScanner();
while (cellScanner.advance()) {
Cell cell = cellScanner.current();
byte[] rowArray = cell.getRowArray();
// 列簇的字节数组
byte[] familyArray = cell.getFamilyArray();
// 列名的字节数组
byte[] qualifierArray = cell.getQualifierArray();
// value的字节数组
byte[] valueArray = cell.getValueArray();
logger.info("行键:{} \t 列簇:{} \t key:{} \t value:{}", new Object[]{new String(rowArray, cell.getRowOffset(), cell.getRowLength()), new String(familyArray, cell.getFamilyOffset(), cell.getFamilyLength()), new String(qualifierArray, cell.getQualifierOffset(), cell.getQualifierLength()), new String(valueArray, cell.getValueOffset(), cell.getValueLength())});
}
}
conn.close();
}


/**
* 查询某列数据的某个版本
*
* @throws Exception
*/
@Test
public void getVersionTest() throws Exception {
HTable htable = new HTable(conf, tableName);

Get get = new Get(Bytes.toBytes("2"));

get.addColumn(Bytes.toBytes(f1), Bytes.toBytes("name"));
get.setMaxVersions(2);
Result result = htable.get(get);
for (KeyValue cell : result.list()) {
byte[] rowArray = cell.getRowArray();
// 列簇的字节数组
byte[] familyArray = cell.getFamilyArray();
// 列名的字节数组
byte[] qualifierArray = cell.getQualifierArray();
// value的字节数组
byte[] valueArray = cell.getValueArray();
logger.info("行键:{} \t 列簇:{} \t key:{} \t value:{} \ttimestamp:{}", new Object[]{new String(rowArray, cell.getRowOffset(), cell.getRowLength()), new String(familyArray, cell.getFamilyOffset(), cell.getFamilyLength()), new String(qualifierArray, cell.getQualifierOffset(), cell.getQualifierLength()), new String(valueArray, cell.getValueOffset(), cell.getValueLength()), cell.getTimestamp()});
}
conn.close();
}

}