HBase笔记

1.1. 什么是HBASE

1.1.1. 概念特性

  HBASE是一个数据库----可以提供数据的实时随机读写。HBASE与mysql、oralce、db2、sqlserver等关系型数据库不同,它是一个NoSQL数据库(非关系型数据库)

  • Hbase的表模型与关系型数据库的表模型不同:
  • Hbase的表没有固定的字段定义;
  • Hbase的表中每行存储的都是一些key-value对
  • Hbase的表中有列族的划分,用户可以指定将哪些kv插入哪个列族
  • Hbase的表在物理存储上,是按照列族来分割的,不同列族的数据一定存储在不同的文件中
  • Hbase的表中的每一行都固定有一个行键,而且每一行的行键在表中不能重复
  • Hbase中的数据,包含行键,包含key,包含value,都是byte[ ]类型,hbase不负责为用户维护数据类型
  • HBASE对事务的支持很差 

  HBASE相比于其他nosql数据库(mongodb、redis、cassendra、hazelcast)的特点:

  Hbase的表数据存储在HDFS文件系统中

  从而,hbase具备如下特性:存储容量可以线性扩展; 数据存储的安全性可靠性极高!

1.2 安装HBASE

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

1.2.1. 安装准备:

  首先,要有一个HDFS集群,并正常运行; regionserver应该跟hdfs中的datanode在一起

  其次,还需要一个zookeeper集群,并正常运行

  然后,安装HBASE

  角色分配如下:

    Hdp01:  namenode  datanode  regionserver  hmaster  zookeeper

    Hdp02:  datanode   regionserver  zookeeper

    Hdp03:  datanode   regionserver  zookeeper

1.2.2. 安装步骤:

1.安装zookeeper     

2.安装hbase

  解压hbase安装包

  修改hbase-env.sh

export JAVA_HOME=/root/apps/jdk1.7.0_67
export HBASE_MANAGES_ZK=false

  修改hbase-site.xml 

<configuration>
<!-- 指定hbase在HDFS上存储的路径 -->
        <property>
                <name>hbase.rootdir</name>
                <value>hdfs://hdp01:9000/hbase</value>
        </property>
<!-- 指定hbase是分布式的 -->
        <property>
                <name>hbase.cluster.distributed</name>
                <value>true</value>
        </property>
<!-- 指定zk的地址,多个用“,”分割 -->
        <property>
                <name>hbase.zookeeper.quorum</name>
               <value>hdp01:2181,hdp02:2181,hdp03:2181</value>
        </property>
</configuration>

  修改 regionservers 

hdp01
hdp02
hdp03

3. 启动hbase集群: 

  bin/start-hbase.sh  启动完后,还可以在集群中找任意一台机器启动一个备用的master

  bin/hbase-daemon.sh start master  新启的这个master会处于backup状态

4. 启动hbase的命令行客户端

  bin/hbase shell

  Hbase> list     // 查看表

  Hbase> status   // 查看集群状态

  Hbase> version  // 查看集群版本

 

1.3. HBASE表模型

  hbase的表模型跟mysql之类的关系型数据库的表模型差别巨大

  hbase的表模型中有:行的概念;但没有字段的概念

  行中存的都是key-value对,每行中的key-value对中的key可以是各种各样,每行中的key-value对的数量也可以是各种各样

1.3.1. hbase表模型的要点:

  1、一个表,有表名

  2、一个表可以分为多个列族(不同列族的数据会存储在不同文件中)

  3、表中的每一行有一个“行键rowkey”,而且行键在表中不能重复

  4、表中的每一对kv数据称作一个cell

  5、hbase可以对数据存储多个历史版本(历史版本数量可配置)

  6、整张表由于数据量过大,会被横向切分成若干个region(用rowkey范围标识),不同region的数据也存储在不同文件中

  7、hbase会对插入的数据按顺序存储:

    要点一:首先会按行键排序

    要点二:同一行里面的kv会按列族排序,再按k排序 

1.3.2. hbase的表中能存储什么数据类型

  hbase中只支持byte[]。此处的byte[] 包括了: rowkey,key,value,列族名,表名

1.3.3. HBASE表的物理存储结构

1.4. 3/ hbase命令行客户端操作

1.4.0.1. 建表:

  create 't_user_info','base_info','extra_info'

               表名      列族名   列族名

1.4.0.2. 插入数据:

hbase(main):011:0> put 't_user_info','001','base_info:username','zhangsan'
0 row(s) in 0.2420 seconds
hbase(main):012:0> put 't_user_info','001','base_info:age','18'
0 row(s) in 0.0140 seconds
hbase(main):013:0> put 't_user_info','001','base_info:sex','female'
0 row(s) in 0.0070 seconds
hbase(main):014:0> put 't_user_info','001','extra_info:career','it'
0 row(s) in 0.0090 seconds
hbase(main):015:0> put 't_user_info','002','extra_info:career','actoress'
0 row(s) in 0.0090 seconds
hbase(main):016:0> put 't_user_info','002','base_info:username','liuyifei'
0 row(s) in 0.0060 seconds

1.4.0.3. 查询数据方式一:scan 扫描

hbase(main):017:0> scan 't_user_info'
ROW                               COLUMN+CELL                                                                                     
 001                              column=base_info:age, timestamp=1496567924507, value=18                                         
 001                              column=base_info:sex, timestamp=1496567934669, value=female                                     
 001                              column=base_info:username, timestamp=1496567889554, value=zhangsan                              
 001                              column=extra_info:career, timestamp=1496567963992, value=it                                     
 002                              column=base_info:username, timestamp=1496568034187, value=liuyifei                              
 002                              column=extra_info:career, timestamp=1496568008631, value=actoress                               
2 row(s) in 0.0420 seconds

1.4.0.4. 查询数据方式二:get 单行数据

hbase(main):020:0> get 't_user_info','001'
COLUMN                            CELL                                                                                            
 base_info:age                    timestamp=1496568160192, value=19                                                               
 base_info:sex                    timestamp=1496567934669, value=female                                                           
 base_info:username               timestamp=1496567889554, value=zhangsan                                                         
 extra_info:career                timestamp=1496567963992, value=it                                                               
4 row(s) in 0.0770 seconds 

1.4.0.5. 删除一个kv数据

hbase(main):021:0> delete 't_user_info','001','base_info:sex'
0 row(s) in 0.0390 seconds

  删除整行数据: 

hbase(main):024:0> deleteall 't_user_info','001'
0 row(s) in 0.0090 seconds
hbase(main):025:0> get 't_user_info','001'
COLUMN                            CELL                                                                                            
0 row(s) in 0.0110 seconds 

1.4.0.6. 删除整个表:

hbase(main):028:0> disable 't_user_info'
0 row(s) in 2.3640 seconds
hbase(main):029:0> drop 't_user_info'
0 row(s) in 1.2950 seconds
hbase(main):030:0> list
TABLE                                                                                                                             
0 row(s) in 0.0130 seconds 
=> []

1.5. 4/ Hbase重要特性--排序特性(行键)

  插入到hbase中去的数据,hbase会自动排序存储:

  排序规则:  首先看行键,然后看列族名,然后看列(key)名; 按字典顺序

  Hbase的这个特性跟查询效率有极大的关系

  比如:一张用来存储用户信息的表,有名字,户籍,年龄,职业....等信息

  然后,在业务系统中经常需要:

  查询某个省的所有用户

  经常需要查询某个省的指定姓的所有用户

  思路:如果能将相同省的用户在hbase的存储文件中连续存储,并且能将相同省中相同姓的用户连续存储,那么,上述两个查询需求的效率就会提高!!!

  做法:将查询条件拼到rowkey内

1.6. 5/ HBASE客户端API操作

1.6.1. DDL操作

  1、创建一个连接

    Connection conn = ConnectionFactory.createConnection(conf);

  2、拿到一个DDL操作器:表管理器admin

    Admin admin = conn.getAdmin();

  3、用表管理器的api去建表、删表、修改表定义

    admin.createTable(HTableDescriptor descriptor); 

1.7. 6 HBASE运行原理

1.7.1. 组件结构图

 

1.7.2. MASTER职责

  • 管理HRegionServer,实现其负载均衡。
  • 管理和分配HRegion,比如在HRegion split时分配新的HRegion;在HRegionServer退出时迁移其负责的HRegion到其他HRegionServer上。
  • Admin职能  创建、删除、修改Table的定义。实现DDL操作(namespace和table的增删改,column familiy的增删改等)。
  • 管理namespace和table的元数据(实际存储在HDFS上)。
  • 权限控制(ACL)。
  • 监控集群中所有HRegionServer的状态(通过Heartbeat和监听ZooKeeper中的状态)。

1.7.3. REGION SERVER职责

  • 管理自己所负责的region数据的读写。
  • 读写HDFS,管理Table中的数据。
  • Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)。

1.7.4. 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节点。

1.7.5. 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的流程就变成了:

  ① 从ZooKeeper(/hbase/meta-region-server)中获取hbase:meta的位置(HRegionServer的位置),缓存该位置信息。

  ② 从HRegionServer中查询用户Table对应请求的RowKey所在的HRegionServer,缓存该位置信息。

  ③ 从查询到HRegionServer中读取Row。

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

1.7.6. hbase:meta表

  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的启动时间戳。

 

1.7.7. 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不是一件好事,可能引起长时间的延迟

 

Java API接口程序

  1 import java.io.IOException;
  2 
  3 import org.apache.hadoop.conf.Configuration;
  4 import org.apache.hadoop.hbase.HBaseConfiguration;
  5 import org.apache.hadoop.hbase.HColumnDescriptor;
  6 import org.apache.hadoop.hbase.HTableDescriptor;
  7 import org.apache.hadoop.hbase.TableName;
  8 import org.apache.hadoop.hbase.client.Admin;
  9 import org.apache.hadoop.hbase.client.Connection;
 10 import org.apache.hadoop.hbase.client.ConnectionFactory;
 11 import org.apache.hadoop.hbase.regionserver.BloomType;
 12 import org.junit.Before;
 13 import org.junit.Test;
 14 
 15 public class HbaseClientDemo {
 16     Connection conn = null;
 17     
 18     @Before
 19     public void getConn() throws Exception{
 20         // 构建一个连接对象
 21         Configuration conf = HBaseConfiguration.create(); // 会自动加载hbase-site.xml
 22         conf.set("hbase.zookeeper.quorum", "hdp2-master:2181,hdp2-slaver1:2181,hdp2-slaver2:2181");
 23         
 24         conn = ConnectionFactory.createConnection(conf);
 25     }
 26     
 27     
 28     /**
 29      * DDL
 30      * @throws Exception 
 31      */
 32     @Test
 33     public void testCreateTable() throws Exception{
 34 
 35         // 从连接中构造一个DDL操作器
 36         Admin admin = conn.getAdmin();
 37         
 38         // 创建一个表定义描述对象
 39         HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("user_info"));
 40         
 41         // 创建列族定义描述对象
 42         HColumnDescriptor hColumnDescriptor_1 = new HColumnDescriptor("base_info");
 43         hColumnDescriptor_1.setMaxVersions(3); // 设置该列族中存储数据的最大版本数,默认是1
 44         
 45         HColumnDescriptor hColumnDescriptor_2 = new HColumnDescriptor("extra_info");
 46         
 47         // 将列族定义信息对象放入表定义对象中
 48         hTableDescriptor.addFamily(hColumnDescriptor_1);
 49         hTableDescriptor.addFamily(hColumnDescriptor_2);
 50         
 51         
 52         // 用ddl操作器对象:admin 来建表
 53         admin.createTable(hTableDescriptor);
 54         
 55         // 关闭连接
 56         admin.close();
 57         conn.close();
 58         
 59     }
 60     
 61     
 62     /**
 63      * 删除表
 64      * @throws Exception 
 65      */
 66     @Test
 67     public void testDropTable() throws Exception{
 68         
 69         Admin admin = conn.getAdmin();
 70         
 71         // 停用表
 72         admin.disableTable(TableName.valueOf("user_info"));
 73         // 删除表
 74         admin.deleteTable(TableName.valueOf("user_info"));
 75         
 76         
 77         admin.close();
 78         conn.close();
 79     }
 80     
 81     // 修改表定义--添加一个列族
 82     @Test
 83     public void testAlterTable() throws Exception{
 84         
 85         Admin admin = conn.getAdmin();
 86         
 87         // 取出旧的表定义信息
 88         HTableDescriptor tableDescriptor = admin.getTableDescriptor(TableName.valueOf("user_info"));
 89         
 90         
 91         // 新构造一个列族定义
 92         HColumnDescriptor hColumnDescriptor = new HColumnDescriptor("other_info");
 93         hColumnDescriptor.setBloomFilterType(BloomType.ROWCOL); // 设置该列族的布隆过滤器类型
 94         
 95         // 将列族定义添加到表定义对象中
 96         tableDescriptor.addFamily(hColumnDescriptor);
 97         
 98         
 99         // 将修改过的表定义交给admin去提交
100         admin.modifyTable(TableName.valueOf("user_info"), tableDescriptor);
101         
102         
103         admin.close();
104         conn.close();
105         
106     }
107 }
  1 import java.util.ArrayList;
  2 import java.util.Iterator;
  3 
  4 import org.apache.hadoop.conf.Configuration;
  5 import org.apache.hadoop.hbase.Cell;
  6 import org.apache.hadoop.hbase.CellScanner;
  7 import org.apache.hadoop.hbase.HBaseConfiguration;
  8 import org.apache.hadoop.hbase.TableName;
  9 import org.apache.hadoop.hbase.client.Connection;
 10 import org.apache.hadoop.hbase.client.ConnectionFactory;
 11 import org.apache.hadoop.hbase.client.Delete;
 12 import org.apache.hadoop.hbase.client.Get;
 13 import org.apache.hadoop.hbase.client.Put;
 14 import org.apache.hadoop.hbase.client.Result;
 15 import org.apache.hadoop.hbase.client.ResultScanner;
 16 import org.apache.hadoop.hbase.client.Scan;
 17 import org.apache.hadoop.hbase.client.Table;
 18 import org.apache.hadoop.hbase.util.Bytes;
 19 import org.junit.Before;
 20 import org.junit.Test;
 21 
 22 public class HbaseClientDML {
 23     Connection conn = null;
 24     
 25     @Before
 26     public void getConn() throws Exception{
 27         // 构建一个连接对象
 28         Configuration conf = HBaseConfiguration.create(); // 会自动加载hbase-site.xml
 29         conf.set("hbase.zookeeper.quorum", "hdp2-master:2181,hdp2-slaver1:2181,hdp2-slaver2:2181");
 30         
 31         conn = ConnectionFactory.createConnection(conf);
 32     }
 33     
 34     
 35     /**
 36      * 增
 37      * 改:put来覆盖
 38      * @throws Exception 
 39      */
 40     @Test
 41     public void testPut() throws Exception{
 42         
 43         // 获取一个操作指定表的table对象,进行DML操作
 44         Table table = conn.getTable(TableName.valueOf("user_info"));
 45         
 46         // 构造要插入的数据为一个Put类型(一个put对象只能对应一个rowkey)的对象
 47         Put put = new Put(Bytes.toBytes("001"));
 48         put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("张三"));
 49         put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("18"));
 50         put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("北京"));
 51         
 52         
 53         Put put2 = new Put(Bytes.toBytes("002"));
 54         put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("李四"));
 55         put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("28"));
 56         put2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("上海"));
 57     
 58         
 59         ArrayList<Put> puts = new ArrayList<>();
 60         puts.add(put);
 61         puts.add(put2);
 62         
 63         
 64         // 插进去
 65         table.put(puts);
 66         
 67         table.close();
 68         conn.close();
 69         
 70     }
 71     
 72     
 73     /**
 74      * 循环插入大量数据
 75      * @throws Exception 
 76      */
 77     @Test
 78     public void testManyPuts() throws Exception{
 79         
 80         Table table = conn.getTable(TableName.valueOf("user_info"));
 81         ArrayList<Put> puts = new ArrayList<>();
 82         
 83         for(int i=0;i<100000;i++){
 84             Put put = new Put(Bytes.toBytes(""+i));
 85             put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("张三"+i));
 86             put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes((18+i)+""));
 87             put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("北京"));
 88             
 89             puts.add(put);
 90         }
 91         
 92         table.put(puts);
 93         
 94     }
 95     
 96     /**
 97      * 删
 98      * @throws Exception 
 99      */
100     @Test
101     public void testDelete() throws Exception{
102         Table table = conn.getTable(TableName.valueOf("user_info"));
103         
104         // 构造一个对象封装要删除的数据信息
105         Delete delete1 = new Delete(Bytes.toBytes("001"));
106         
107         Delete delete2 = new Delete(Bytes.toBytes("002"));
108         delete2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"));
109         
110         ArrayList<Delete> dels = new ArrayList<>();
111         dels.add(delete1);
112         dels.add(delete2);
113         
114         table.delete(dels);
115         
116         
117         table.close();
118         conn.close();
119     }
120     
121     /**
122      * 查
123      * @throws Exception 
124      */
125     @Test
126     public void testGet() throws Exception{
127         
128         Table table = conn.getTable(TableName.valueOf("user_info"));
129         
130         Get get = new Get("002".getBytes());
131         
132         Result result = table.get(get);
133         
134         // 从结果中取用户指定的某个key的value
135         byte[] value = result.getValue("base_info".getBytes(), "age".getBytes());
136         System.out.println(new String(value));
137         
138         System.out.println("-------------------------");
139         
140         // 遍历整行结果中的所有kv单元格
141         CellScanner cellScanner = result.cellScanner();
142         while(cellScanner.advance()){
143             Cell cell = cellScanner.current();
144             
145             byte[] rowArray = cell.getRowArray();  //本kv所属的行键的字节数组
146             byte[] familyArray = cell.getFamilyArray();  //列族名的字节数组
147             byte[] qualifierArray = cell.getQualifierArray();  //列名的字节数据
148             byte[] valueArray = cell.getValueArray(); // value的字节数组
149             
150             System.out.println("行键: "+new String(rowArray,cell.getRowOffset(),cell.getRowLength()));
151             System.out.println("列族名: "+new String(familyArray,cell.getFamilyOffset(),cell.getFamilyLength()));
152             System.out.println("列名: "+new String(qualifierArray,cell.getQualifierOffset(),cell.getQualifierLength()));
153             System.out.println("value: "+new String(valueArray,cell.getValueOffset(),cell.getValueLength()));
154             
155         }
156         
157         table.close();
158         conn.close();
159         
160     }
161     
162     
163     /**
164      * 按行键范围查询数据
165      * @throws Exception 
166      */
167     @Test
168     public void testScan() throws Exception{
169         
170         Table table = conn.getTable(TableName.valueOf("user_info"));
171         
172         // 包含起始行键,不包含结束行键,但是如果真的想查询出末尾的那个行键,那么,可以在末尾行键上拼接一个不可见的字节(\000)
173         Scan scan = new Scan("10".getBytes(), "10000\001".getBytes());
174         
175         ResultScanner scanner = table.getScanner(scan);
176         
177         Iterator<Result> iterator = scanner.iterator();
178         
179         while(iterator.hasNext()){
180             
181             Result result = iterator.next();
182             // 遍历整行结果中的所有kv单元格
183             CellScanner cellScanner = result.cellScanner();
184             while(cellScanner.advance()){
185                 Cell cell = cellScanner.current();
186                 
187                 byte[] rowArray = cell.getRowArray();  //本kv所属的行键的字节数组
188                 byte[] familyArray = cell.getFamilyArray();  //列族名的字节数组
189                 byte[] qualifierArray = cell.getQualifierArray();  //列名的字节数据
190                 byte[] valueArray = cell.getValueArray(); // value的字节数组
191                 
192                 System.out.println("行键: "+new String(rowArray,cell.getRowOffset(),cell.getRowLength()));
193                 System.out.println("列族名: "+new String(familyArray,cell.getFamilyOffset(),cell.getFamilyLength()));
194                 System.out.println("列名: "+new String(qualifierArray,cell.getQualifierOffset(),cell.getQualifierLength()));
195                 System.out.println("value: "+new String(valueArray,cell.getValueOffset(),cell.getValueLength()));
196             }
197             System.out.println("----------------------");
198         }
199     }
200     
201     @Test
202     public void test(){
203         String a = "000";
204         String b = "000\0";
205         
206         System.out.println(a);
207         System.out.println(b);
208         
209         
210         byte[] bytes = a.getBytes();
211         byte[] bytes2 = b.getBytes();
212         
213         System.out.println("");
214         
215     }
216 }

 

posted @ 2018-10-26 20:15  _再遇见  阅读(464)  评论(0编辑  收藏  举报