HBase


介绍

定义

HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库


数据模型

逻辑上,HBase 的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列。

但从 HBase 的底层物理存储结构(K-V)来看,HBase 更像是一个 multi-dimensional map


逻辑结构

HBase表由行和列组成,每个行由行键(row key)来标识,列划分为若干列族,一个列族中可以包含任意多个列,同一个列族里面的数据存储在一个文件中。当这个文件达到一定大小后,会进行分裂形成多个region。当一个行键在不同的列族中都有相应的列值的话,不同列族中的文件都会存储这个行键的值。也就是说,一行可能包含多个列族,一个列族有多个列,对某一行而言,某列族文件中只存储了这一行键在列族中有值的那些列(列族可能有上百个列),没有不会存储(不存null)

每一个region由一个或多个store组成,至少是一个store,hbase会把一起访问的数据放在一个store里面,即为每个ColumnFamily建一个store,如果有几个ColumnFamily,也就有几个Store。一个Store由一个memStore和0或者多个StoreFile组成。

HBase以store的大小来判断是否需要切分region


物理存储结构

当在t4时间put(插入)row_key1的phone数据时,原来t3的并不会马上被覆盖。当查询row_key1的phone时会返回时间戳最大的t4那一个数据(最新的)


数据模型

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

  • Column Family
    列族:HBASE 表中的每个列,都归属于某个列族。列族是表的 schema 的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于 courses 这个列族。

  • Cell
    {rowkey, column Family:column, version} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。 关键字:无类型、字节码

  • Time Stamp
    每个 cell 都保存着同一份数据的多个版本(version)。版本通过时间戳来索引。时间戳的类型是 64 位整型。时间戳可以由 HBASE(在数据写入时自动 )赋值,此时时间戳是精确到毫秒 的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。 为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,HBASE 提供 了两种数据版本回收方式。一是保存数据的最后 n 个版本,二是保存最近一段 时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。在HBase中,timestamp是一个很重要的概念。它记录着往HBase进行增删改操作的时间(系统自动赋值),它的值越大,说明是这个操作就越新,通常我们从HBase得到的只是那个最新操作的结果,但是之前的操作(时间戳小的)会保留直到达到一定的版本数或者设定时间。例如:

    #timestamp1<timestamp2<timestamp3
    put 'student','1002','info:name','shane'#step1,timestamp1=1585836527749
    put 'student','1002','info:name','shane'#step2,timestamp2=1585838153208
    #那么get 'student','1002','info:name'只会得到最新的timestamp2=1585838153208的数据,即value=shane
    #step1的数据在进行step2后并不会被立即删除,通过scan 'student',{RAW=>TRUE,VERSIONS=>3}可以查看同一个cell的最近的三个版本
    delete 'student','1002','info:name'#step3,timestamp3=1585838197596
    put 'student','1002','info:name','shane',1585838166666#step4,指定timestamp4=1585838166666
    #timestamp<timestamp3,timestamp3是最新的且是delete操作,所以在scan 'student'时并不会得到1002的info:name的信息。
    #但是通过scan 'student',{RAW=>TRUE,VERSIONS=>5}可以看到之前插入的信息

  • NameSpace
    命名空间的结构:

    1. Table:表,所有的表都是命名空间的成员,即表必属于某个命名空间,如果没有指定,则在 default 默认的命名空间中。
    2. RegionServer group:一个命名空间包含了默认的 RegionServer Group。
    3. Permission:权限,命名空间能够让我们来定义访问控制列表 ACL(Access Control List)。例如,创建表,读取表,删除,更新等等操作。
    4. Quota:限额,可以强制一个命名空间可包含的 region 的数量。
  • Region

    类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需

    要声明具体的列。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关

    系型数据库相比,HBase 能够轻松应对字段变更的场景


系统架构

Hbase 是由 Client、Zookeeper、Master、HRegionServer、HDFS 等几个组件组成,HBase依赖于ZooKeeper和HDFS。

  • Zookeeper :HBase 通过 Zookeeper 来做 master 的高可用(通过 Zoopkeeper 来保证集群中只有 1 个 master 在运行,如果 master 异常,会通过竞争机制产生新的 master 提供服务。)、RegionServer 的监控(通过 Zoopkeeper 来监控 RegionServer 的状态,当 RegionSevrer 有异常的时候,通过回调的形式通知 Master RegionServer 上下线的信息)、元数据的入口以及集群配置的维护等工作。(DML的请求通过ZK分发到HRegionServer不通过HMaster,HMaster是处理DDL的请求。HMaster宕机不会影响客户端的读写请求;但是取法进行create 'stu4','info'的DDL操作。当原有的Meta元数据信息改变时也无法维护。)
  • Hmaster监控 RegionServer为 RegionServer 分配 Region(维护整个集群的负载均衡,在空闲时间进行数据的负载均衡 ) ,维护集群的元数据信息处理 region 的分配或转移(发现失效的 Region,并将失效的 Region 分配到正常的 RegionServer 上 ;当 RegionSever 失效的时候,协调对应 Hlog 的拆分)
  • HregionServer:HregionServer 直接对接用户的读写请求,是真正的“干活”的节点。它的功能概括如下: 管理 master 为其分配的 Region处理来自客户端的读写请求负责和底层 HDFS 的交互(存储数据到 HDFS),负责 Region 变大以后的拆分负责 Storefile 的合并工作刷新缓存到HDFS维护Hlog
  • HDFS :为 Hbase 提供最终的底层数据存储服务,同时为HBase 提供高可用(Hlog 存储在HDFS)的支持,具体功能概括如下:提供元数据和表数据的底层分布式存储服务保证的高可靠和高可用性 (数据多副本)
  • MemStore :内存缓存,达到一定缓存大小或者时间节点触发一次 flush,文件系统中生成新的 HFile,每次 Flush 的最小单位是 Region。每个 Column family 维护一个 MemStore。
  • Write-Ahead logs(WAL,Hlog)用来容灾。当对 HBase 写数据的时候,数据会在内存MemStore中保留一段时间,MemStore达到一定的数据量(时间以及数据量阈值可以设定),数据再写进磁盘。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入内存中,所以在系统出现故障的时候,数据可以通过这个日志文件重建。 (参考文章参考文章
  • Region :Hbase表的分片,HBase 表会根据 RowKey 值被切分成不同的 region 存储在 RegionServer中,在一个 RegionServer 中可以有多个不同的 region。同一个行键的 Region 不会被拆分到多个 Region 服务器上。 一个HBase表被划分成多个Region,开始只有一个Region,后台不断分裂。

    一个表中包含多个列族,一个列族一个文件存储,region的切分是横向切分的,那么包含了多个列族。
  • Meta表:描述HBase表的表,元数据表。有了 Region 标识符,就可以唯一标识每个 Region。为了定位每个 Region 所在的位置,可以构建一张映射表。映射表的每个条目包含两项内容,一项是 Region 标识符,另一项是 Region 服务器标识。这个条目就表示 Region 和 Region 服务器之间的对应关系,从而就可以使用户知道某个 Region 存储在哪个 Region 服务器中。这个映射表包含了关于 Region 的元数据,因此也被称为“元数据表”,又名“Meta表”。使用 scan 命令可查看 Meta 表的结构,如图所示

    Meta 表中的每一行记录了一个 Region 的信息。RowKey 包含表名、起始行键和时间戳信息,中间用逗号隔开,第一个 Region 的起始行键为空。时间戳之后用.隔开的为分区名称的编码字符串,该信息是由前面的表名、起始行键和时间戳进行字符串编码后形成的。
    Meta 表里有一个列族 info。info 包含了三个列,分别为 Regioninfo、Server 和 Serverstartcode。Regionlnfo中记录了 Region 的详细信息,包括行键范围 StartKey 和 EndKey、列族列表和属性。
    Server 记录了管理该 Region 的 Region 服务器的地址,如 localhost:16201。Serverstartcode 记录了 Region 服务器开始托管该 Region 的时间。
    当用户表特别大时,用户表的 Region 也会非常多。Meta 表存储了这些 Region 信息,也变得非常大。Meta 表也需要划分成多个 Region,每个 Meta 分区记录一部分用户表和分区管理的情况。(有了meta表,就可以得到region和HRegionServer的对应关系,可以进行Region定位:客户端通过 ZooKeeper 获取 Meta 表(分区表)存储的地址,首先在对应的 Regionserver上获取 Meta 表的信息(meta表存在Regionserver上),得到所需的Region对应的Regionserver的信息,然后从Region 服务器上找到所需的数据)
  • Store :HFile 存储在 Store 中,一个 Store 对应 HBase 表中的一个列族。
  • HFile :这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile 是以 Hfile的形式存储在 HDFS 的。
  • Block Cache:见下方

安装部署

下载

https://hbase.apache.org/downloads.html


部署准备

首先保证 Zookeeper 集群的正常部署,并启动之:

hadoop02: bin/zkServer.sh start    
hadoop03: bin/zkServer.sh start    
hadoop04: bin/zkServer.sh start    

Hadoop 集群的正常部署并启动:

hadoop02: sbin/start-dfs.sh
hadoop03: sbin/start-yarn.sh

部署

修改 HBase 对应的配置文件。

  1. hbase-env.sh 修改内容

    export JAVA_HOME=/opt/module/jdk1.8.0_144
    export HBASE_MANAGES_ZK=false
  2. hbase-site.xml 修改内容

    <property>
        <name>hbase.rootdir</name>
        <value>hdfs://hadoop2:9000/HBase</value>
    </property>
    
    <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
    </property>
    
    <!-- 0.98 后的新变动,之前版本没有.port,默认端口为 60000 -->
    <property>
        <name>hbase.master.port</name>
        <value>16000</value>
    </property>
    
    <property>
        <name>hbase.unsafe.stream.capability.enforce</name>
        <value>false</value>
    </property>
    
    <property>
        <name>hbase.wal.provider</name>
        <value>filesystem</value>
    </property>
    
    
hbase.master.maxclockskew 180000 Time difference of regionserver from master hbase.zookeeper.quorum hadoop2,hadoop3,hadoop4 hbase.zookeeper.property.dataDir /opt/module/apache-zookeeper-3.6.1-bin/zkData ```
  1. regionservers:

    hadoop2
    hadoop3
    hadoop4
  2. 软连接 hadoop 配置文件到 HBase

    ln -s /opt/module/hadoop-3.3.0/etc/hadoop/core-site.xml  /opt/module/hbase-2.3.1/conf/core-site.xml
    ln -s /opt/module/hadoop-3.3.0/etc/hadoop/hdfs-site.xml  /opt/module/hbase-2.3.1/conf/hdfs-site.xml
  3. HBase远程发送到其他集群

     xsync hbase/

HBase服务启动的方式

单节点启动

 bin/hbase-daemon.sh start master
 bin/hbase-daemon.sh start regionserver

如果集群之间的节点时间不同步,会导致 regionserver 无法启动,抛出ClockOutOfSyncException等异常

解决办法

  1. 同步时间服务

  2. 属性:hbase.master.maxclockskew 设置更大的值

    <property>
        <name>hbase.master.maxclockskew</name>
        <value>180000</value>
        <description>Time difference of regionserver from master</description>
    </property

集群启动

bin/start-hbase.sh

停止服务

bin/stop-hbase.sh

查看HBase页面

启动成功后,可以通过“host:port”的方式来访问 HBase 管理页面,例如:

http://hadoop2:16010
或者
http://192.168.234.128:16010/

HBase Shell操作

基本操作

  1. 进入 HBase 客户端命令行

    bin/hbase shell
  2. 查看帮助命令

    help
  3. 查看当前数据库中有哪些表

     list
  4. 刷新数据

    flush 'student'
  5. 合并数据

    compact 'student'

DDL

  1. 创建表

    create 'student','info'
    create 'stu','info1','info2'
    create "bigdata:stu","stu"
  2. 查看表结构

     describe 'student'
  3. 变更表信息

    将 info 列族中的数据存放 3 个版本:

    alter 'student',{NAME=>'info',VERSIONS=>3}
  4. 删除表

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

    disable 'student'

    然后才能 drop 这个表:

    drop 'student'

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

  5. 创建命名空间

    create_namespace 'bigdata'
  6. 查看命名空间

    list_namespace
  7. 删除命名空间

    drop_namespace 'bigdata'

DML和DQL

  1. 插入数据

    put 'stu','1001','info1:name','zhangsan'
  2. 扫描查询数据

    scan 'stu'
    scan 'stu',{STARTROW=>'1001',STOPROW=>'1001'}
    scan 'stu',{RAW=>TRUE,VERSIONS=>10}
  3. 查看“指定行”或“指定列族:列”的数据

     get 'stu','1001'
     get 'stu','1001','info1:name'
  4. 更新指定字段的数据

    put 'stu','1001','info1:name','zhangsansan'
    put 'stu','1001','info1:sex','0',1563261123020  # 指定时间戳
    get 'stu','1001',{COLUMN=>'info1:name',VERSIONS=>3}  # 指定查看的版本数(与该列族的版本数有关)
  5. 删除数据

    删除某 rowkey 的全部数据:

    deleteall 'stu','10011'

    删除某 rowkey 的某一列数据:

    delete 'stu','1001','info1:sex' 
    delete 'stu','1001','info1:sex',1563261120000  # 指定时间戳
  6. 清空表数据

     truncate 'stu'

所有的增删改查都是看的时间戳


HBase进阶

架构原理


  1. StoreFile

    保存实际数据的物理文件,StoreFile 以 HFile 的形式存储在 HDFS 上。每个 Store 会有一个或多个StoreFile(HFile),数据在每个 StoreFile 中都是有序的

  2. MemStore

    写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好HBase 序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile

  3. WAL

    由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile (预写日志) 的文件中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重建


写流程

写流程

  1. Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
  2. 访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
  3. 与目标 Region Server 进行通讯;
  4. 将数据顺序写入(追加)到 WAL;
  5. 将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
  6. 向客户端发送 ack;
  7. 等达到 MemStore 的刷写时机后,将数据刷写到 HFile。


MemStore Flush

MemStore 刷写时机:

  1. 当某个 memstroe 的大小达到了 hbase.hregion.memstore.flush.size(默认值 128M), 其所在 region 的所有 memstore 都会刷写。

    当 memstore 的大小达到了hbase.hregion.memstore.flush.size(默认值128M)* hbase.hregion.memstore.block.multiplier(默认值4)时,会阻止继续往该 memstore 写数据。

  2. 当 region server 中 memstore 的总大小达到java_heapsize * hbase.regionserver.global.memstore.size(默认值0.4)* hbase.regionserver.global.memstore.size.lower.limit(默认值0.95),region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。直到 region server 中所有 memstore 的总大小减小到上述值以下。

    当 region server 中 memstore 的总大小达到java_heapsize * hbase.regionserver.global.memstore.size(默认值0.4) 时,会阻止继续往所有的 memstore 写数据。

  3. 到达自动刷写的时间,也会触发 memstore flush。自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval(默认 1 小时)

  4. 当 WAL 文件的数量超过 hbase.regionserver.max.logs,region 会按照时间顺序依次进行刷写,直到 WAL 文件数量减小到 hbase.regionserver.max.log 以下(该属性名已经废弃,现无需手动设置,最大值为 32)


读流程

  1. Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。

  2. 访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。

  3. 与目标 Region Server 进行通讯;

  4. 分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。

  5. 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到Block Cache。

  6. 将合并后的最终结果返回给客户端


StoreFile Compaction

由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。

Compaction 分为两种,分别是 Minor CompactionMajor Compaction。Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且清理掉过期和删除的数据

配置

<!-- 一个region进行 major compaction合并的周期,在这个点的时候, 这个region下的所有hfile会进行合并,默认是7天,major compaction非常耗资源,建议生产关闭(设置为0),在应用空闲时间手动触发 -->  
<property>  
    <name>hbase.hregion.majorcompaction</name>  
    <value>604800000</value>  
    <description>The time (in miliseconds) between 'major' compactions of  
        all  
        HStoreFiles in a region. Default: Set to 7 days. Major compactions tend to  
        happen exactly when you need them least so enable them such that they  
        run at  
        off-peak for your deploy; or, since this setting is on a periodicity that is  
        unlikely to match your loading, run the compactions via an external  
        invocation out of a cron job or some such.  
    </description>  
</property>  
<!-- 一个抖动比例,意思是说上一个参数设置是7天进行一次合并,也可以有50%的抖动比例 -->  
<property>  
    <name>hbase.hregion.majorcompaction.jitter</name>  
    <value>0.50</value>  
    <description>Jitter outer bound for major compactions.  
        On each regionserver, we multiply the hbase.region.majorcompaction  
        interval by some random fraction that is inside the bounds of this  
        maximum. We then add this + or - product to when the next  
        major compaction is to run. The idea is that major compaction  
        does happen on every regionserver at exactly the same time. The  
        smaller this number, the closer the compactions come together.  
    </description>  
</property>  

操作


删除数据

hbase官方文档中描述了,hbase删除数据可以总结为下面三种(Java API有很多接口,可以总结下面的几种):

  1. 删除一个列的指定版本
  2. 删除一个列的所用版本
  3. 删除指定列族的所有列

hbase删除数据,并不是马上删掉,只是对数据打一个删除标记

  1. 当flush后,该条数据会被删除,但是前面的数据依然会保留一个版本(取时间戳最大的,保留的数据跟你的表的VERSION有关),并且标记依然存在。

  2. major_compact(除非KEEP_DELETED_CELLS=true)。当删除整行时,hbase会给这条数据每个列族打一个删除标记。有两个需要注意的地方:

    • major_compact之前和之后,查询结果不一样
    • 删除会屏蔽时间戳靠前的put操作

    多个文件会进行一个合并,所以只会保留一条数据

  3. 删除整行时,是一个列族一个列族的进行删除


Region Split

默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。

Region Split 时机:

  1. 当1个region中的某个Store下所有StoreFile的总大小超过hbase.hregion.max.filesize, 该 Region 就会进行拆分(0.94 版本之前)。

  2. 当 1 个 region 中 的 某 个 Store 下所有 StoreFile 的 总 大 小 超 过 Min(R^2 * “hbase.hregion.memstore.flush.size”,hbase.hregion.max.filesize”),该 Region 就会进行拆分,其中 R 为当前 Region Server 中属于该 Table 的个数(0.94 版本之后)。

建议:尽量使用一个列族,多个列族可能到时候数据不均匀,flush之后,少量文件还进行了一个上传保存


HBase API

环境准备

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-server</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>1.3.1</version>
</dependency>

API操作

package top.codekiller.hbase.test;

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 java.io.IOException;

/**
 * @author codekiller
 * @date 2020/9/4 21:40
 * @Description HBase的基本API操作
 */
public class TestApi {

    private static  Configuration conf;
    private static  Connection conn;
    private static  HBaseAdmin admin;

    static{
        try {
            //1. 配置配置信息
            conf = HBaseConfiguration.create();
            conf.set("hbase.zookeeper.quorum", "39.97.180.158");
            //            conf.set("hbase.zookeeper.property.clientPort", "2181");

            //2. 创建连接对象
            conn = ConnectionFactory.createConnection(conf);

            //3. 创建admin对象
            admin = (HBaseAdmin) conn.getAdmin();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
    * @Description 判断表是否存在
    * @date 2020/9/4 22:32
    * @param tableName
    * @return java.lang.Boolean
    */
    public static Boolean isTableExist(String tableName)  {
        try {
            return admin.tableExists(tableName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
    * @Description 创建表
    * @date 2020/9/4 22:35
    * @param tableName
    * @param cfs
    * @return void
    */
    public static void createTable(String tableName,String... cfs){
        //1. 判断是否存在列族
        if (cfs.length < 0) {
            System.out.println("请输入列族信息!!");
            return;
        }

        //2. 判断表是否存在
        if(isTableExist(tableName)){
            System.out.println(tableName+"表已经存在!!");
            return;
        }

        //3. 创建表描述器
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));

        //4. 循环添加列族信息
        for (String cf : cfs) {
            //添加列族描述器
            HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(cf);

            //添加具体的列族信息
            hTableDescriptor.addFamily(hColumnDescriptor);
        }

        //5. 创建表
        try {
            admin.createTable(hTableDescriptor);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(tableName+"表创建失败!!");
        }
    }

    /**
    * @Description 删除表
    * @date 2020/9/4 22:50
    * @param tableName
    * @return void
    */
    public static void dropTable(String tableName) throws IOException {
        //1. 判断表是否存在
        if(isTableExist(tableName)){
            System.out.println(tableName+"表不存在!!");
            return;
        }

        //2. 使表下线
        admin.disableTable(tableName);

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

    /**
    * @Description 创建命名空间
    * @date 2020/9/4 22:51
    * @param ns
    * @return void
    */
    public static void createNameSpace(String ns)  {
        //1. 创建命名空间描述器
        NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor.create(ns).build();

        //2. 创建命名空间
        try {
            admin.createNamespace(namespaceDescriptor);
        } catch (NamespaceExistException e) {
            System.out.println(ns+"命名空间已经存在");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
    * @Description 插入数据
    * @date 2020/9/4 23:01
    * @param tableName
    * @param rowKey
    * @param cn
    * @param value
    * @return void
    */
    public static void putData(String tableName,String rowKey,String cf,String cn,String value) throws IOException {
        //1. 获取表对象
        Table table = conn.getTable(TableName.valueOf(tableName));

        //2. 创建put对象
        Put put = new Put(Bytes.toBytes(rowKey));


        //3. 给put对象赋值
        put.addColumn(Bytes.toBytes(cf), Bytes.toBytes(cn), Bytes.toBytes(value));

        //4. 插入数据
        table.put(put);

        //5. 关闭连接
        table.close();
    }

    /**
    * @Description 获取数据
    * @date 2020/9/4 23:18
    * @param tableName
    * @param rowKey
    * @param cf
    * @param cn
    * @return void
    */
    public static void getData(String tableName, String rowKey, String cf, String cn) throws IOException {
        //1. 获取表对象
        Table table = conn.getTable(TableName.valueOf(tableName));

        //2.  创建Get对象
        Get get = new Get(Bytes.toBytes(rowKey));

        //2.1 指定获取的列族
        //        get.addFamily(Bytes.toBytes(cf));

        //2.2 指定列族和列名
        get.addColumn(Bytes.toBytes(cf), Bytes.toBytes(cn));

        //2.3 指定获取数据的版本数
        get.setMaxVersions(10);

        //3. 获取数据
        Result result = table.get(get);

        //4. 解析result并进行打印
        for (Cell cell : result.rawCells()) {
            //5. 打印数据
            System.out.println("CF:" + Bytes.toString(CellUtil.cloneFamily(cell))+
                               ", CN:" + Bytes.toString(CellUtil.cloneQualifier(cell))+
                               ", Value" + Bytes.toString(CellUtil.cloneValue(cell)));
        }

        //6. 关闭表连接
        table.close();
    }


    /**
    * @Description 扫描全表
    * @date 2020/9/7 17:11
    * @param tableName
    * @return void
    */
    public static void scanTable(String tableName) throws IOException {
        //1. 获取表对象
        Table table = conn.getTable(TableName.valueOf(tableName));

        //2. 构建scan对象
        Scan scan = new Scan(Bytes.toBytes("1001"), Bytes.toBytes("1003"));

        //3. 扫描表
        ResultScanner resultScanner = table.getScanner(scan);

        //4. 解析resultScanner
        for (Result result : resultScanner) {

            //5. 解析result并打印
            for (Cell cell : result.rawCells()) {
                //5. 打印数据
                System.out.println("CF:" + Bytes.toString(CellUtil.cloneFamily(cell)) +
                                   ", CN:" + Bytes.toString(CellUtil.cloneQualifier(cell)) +
                                   ", Value" + Bytes.toString(CellUtil.cloneValue(cell)));
            }
        }

        //6. 关闭表连接
        table.close();
    }

    /**
    * @Description 删除数据
    * @date 2020/9/7 19:18
    * @param tableName
    * @param rowKey
    * @param cf
    * @param cn
    * @return void
    */
    public static void deleteData(String tableName, String rowKey, String cf, String cn) throws IOException {
        //1. 获取表对象
        Table table = conn.getTable(TableName.valueOf(tableName));

        //2. 构建删除对象
        Delete delete = new Delete(Bytes.toBytes(rowKey));

        //2.1 设置删除的列,删除之前的所有版本,addColumn()是删除当前版本
        delete.addColumns(Bytes.toBytes(cf), Bytes.toBytes(cn));
        //2.2 设置删除的列族
        //        delete.addFamily(Bytes.toBytes(cf));


        //3. 执行删除操作
        table.delete(delete);

        //4. 关闭连接
        table.close();
    }






    /**
    * @Description 关闭资源
    * @date 2020/9/4 22:31
    * @return void
    */
    public static void close(){
        if(null != admin){
            try {
                admin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(null != conn){
            try {
                conn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        //1. 测试表是否存在
        System.out.println(isTableExist("stu5"));

        //2. 创建表测试
        createTable("stu5", "info1", "info2");

        //3. 删除表测试
        dropTable("stu5");

        //4. 创建命名空间测试
        createNameSpace("0408");

        //5. 插入数据测试
        putData("stu5", "1001", "info1", "name", "lisi");

        //6. 获取数据
        getData("stu5", "1001", "info1", "name");

        //7. 扫描数据
        scanTable("stu5");

        //8. 删除数据
        deleteData("stu5", "1001", "info1", "name");

        //关闭资源
        close();
    }
}

文章作者: 迷雾总会解
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 迷雾总会解 !
评论
 上一篇
Hive Hive
hive是基于Hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。
下一篇 
Zookeeper Zookeeper
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务
  目录