HBase精讲(上)

一、Hbase下载安装

Hbase下载及安装详情请查看:Hbase下载安装.

二、HBase 架构原理

2.1、HBase 架构

HBase 也是 Master/slaves 架构,从前面安装环境应该能看出,HBase 分布式环境安装成功后,是有一个 HMaster,多个 HResgionServer 进程。HBase 架构如图所示:
图 HBase 架构图
从上图中能看出 HBase 是由 Client、ZooKeeper、HMaster、HRegionServer、
HDFS 等几个组件组成,组件的相关功能:

2.1.1、Client:客户端

可以是 HBase Shell、Java API 客户端、RestAPI 等
Client 就是用来访问 HBase 数据库的,它不仅提供了访问接口,还维护了对应的缓存(cache)来加速 HBase 的访问。Client 端的缓存主要是缓存 Region 的位置信息,减少获取元数据的时间。当 Client 端没有缓存的时候(第一次请求),会加载 Region 的位置信息到 Client 端,后面直接使用 cache 中的信息,如果出现重试,则会重新获取 Region 的位置信息,更新 Client 端的 cache。

2.1.2、ZooKeeper

HBase 通过 ZooKeeper 来做 HMaster 的高可用、HRegionServer 的监控、元数据的入口以及集群配置的维护等工作。它具体工作如下:

(1)通过 ZoopKeeper 来保证集群中只有 1 个 HMaster 在运行,如果 HMaster异常,会通过竞争机制产生新的 HMaster 提供服务。
(2)通过 ZoopKeeper 来监控 HRegionServer 的状态,当 HRegionSevrer 有异
常的时候,通过 Master 会收到 ZooKeeper 消息通知 HRegionServer 上下线的信息。
(3)通过 ZoopKeeper 存储元数据的统一入口地址(该部分内容后面会介绍具体存储了什么元数据信息)。

2.1.3、HMaster

HMaster 节点的主要职责如下:

(1)监 控 HRegionServer ,处理 HRegionServer 故 障 转 移 , 当 某 个 HRegionServer 挂掉时,ZooKeeper 会将分配在该 HRegionServer 上的 HRegion 分配到其他 HRegionServer 上进行管理。
(2)HRegion 分裂后,负责新的 HRegion 的分配。
(3)处理元数据的变更,比如对表的添加,修改,删除等操作。
(4)在空闲时间进行数据的负载均衡,主要就是在 HRegionServer 间迁移 HRegion,达到负载均衡。
(5)通过 ZooKeeper 发布自己的位置给客户端。

2.1.4、HRegionServer

HRegionServer 直接负责用户的读写请求,是真正的“干活”的节点。它的功能概括如下:

(1)负责和底层 HDFS 的交互,存储数据到 HDFS,HBase 是依托于 HDFS 的 NoSQL 数据库,数据会存储在 HDFS 上。后面《数据 Flush 过程》会解释什么时候执行数据存储到 HDFS 上的操作。
(2)处理分配给它的 HRegion。
(3)刷新缓存到 HDFS。
(4)维护 HLog,HLog 是一个容错机制,下面的内容会介绍到 HLog 的功能。
(5)处理来自客户端的读写请求。HRegionServer 是实际管理数据的,所以客户端的读写请求最终都要由 HRegionServer 来处理。
(6)负责处理 HRegion 变大后的拆分。
(7)负责 StoreFile 的合并工作。

2.1.5、HDFS

HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用(HLog 存储在 HDFS)的支持,具体功能概括如下:

(1)提供元数据和表数据的底层分布式存储服务。
(2)数据多副本,保证的高可靠和高可用性。

2.1.6、Write-Ahead Logs

Write-Ahead Logs 也叫预写日志,在 HBase 中为 HLog,HLog 存储在 HDFS 上。为什么存在要有预写日志呢?其实跟 HBase 架构设计有关,当对 HBase 读写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问题,数据在写入内存之前会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。需要注意的是,HLog 是每一个 RegionServer 对应一个

2.1.7、HRegion

HRegion 可以看成是表的横向切分,HBase 表的分片,HBase 每个表都会根据 RowKey 值被切分成不同的 HRegion 分散存储在HRegionServer 中,在一个HRegionServer 中可以有多个不同的 HRegion。这些 HRegion 可以是来自不同的表。HRegion、Table、HRegionServer 关系如下面的图所示。(该部分可以画图使用两个表 TableA、TableB,分别切成 HRegion,分散存储在 HRegionServer 上进行理解)
图 HRegion、Table、HRegionServer 关系图
值得注意的是,一个 HRegion 只能在一个 HRegionServer 上,不能跨多个 HRegionServer。

2.1.8、Store

每一个 Region 内部,又分为多个 Store,一个 Store 对应表中的一个列族。也就是说一个 Store 存储了对应表该列族下的部分数据。

2.1.9、MemStore

顾名思义,就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在 WAL 中之后,HRegsionServer 会在内存中存储键值对。该组件功能可以看该文档《读流程》和《写流程》部分进行更深刻的理解。

2.1.10、HFile

这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile 是以 HFile 的形式存储在 HDFS 的。文件内容是二进制。组件功能可以看该文档《写流程》和《数据 Flush 过程》。

2.2、HBase 数据存储结构

HBase 是稀疏的,稀疏主要是针对 HBase 列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。HBase 底层存储的数据是 KeyValue 格式的数据。这一块内容在 hbase shell 插入数据部分有详细的解释。HBase 逻辑上也可以看成一个二维表格,可以添加行,可以动态添加列。

2.2.1、RowKey

RowKey 是用来检索记录的主键。类似关系型数据库中的主键,但是 RowKey 在 HBase 中功能不只是为了检索,HBase 中表数据会按照 RowKey 进行排序,并会在 HRegion 达到一定大小后,会按照 RowKey 范围进行裂变(该部分内容在下一课内容中会详解)。

HBase Table 中的行,只有三种方式:
(1)通过单个 RowKey 访问
(2)通过 RowKey 的 range(正则)
(3)全表扫描
RowKey 行键 (RowKey)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在 HBase 内部,RowKey 保存为字节数组。存储时,数据按照 RowKey 的字典序排序存储。设计 RowKey 时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)。

2.2.2、Column Family

列族是 HBase 对表在纵向上的优化,前面介绍过 Region,将一个表横向上切成多个 Region,列族是在纵向进行切分的,将多列分成一组进行管理。

列族在在物理存储上对应于 HDFS 上的一个目录。HBase 表中的每个列,都归属于某个列族。列族是表的 schema 的一部分(而列不是),必须在使用表之前定义(也就是创建表时定义)。列名都以列族作为前缀。例如 courses:history,courses:math 都属于 courses 这个列族。

每一个列族对应一个 Store,也对应 HDFS 一个目录,类似于 Hive 分区操作一样,HBase 相当于按列族进行了分区。(该部分内容可以辅助查看 HBase Shell 操作插入数据部分内容)

2.2.3、Cell

由{RowKey, ColumnFamily, Version} 唯一确定的单元。其中 Versions 实际上是 TimeStamp。Cell 可以看成是一个存储空间,类似与 Excel 中的单元格,Cell 中的数据是没有类型的,全部是字节码形式存储。

2.2.4、Time Stamp

HBase中通过rowkey和 ColumnFamily:Column确定的一个存储单元称为Cell。每个 Cell 都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64 位整型。时间戳可以由 HBase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒 的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。

为了避免数据存在过多版本造成的管理 (包括存储和索引)负担,HBase 提供了两种数据版本回收方式。一是保存数据的最后 n 个版本,二是保存最近一段时间内的版本(比如最近七天)。

2.3、HBase 原理

2.3.1、元数据存储

HBase 中有一个系统表 hbase:meta 存储 HBase 元数据信息,可以在 HBase Web UI 查看到相关信息。如下图。
图 HBase 元数据表
该表记录保存了每个表的 Region 地址,还有一些其他信息,例如 Region 的名字,对应表的名字,开始行键,结束行键,服务器的信息。hbase:meta 表中每一行对应一个单一的 Region。数据如下图。
图 hbase:meta 表数据格式
在这里插入图片描述
ZooKeeper 中存储了 hbase:meta 表的位置,客户端可以通过 ZooKeeper 查找到 hbase:meta 表的位置,hbase:meta 是 hbase 当中一张表,肯定由一个 HRegionServer 来 管 理 , 其 实 主 要 就 是 要 通 过 ZooKeeper 的 “/hbase/meta-region-server”获取存储“hbase:meta”表的 HRegionServer 的地址。

2.3.2、读流程(重点)

HBase 读数据流程如图所示:
在这里插入图片描述
(1)Client 先访问 ZooKeeper,从 meta 表读取 Region 的位置,然后读取 meta
表中的数据。meta 中又存储了用户表的 Region 信息;
(2)根据 RowKey 在 meta 表中找到对应的 Region 信息;
(3)找到这个 Region 对应的 RegionServer;
(4)查找对应的 Region;
(5)先从 MemStore 找数据,如果没有,再到 BlockCache 里面读;
(6)BlockCache 还没有,再到 StoreFile 上读(为了读取的效率);
(7)如果是从 StoreFile 里面读取的数据,不是直接返回给客户端,而是先写入 BlockCache,再返回给客户端。从整体的方面看,如下图所示
在这里插入图片描述

2.3.3、写流程(重点)

Hbase 写流程如图所示(写数据比读数据要快):
在这里插入图片描述
(1)Client 访问 ZooKeeper,获取 Meta 表所处位置(ip)
(2)访问 Meta 表,然后读取 Meta 表中的数据
(3)根据 namespace(类似与关系型数据库中的数据库,下一课会介绍)、表名和 RowKey 在 Meta 表中找到该 RowKey 应该写入到哪个 Region
(4)找到这个 Region 对应的 RegionServer,并发送写数据请求
(5)HRegionServer 将数据先写到 HLog(Write Ahead Log)。为了数据的持久化和恢复
(6)HRegionServer 将数据写到内存(MemStore)
(7)反馈 Client 写成功

写数据这一块也可以看出,HBase 将数据写入到内存中后,就返回给客户端写入成功,响应非常快。这也是为什么 HBase 写数据速度快的原因。

2.3.4、数据 Flush 过程

从上面写入数据的流程中可以看出,HBase 写数据是写入到 MemStore 内存就会返回客户端了,并没有直接落磁盘。这也是为什么 HBase 插入数据会比较快的原因,磁盘 IO 非常小。那么什么时候数据会落磁盘呢?其实 MemStore 空间是有限的,当 MemStore 数据达到阈值(默认是 128M,老版本是 64M),RegionServer 将数据刷到 HDFS 上,生成 HFile,然后将内存中的数据删除,同时删除 HLog 中的历史数据。该操作是由 RegionServer 自己完成的。

三、Hbase Shell 操作

3.1、基本操作

(1)进入 HBase 客户端命令行

hbase shell

(2)查看帮助命令

help

(3)查看当前数据库中有哪些表

list

3.2、表的操作

(1)创建表

create 'customer', {NAME=>'addr'}, {NAME=>'order'}

NAME=>‘xxx’,xxx 为列族的名字。HBase 中创建一个表,必须指定至少一个列族

创建完表后,可以在 HDFS 上查看对应的目录结构,表其实就是对应 HDFS 上一个目录。列族就对应 HDFS 上一个文件夹。
表在 HDFS 上结构
列族在 HDFS 上结构图
(2)插入数据到表

put 'customer', 'jsmith', 'addr:city', 'montreal'
put 'customer', 'jsmith', 'addr:state', 'ON'
put 'customer', 'jsmith', 'order:numb', '123456'
put 'customer', 'jsmith', 'order:date', '2015-12-19'

插入数据后,到对应的列族目录下,发现没有对应的文件生成,如下图。
在这里插入图片描述
这就证明,HBase 写入数据是没有直接落磁盘的,是先在缓冲中的。为了方便演示,可以手动刷出缓存,命令为 flush,在命令行输入 flush,会提示使用方法。、

不手动刷出缓存则会等缓存满(128M)了以后自动刷出

 flush 'customer'

flush 完成后,可以看到对应的列族目录下,有数据文件了,该文件就是 HFile。
刷出缓存后列族 addr 目录
列族目录下的文件,可以尝试使用 hdfs 命令查看其内容,会发现是二进制的内容。HBase 提供了查看该文件内容的方式。下面命令在 Linux 命令行执行,不是在 hbase shell 中。

hbase hfile -v -p -f /path...
hbase hfile -v -p -f /hbase/data/default/customer/4474c890bdded1896e16cfdab597a956/addr/a6497c8fcab24c48be20c7c6b90392ea

执行完后,会输出下面的内容(只是部分,元数据信息没有列出):

K: jsmith/addr:city/1575960250464/Put/vlen=8/seqid=4 V: montreal
K: jsmith/addr:state/1575960257776/Put/vlen=2/seqid=5 V: ON

前面是 K,后面是 V,从这里也能看出 HBase 是 KeyValue 数据库。

(3)插入多行数据

put 'customer', 'jsmith', 'order:numb', '1235'
put 'customer', 'jsmith', 'order:numb', '1236'
put 'customer', 'jsmith', 'order:numb', '1237'
put 'customer', 'jsmith', 'order:numb', '1238'
put 'customer', 'njones', 'addr:city', 'miami'
put 'customer', 'njones', 'addr:state', 'FL'
put 'customer', 'njones', 'order:numb', '5555'
put 'customer', 'tsimmons', 'addr:city', 'dallas'
put 'customer', 'tsimmons', 'addr:state', 'TX'
put 'customer', 'jsmith', 'addr:city', 'denver'
put 'customer', 'jsmith', 'addr:state', 'CO'
put 'customer', 'jsmith', 'order:numb', '6666'
put 'customer', 'njones', 'addr:state', 'TX'
put 'customer', 'amiller', 'addr:state', 'TX'

(4)扫描查看表

scan 'customer'
scan 'customer', {COLUMNS=>['order:numb'], VERSIONS => 2}
scan 'customer', {STARTROW => 'j', STOPROW => 't'}

(5)查看表结构

desc 'customer'

(6)更新指定字段的数据

put 'customer', 'jsmith', 'order:numb', '654321'

(7)查看“指定行”或“指定列族:列”的数据

get 'customer', 'jsmith'
get 'customer', 'jsmith','addr'
get 'customer', 'jsmith','addr:city'

(8)统计表数据行数

count 'customer'

(9)删除数据

删除某 rowkey 的全部数据

deleteall 'customer','jsmith'

删除某 rowkey 的某一列数据

delete 'customer','njones','addr:city'

(10)清空表数据

truncate 'customer'

提示:清空表的操作顺序为先 disable,然后再 truncate。disable 的操作会自动完成。

(11)删除表

注意需要先 disable 表,不然直接删除会报错。

首先需要先让该表为 disable 状态:

disable 'customer'

然后才能 drop 这个表:

drop 'customer'

提示:如果直接 drop 表,会报错:ERROR: Table customer is enabled. Disable it first.

(12)变更表信息

将 order 列族中的数据设置为可以存放 5 个版本:

alter 'customer',NAME=>'order', VERSIONS=>5

(13)获取多版本数据

先更新一条数据

put 'customer', 'tsimmons', 'order:city', 'beijing'
put 'customer', 'tsimmons', 'order:city', 'beijing2'

查看多版本数据

get 'customer','tsimmons',{COLUMN=>'order:city',VERSIONS=>2}

继续添加数据

put 'customer', 'tsimmons', 'order:city', 'beijing3'
put 'customer', 'tsimmons', 'order:city', 'beijing4'
put 'customer', 'tsimmons', 'order:city', 'beijing5'
put 'customer', 'tsimmons', 'order:city', 'beijing6'

get 'customer','tsimmons',{COLUMN=>'order:city',VERSIONS=>5}
get 'customer','tsimmons',{COLUMN=>'order:city',VERSIONS=>6}

3.3、名称空间(NameSpace)

NameSpace:一种易于表管理的机制
(1)创建 NameSpace

create_namespace '名称'

(2)列出所有 NameSpace

list_namespace

(3)NameSpace 下创建表

create 'namespace名字:表名', '列族名'

(4)删除 NameSpace

drop_namespace '名称'

(5)更改 NameSpace 属性

alter_namespace 'namespace名称', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'} 

3.4、权限操作

我们可以为用户赋予RWXCA权限中的一个或多个

R		read privilege		读权限
W		write privilege		写权限
C		create privilege	创建权限	
A		admin privilege		管理权限
X		execute				执行权限

(1)使用 grant 命令授权

//语法:
grant <user> <permissions> <table> [<column family> [<column:qualifier>]] 
//示例:
grant 'will', 'RWCA', 'my_hbase_table'

(2)使用 revoke 命令删除权限

//语法:
revoke <user> <table> [<column family> [<column:qualifier>]]
//示例:
revoke 'will', 'my_hbase_table'  

(3)使用 user_permission 查看用户对表所拥有的权限

//语法:
user_permission 'tablename'
//示例:
user_permission 'my_hbase_table'

四、HBase API 操作

项目部分会使用到从 Kafka 消费数据,然后存储到 HBase 数据库,使用的是 HBase Java API

4.1、环境准备

新建 Maven 项目后在 pom.xml 中添加依赖

	<properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <hadoop.version>2.6.0</hadoop.version>
        <hive.version>1.1.0</hive.version>
        <hbase.version>1.2.0</hbase.version>
    </properties>

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-common</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-client</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-hdfs</artifactId>
      <version>${hadoop.version}</version>
    </dependency>

    <!--日志-->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

    <!--MapReduce-->
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-auth</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-mapreduce-client-core</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    
    <!--zookeeper-->
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.4.5</version>
      <type>pom</type>
    </dependency>

    <!--hbase-->
    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-client</artifactId>
      <version>${hbase.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-common</artifactId>
      <version>${hbase.version}</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-server</artifactId>
      <version>${hbase.version}</version>
    </dependency>

    <!--log4j-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <!--测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
    </dependency>
  </dependencies>

4.2、Hbase Java API

4.2.1、建表

	@Test
    public void createTable() throws IOException {
        //1.获取hbase连接,配置
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","hadoop");
        conf.set("hbase.zookeeper.property.clientPort","2181");
        //2.创建连接
        Connection conn = ConnectionFactory.createConnection(conf);
        //3.创建admin
        Admin admin = conn.getAdmin();
        //4.创建表的相关信息,表名
        HTableDescriptor student = new HTableDescriptor(TableName.valueOf("student"));
        //5.添加列簇信息
        student.addFamily(new HColumnDescriptor("info"));
        student.addFamily(new HColumnDescriptor("score"));
        //6.调用创建表单方法,进行建表操作
        admin.createTable(student);
        //关闭连接
        conn.close();
    }

4.2.2、向表中添加数据

	@Test
    public void putData2Table() throws IOException {
        //1.配置
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","hadoop");
        conf.set("hbase.zookeeper.property.clientPort","2181");
        //2.创建连接
        Connection conn = ConnectionFactory.createConnection(conf);
        //3.获取table
        Table student = conn.getTable(TableName.valueOf("student"));
        //4.往表中添加数据roekey
        Put put = new Put(Bytes.toBytes("1001"));
        //5.添加列 info:name zhangsan
        put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("zhangsan"));
        put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("gender"),Bytes.toBytes("male"));
        put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"),Bytes.toBytes("18"));
        //6.插入数据
        student.put(put);
        //7.关闭连接
        conn.close();
    }

4.2.3、读取数据

	@Test
    public void getDataFromTable() throws IOException {
        //1.配置
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","hadoop");
        conf.set("hbase.zookeeper.property.clientPort","2181");
        //2.创建连接
        Connection conn = ConnectionFactory.createConnection(conf);
        //3.获取table
        Table student = conn.getTable(TableName.valueOf("student"));
        //4.读取数据,使用Get
        Get get = new Get(Bytes.toBytes("1001"));
        //5.调用API获取结果
        Result result = student.get(get);
        //6.遍历输出
        Cell[] cells = result.rawCells();
        for (Cell cell : cells) {
            //获取值
            System.out.println("rowkey:" + Bytes.toString(CellUtil.cloneRow(cell)));
            System.out.println("列簇:" + Bytes.toString(CellUtil.cloneFamily(cell)));
            System.out.println("列名:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
            System.out.println("value:" + Bytes.toString(CellUtil.cloneValue(cell)));
            System.out.println("---------------------------------------------------");
        }
        //7.关闭连接
        conn.close();
    }

4.2.4、删除表

	@Test
    public void dropTable() throws IOException {
        //1.配置
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","hadoop");
        conf.set("hbase.zookeeper.property.clientPort","2181");
        //2.创建连接
        Connection conn = ConnectionFactory.createConnection(conf);
        //3.获取admin
        Admin admin = conn.getAdmin();
        //4.禁用表
        admin.disableTable(TableName.valueOf("student"));
        //5.删除表
        admin.deleteTable(TableName.valueOf("student"));
        //6.关闭连接
        conn.close();
    }

4.3、 Java API 操作

运行命令:java -cp jar 包 运行主类

可能出现的问题是打包运行的时候如果不是 fat 包,会报如下错误。

NoClassDefFoundError 异常
需要打成 fat 包

Hbase精讲(下)

Hbase精讲后续更精彩:Hbase精讲(下)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值