HBase浅度学习

48次阅读

共计 49506 个字符,预计需要花费 124 分钟才能阅读完成。

简介

hbase 是大数据 hadoop 的数据库

存储数据

 支持海量数据的存储
hbase 是构建在 hdfs 上的分布式数据库

检索数据

hbase 支持对存储在 hbase 表中的海量数据进行随机的实时的查询服务 
hbase 对其大表中的海量数据构建了层层索引 
    
    

已经有 RDBMS 数据库为什么还需要 hbase 这种 hadoop 数据库?(什么时候需要选择 hbase)

要存储的数据为海量的数据

RDBMS 
    集群性能比较弱,不容易集群节点扩展
    一旦存储的表的数据量较大,导致表的索引文件也变大,影响到后续的读写效率            
    
hbase 
    构建在 hdfs 上分布式数据库,支持节点无限扩展
    hbase 的出现就是 RDBMS 在面对海量数据存储及检索时的一个可替代工具 / 框架

要存储的数据为非结构化的数据

 结构化数据 
    mysql 或 hive 表中的数据结构化的数据 

非结构化的数据 
    每条数据的字段数量不相同 
    图片、视频、文本数据都是非结构化的数据
                
        

hbase 是一种 nosql 数据库(非关系型数据库)

“Not Only SQL”的缩写,不仅仅是 sql , 以 nosql 数据库在记录数据时所使用的数据结构的不同将 nosql 数据库分为四大家族

 列存储数据库家族 -- 代表 hbase  
    表中每列的数据是一个连续的存储单元 
    hive 表或者 mysql 中默认每行的数据是一个连续的存储单元 

文档型数据库家族 -- 代表 MongoDB  
    以文档形式记录数据库中的数据
    爬虫项目中
    
键值对数据库家族 -- 代表 redis
    以 key-value 形式记录数据的数据库 
    redis 是基于内存的 key-value 数据库 
        sparkStreaming/strom/Flink 进行实时分析计算 -》redis-》接入前端进行实时更新展示

图形结构数据库家族 -- 代表 Neo4J 
    以图形形式记录数据 
    

https://baike.baidu.com/item/…

hbase 常见的应用场景

  • 接入在线业务提供内容查询服务(借助 hbase 分布式数据库进行海量用户数据的存储,并依靠其完善的检索机制为海量数据提供随机的实时的查询服务)
  • 微博、论坛、朋友圈等社交数据的存储
    海量数据
    图片、视频、文本数据都是非结构化的数据
  • 各大电商平台的订单数据
    未完成的订单(热数据)– oracle
    已完成的订单(冷数据)– hbase
  • 物流信息存储查询
  • 银行客户交易数据
  • 支付公司的交易数据
  • 移动电信公司的话单记录存储
  • 交通数据存储
  • 医疗数据

  • 大数据分析平台中的数据存储库
    可以用 hbase 作为大数据分析平台中的数据源
    MapReduce、hive、spark 等计算框架可以直接从 hbase 表中读写数据

  • HBase 在滴滴出行的应用场景和最佳实践
    https://blog.51cto.com/xiaogo…

hbase 的特点:

hbase 源自于谷歌三大论文之一的 BigTable     
    GFS  -- hdfs  
    MapReduce  -- MapReduce 
    BigTable -- hbase 
hbase 在 hadoop 生态圈中的地位 
    构建在 hdfs 上分布式数据库,支持海量数据的存储 
    可以 MapReduce、hive、spark 框架进行集成使用  
基于【列存储】的数据库 
    列存储与行存储  
        RDBMS 数据库都是默认行存储  
            每行的数据在底层是一个连续的存储单元 
            在 select 查询时如果只涉及到表中的其中几列数据,则无关列也会被加载读取,大大增加系统的 io 流 
        Hbase 数据库默认是列存储 
            每列的数据在底层是一个连续的存储单元 
            在 select 查询时如果只涉及到表中的其中几列数据,则只有涉及到的列会被加载读取
            因为每列的数据保存在一起,并且每列的数据的数据类型相同,则更容易实现压缩存储  
适合存储非结构化和结构化的数据  
基于 key-value 形式存储的数据库 
    根据 key 来获取对应的 value 值
    rowkey+ 列簇 + 列 + 时间戳 =》value
高可靠、高性能、可伸缩的分布式数据库 
    高可靠、可伸缩:构建在 hdfs 上 
    高性能:对比 RDBMS,针对海量数据的读写是高性能的 
    

行存储与列存储
https://blog.csdn.net/dc_726/…

Hive 和 HBase 的区别?

hive
    面向分析的数据仓库工具(非数据库),使用的是 Hql 语句分析
    查询高延迟
    存储结构化数据
    不能直接接入 web 前端业务使用
    默认行存储,是纯逻辑表
    Hive 本身不存储和计算数据,它完全依赖于 HDFS 和 MapReduce,本质就是将 hql 转化为 mr。hbase
    面向数据存储和检索的数据库
        数据存储 -- 海量数据存储(底层是 hdfs)数据检索 -- 支持海量数据随机的、实时的数据检索(依靠层层索引)查询低延迟
        可以实现随机实时检索大表中的数据
    适合存储结构化和非结构化数据        
    可以接入 web 前端业务使用
    hbase 是列存储,是物理表,不是逻辑表,通过索引方便快速查询更新等操作
    hbase 是一个在 hdfs 上开发的面向列的分布式数据库,本身不支持 sql,但是可以借助其他插件实现 sql 功能。通过 hive 创建与 hbase 表的关联映射 -- 使用 hql 实现离线分析
        通过 Phoenix 插件实现实时分析

    Phoenix 介绍:针对 hbase 开发的第三方插件,目前已贡献给 Apache               
        Phoenix 是构建在 HBase 上的一个 SQL 层
        Phoenix 完全使用 Java 编写,作为 HBase 内嵌的 JDBC 驱动能让我们用标准的 JDBC API 而不是 HBase 客户端 API 来创建表、插入数据和查询数据。Phoenix 查询引擎会将 SQL 查询转换为一个或多个 HBase 扫描,编排执行以生成标准的 JDBC 结果集。JDBC 是一种用于执行 SQL 语句的 Java API               
        版本    Phoenix 2.x/3.x - HBase 0.94.x
                Phoenix 4.x - HBase 0.98.1+    
                

RDBMS 和 HBase 的区别?

** HBase 是分布式架构, 支持服务器在线添加和移除,可以很好的扩展(基于 hdfs)** RDBMS 可以使用 sql 语句,HBase 通常使用 API 来访问 [第三方插件支持 phoenix]
** RDBMS 是基于行存储,HBase 是基于列存储, 可以支持更好的存储和压缩
** RDBMS 适合存储结构化的数据,HBase 适合结构化和非结构化的数据存储
** RDBMS 支持比较好的事务,HBase 不支持事务
** RDBMS 支持多表 Join,HBase 不支持 Join
** 通常 HBase 表的应用场景比较简单, 不适合业务逻辑很复杂的查询环境
** RDBMS 相比较 hbase 有更完善的索引机制
** HBase 通常应用都是单表数据量巨大, 用关系型数据库无法满足的情况        
    
    
    

hbase 架构角色

hbase 分布式数据库是主从架构 
    

master

hbase 的集群的主节点 
管理用户对 hbase 表的增删改查(表整体非表内的数据)管理并分配表的 region 给 regionserver 从节点, 分配时遵循一个【散列原则:同一张表多个分片尽量分配个不同的机器】及【负载均衡原则:不同的机器尽量处理相同数量的分片】监控 regionserver 的运行状态及 regionserver 节点宕机后的容灾处理  
    master 借助 zookeeper 间接监控 regionserver 
    zookeeper 会感知 regionserver 节点的上线和下线 
        regionserver 启动后会在 zookeeper 上注册信息
    但某个 regionserver 节点宕机后 zookeeper 会第一时间感知并及时通知 master,master 会进行容灾处理将此 regionserver 节点上管理的 region 重构在其他 regionserver 节点上
    

regionserver

hbase 的从节点  
管理 master 所分配的 region 
真正响应客户端对表的读写请求的节点

HRegion

table 在行的方向上分隔为多个 Region。Region 是 HBase 中分布式存储和负载均衡的最小单元,即不同的 region 可以分别在不同的 Region Server 上,但同一个 Region 是不会拆分到多个 server 上。

Store

 每个 region 至少一个 store,每个列簇对应一个 store
一个 Store 由一个 memStore 和 0 或者 多个 StoreFile 组成。HBase 以 store 的大小来判断是否需要切分 region

HLog

HLog(WAL log):WAL 意为 write ahead log,用来做灾难恢复使用,HLog 记录数据的所有变更,一旦 region server 宕机,就可以从 log 中进行恢复。每个 Region Server 维护一个 Hlog, 而不是每个 Region 一个。HLog 文件就是一个普通的 Hadoop Sequence File,Sequence File 的 value 是 key 时 HLogKey 对象,其中记录了写入数据的归属信息,除了 table 和 region 名字外,还同时包括 sequence number 和 timestamp,timestamp 是写入时间,sequence number 的起始值为 0,或者是最近一次存入文件系统中的 sequence number。Sequence File 的 value 是 HBase 的 KeyValue 对象,即对应 HFile 中的 KeyValue。

memStore

memStore 是放在内存里的。保存修改的数据即 keyValues。当 memStore 的大小达到一个阀值(默认 128MB)时,memStore 会被 flush 到文 件,即生成一个快照。目前 hbase 会有一个线程来负责 memStore 的 flush 操作。

StoreFile

memStore 内存中的数据写到文件后就是 StoreFile,StoreFile 底层是以 HFile 的格式保存。当 storefile 文件的数量增长到一定阈值后,系统会进行合并(minor、major compaction),在合并过程中会进行版本合并和删除工作(majar),形成更大的 storefile。

hdfs

hbase 表数据最终落地在 hdfs 上 
为 hbase 的表数据提供了一个存储平台 

zookeeper

hbase 强依赖 zookeeper  
    kafka、storm 也是强依赖 zookeeper
zookeeper 基于第三方观察者模式监控 regionserver、master 的运行状态及节点宕机后的容灾处理 
    保证 hbase 集群的高可用性
        hbase 可以为 master 节点配置多个备份 
        当 active master 宕机后 zookeeper 会感知并从备份 master 中选举出一个作为后续的 active master  
zookeeper 持有 hbase 集群的所有的服务器节点信息及所有用户表的元数据信息的位置信息 
    持有 hbase 集群的所有的服务器节点信息 
        hbase 服务启动后 master 及 regionserver 节点会向 zookeeper 上注册自己的节点信息 
            
    持有所有用户表的元数据所在的 meta 表的 region 位置信息!!!hive 表的元数据 -- 存在 RDBMS 数据库 
        hbase 表的元数据 -- 存在在 hbase 上的一张名称为 meta 的系统表中  
        
        hbase 上所有用户表的元数据信息都存在 hbase 上的一个 meta 表中  
        meta 表的数据量通常比较少,所有 meta 表通常只有一个 region
        此 meta 表的 region 会被 master 分配给某个 regionserver 节点管理  
        zookeeper 持有该 meta 表的 region 所在的 regionserver 节点信息(知道 meta 表的 region 在哪个 regionserver 节点上)meta 表存有用户表的哪些元数据信息呢?用户表分成了几个 region 
            每个 region 的 key 的范围信息  
            每个 region 所在 regionserver 节点信息 
            …… 


                

hbase 安装部署

1、安装 hadoop 并启动 hdfs 服务

2、安装并启动 zookeeper 服务

3、上传解压并修改 hbase 配置文件

$ tar zxf /opt/softwares/hbase-1.2.0-cdh5.14.2.tar.gz -C /opt/cdh-5.14.2/
    
1)修改 hbase-env.sh    
    # The java implementation to use.  Java 1.7+ required.
    export JAVA_HOME=/opt/cdh-5.14.2/jdk1.8.0_112
    # Tell HBase whether it should manage it's own instance of Zookeeper or not.
    export HBASE_MANAGES_ZK=false
    
2)修改 hbase-site.xml 

声明 hbase 表相关数据在 hdfs 上的根目录 
     <property>
        <name>hbase.rootdir</name>
        <value>hdfs://192.168.134.101:8020/hbase</value>
      </property>
      
      <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
      </property>
      
      <!-- 声明外置的 zookeeper 集群节点服务器 ip 地址  -->
      <property>
        <name>hbase.zookeeper.quorum</name>
        <value>192.168.134.101</value>
      </property>
3)修改 regionservers 
    声明集群中的哪些服务器作为 hbase 的 regionserver 节点 
    类似于 hadoop 中配置 slaves
    
    192.168.134.101    
    

4、启动 hbase 服务

 在主节点服务器上启动 master 进程 
    $ bin/hbase-daemon.sh start master
在所有的 hbase 的从节点服务器上启动 regionserver 进程 
    $ bin/hbase-daemon.sh start regionserver 
    
或者在 master 节点服务器上执行 
    $ bin/start-hbase.sh 
    

5、hbase 的 web 管理平台

http://192.168.134.101:60010/master-status 

    user table -- 用户表  
    system table -- 系统表 
        hbase:meta    
            hbase 的元数据表 
            存储了所有用户表的 region 的引用 
        hbase:namespace 
            命名空间表  
            

6、启动 hbase 后的 zookeeper 及 hdfs 上的变化

    hdfs 上 
        /hbase/data/
            hbase 的表数据的命名空间库目录 
            自定义的命名空间库目录将出现在该 data 目录下
        /hbase/data/default 
            默认命名空间库目录 
            在 hbase 上建表时如果未指定表的命名空间库则默认创建在 default 下 
        /hbase/data/hbase 
            系统命名空间库目录 
            meta --- meta 表数据目录  
            namespace -- 命名空间库元数据表     
            
    zookeeper 
        $ bin/zkCli.sh 
        ls /hbase                    
            meta-region-server  -- meta 表的位置信息 
            rs -- regionserver 节点的信息 
            backup-masters -- master 节点的备份信息 
            master -- active master 节点信息 
        

hbase 表的存储模型及存储模型中的专业术语

rowkey:行健  
    用来标识 hbase 表中唯一一行数据  
    类似 RDBMS 中的主键  
column family:列簇 / 列族    
    可以将某些列组织到一起形成一个家族 
    列簇为一张表的列增加一层索引
    在 hbase 表创建时可以声明多个列簇,一般应用中列簇的数量不超过 3 个,最好只设置 1 个列簇????在创建表至少要声明一个列簇 , 但是不需要声明列名 
column:列 
    值所属的列  
    hbase 表中不同的行可以有不同的列 
    在 hbase 表中插入具体数据时再指定列名 
    某个列必须要属于某个列簇  
cell:单元格  
    单元格是 hbase 表最小最基本的存储单元  
    单元格是实际值的存储地 
    每个单元格的组成 : rowkey+ 列簇 + 列 + 时间戳 =》value 值  
        时间戳:value 值在插入到单元格那一刻的时间戳 
            时间戳可以用来区分一个单元格中多个历史版本的值 
        版本:hbase 表的单元格中可以存储多个历史版本值  
            一个单元格中默认显示的最新版本的值 
        
在 hbase 表中如何指定唯一的一个单元格 / 唯一的值 
    rowkey+ 列簇 + 列 =》单元格 
    rowkey+ 列簇 + 列 + 时间戳 =》唯一的值 
            
    
    

hbase shell 基本使用

与 hbase 进行交互的几种方式

hbase shell -- 测试 
web ui(hue)-- 测试  
java api / Phoenix -- 生产 

    

$ bin/hbase shell 进入到 hbase 的 shell 交互命令行

退格键出现乱码:

xshell 
    文件 - 属性 - 终端 - 键盘 - 两个都选择 ASCII 127  
secureCRT 
    选项 - 会话选项 - 仿真 - 终端 - 选择 linux 
    选项 - 会话选项 - 映射键 - 两个勾选 
            

help 查看 hbase shell 支持的命令

COMMAND GROUPS:
  Group name: general
  Commands: status, table_help, version, whoami

  Group name: ddl
  Commands: alter, alter_async, alter_status, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, locate_region, show_filters

  Group name: namespace
  Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables

  Group name: dml
  Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve

  Group name: tools
  Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, close_region, compact, compact_mob, compact_rs, flush, major_compact, major_compact_mob, merge_region, move, normalize, normalizer_enabled, normalizer_switch, split, trace, unassign, wal_roll, zk_dump

  Group name: replication
  Commands: add_peer, append_peer_tableCFs, disable_peer, disable_table_replication, enable_peer, enable_table_replication, get_peer_config, list_peer_configs, list_peers, list_replicated_tables, remove_peer, remove_peer_tableCFs, set_peer_tableCFs, show_peer_tableCFs, update_peer_config

  Group name: snapshots
  Commands: clone_snapshot, delete_all_snapshot, delete_snapshot, list_snapshots, restore_snapshot, snapshot

  Group name: configuration
  Commands: update_all_config, update_config

  Group name: quotas
  Commands: list_quotas, set_quota

  Group name: security
  Commands: grant, list_security_capabilities, revoke, user_permission

  Group name: procedures
  Commands: abort_procedure, list_procedures

  Group name: visibility labels
  Commands: add_labels, clear_auths, get_auths, list_labels, set_auths, set_visibility

  Group name: rsgroup
  Commands: add_rsgroup, balance_rsgroup, get_rsgroup, get_server_rsgroup, get_table_rsgroup, list_rsgroups, move_servers_rsgroup, move_tables_rsgroup, remove_rsgroup

创建表命令

create ‘ns1:t1’, {NAME => ‘f1’, VERSIONS => 5}

    在 ns1 命名空间库下创建一个 t1 表
    t1 表有一个名称为 f1 的列簇,并且该列簇下的单元格可最大支持 5 个版本值

create_namespace ‘ns1’ 创建一个命名空间库

list_namespace

list 查看用户表

create ‘t1’, {NAME => ‘f1’}, {NAME => ‘f2’}, {NAME => ‘f3’}

    在默认命名空间库下创建一个 t1 表  
    该表有三个名称为 f1 f2 f3 的列族   
        

describe ‘t1’ 描述一张表

create ‘t2’, {NAME => ‘f1’, VERSIONS => 2}, {NAME => ‘f2’, VERSIONS => 4}, {NAME => ‘f3’}

 在 hbase 表同一张表中不同的列簇可以有不同的属性值 
    因为同一张表下的同一个列簇中的 cell 存在同一个文件中,不同列簇中的 cell 存放在不同文件中

create ‘t1’, ‘f1’, ‘f2’, ‘f3’

 创建一个 t1 表,并且该表的三个列簇都使用默认属性    
如果希望对列簇进行特殊属性设定,需要使用 {NAME => 'f1', VERSIONS => 2}
    

创建一个员工表并插入数据

create 'emp' , 'info1','info2'   

# put 插入 / 更新数据 
# 一次只能插入一个 cell,注意和 sql 的区别,不能 insert 插入一整行数据

hbase(main):037:0> put 'emp', '10003', 'info1:name', 'tom'
0 row(s) in 0.2320 seconds

hbase(main):038:0> put 'emp', '10003', 'info1:age', '25'
0 row(s) in 0.0090 seconds

hbase(main):039:0> put 'emp', '10003', 'info2:school', 'jiaoda'
0 row(s) in 0.0200 seconds

hbase(main):040:0> put 'emp', '10003', 'info2:qq', '1234554321'
0 row(s) in 0.0100 seconds

hbase(main):041:0> put 'emp', '10004', 'info1:name', 'lio'
0 row(s) in 0.0060 seconds

hbase(main):042:0> put 'emp', '10004', 'info1:sex', 'boy'
0 row(s) in 0.0090 seconds

hbase(main):043:0> put 'emp', '10004', 'info1:tall', '175cm'
0 row(s) in 0.0300 seconds

hbase(main):044:0> put 'emp', '10004', 'info2:job1', 'java'
0 row(s) in 0.0140 seconds

hbase(main):045:0> put 'emp', '10004', 'info2:job2', 'bigdata'
0 row(s) in 0.0330 seconds

hbase(main):046:0> put 'emp', '10005', 'info1:name', 'lili'
0 row(s) in 0.0190 seconds

hbase(main):047:0> put 'emp', '10005', 'info2:school', 'ligong'
0 row(s) in 0.0190 seconds

hbase(main):048:0> put 'emp', '10006', 'info1:name', 'mary'
0 row(s) in 0.0510 seconds

hbase(main):049:0> put 'emp', '10006', 'info1:age', '18'
0 row(s) in 0.0440 seconds

hbase(main):050:0> put 'emp', '10006', 'info2:job1', 'UI'

scan 查询数据

scan 'emp'    
scan 'emp' , {COLUMNS => 'info1'}     查看某张表中某个列簇下的数据  
scan 'emp' , {COLUMNS => 'info1:name'}    查看某列数据  
scan 'emp' , {COLUMNS => ['info1:name','info1:age']}     查看多列数据  
scan 'emp' , {COLUMNS => ['info1:name','info1:age']}     查看多列数据  
scan 'emp' , {COLUMNS => 'info1:name',LIMIT => 3}     最前面的 3 条数据 
scan 'emp' , {COLUMNS => 'info1',STARTROW => '10004',STOPROW=>'10006'}    某个 rowkey 范围内的数据  

显示出来的每行是一个 cell


get 查询数据

scan 是一个 范围扫描查询 
get 只能查询某条范围内的数据   
get 'emp','10004'  查询某行数据
get 'emp','10004' ,{COLUMN => 'info1'} 查询某行数据下的某个列簇下的数据
get 'emp','10004' ,{COLUMN => 'info1:name'}  查询某个 cell 中的数据最新版本的值  
get 't1', 'r1', {COLUMN => 'c1', TIMESTAMP => ts1}  查询指定历史版本的值 
get 't1', 'r1', {COLUMN => 'c1',  VERSIONS => 4} 查询指定的 cell 中最新 4 个历史版本值 

delete/deleteall 删除数据

delete– 删除某个 value 值

deleteall– 删除的某行或某个 cell

delete 't1', 'r1', 'c1', ts1  删除指定历史版本的值  
delete 'emp','10004','info2:job1'  不指定时间戳删除的是最新版本的值 
deleteall 'ns1:t1', 'r1'  删除某行数据  
deleteall 't1', 'r1'
deleteall 't1', 'r1', 'c1'  删除某个 cell 单元格  
deleteall 't1', 'r1', 'c1', ts1  测试!!!

演示多版本值:

create 'emp2' ,{NAME => 'info', VERSIONS => 5}
put 'emp2','10005','info:age' ,'18'
put 'emp2','10005','info:age' ,'19'
put 'emp2','10005','info:age' ,'20' 
scan 'emp2',{VERSIONS => 2}   查询所有 cell 最新 2 个版本的值  
delete 'emp2','10005','info:age' 不指定时间戳删除的是最新版本的值  
delete 'emp2','10005','info:age' ,1543999422333  删除指定历史版本的值     

修改表的 hbase shell 命令

alter 't1', NAME => 'f1', VERSIONS => 5  将 t1 表中的 f1 列簇的 VERSIONS 属性值改为 5  
alter 'ns1:t1', NAME => 'f1', METHOD => 'delete'  将 t1 表中的 f1 列簇删除 
alter 't1', NAME => 'f2', METHOD => 'delete' 

truncate ’emp2′ 清空表

disable ’emp1′ 禁用

drop ’emp1′ 删除表

count ’emp’ 统计多少行


hbase 表的读写流程

 十五章 -46 页 架构图     

HBase 读数据流程:– 根据 rowkey 查询 emp 表数据

1、client 先去访问 zookeeper, 从 zookeeper 里面获取 meta 表所在位置信息
        0.96 之前的版本除了 meta 表还有一个 root 表,root 表存储 meta 表位置信息,先通过 zookeeper 获取 root 表位置
        在从 root 表中读取 meta 表位置
        现在直接将 meta 表位置存入 zookeeper 中,减少会话次数
2、client 向 meta 所在 regionserver 发起访问, 读取 meta 表数据,获取 hbase 集群上所有 user 表的元数据
3、根据 meta 表中 emp 表的 region 的位置信息,client 找到了当前需要访问的 emp 表对应的 region 及所在的 regionserver 服务器
4、client 向对应 regionserver 服务器发起读请求
5、regionserver 收到 client 访问请求,先扫描 memstore,在扫描 blockcache,最后再读取 storefile[HDFS] 文件
6、regionserver 把数据响应给 client

HBase 写数据流程:– 根据 rowkey 写入 emp 表

1、client 先去访问 zookeeper, 从 zookeeper 里面获取 meta 表所在位置信息
2、client 向 meta 所在 regionserver 发起访问, 读取 meta 表数据,获取 hbase 集群上所有 user 表的元数据
3、根据 meta 表中 emp 表的 region 的位置信息,client 找到了当前需要访问的 emp 表对应的 region 及所在的 regionserver 服务器
4、client 向对应 regionserver 服务器发起写请求
5、regionserver 收到 client 请求, 并响应,client 先把数据写入 HLog, 防止数据丢失
6、再把数据写入 memstore 内存缓存区, 默认 128M
7、当 Hlog 和 memstore 都写入成功, 则这条数据写入成功    
8、当 memstore 达到阀值 [128M], 会把 memstore 里面的数据 Flush 成 storefile
9、当 [128M]storefile 越来越多, 会触发 compact 合并操作, 把多 storefile 合并成一个大的 storefile
    合并的期间会删除过期的版本或数据,例如更新或 delete 删除的数据并未直接删除,而是打了删除标签直到此时才会真正删除    
10、当单个 storefiles[region] 越来越大, 达到一定阀值(10G 或其他动态阈值)时会触发 split 操作,region 被一分为二被管理    


写流程中的三大机制

flush 机制
compaction 机制 
split 机制  


hbase java api 使用

 增删改查操作  






hbase 表的物理模型

hbase 中将表分成了 1 个或多个 region 进行分布式管理
region 是 hbase 表管理的基本单元   

hbase 为什么将表分成多个 region 进行管理呢?如果表比较大不利于表数据的并行读写操作 
    分而治之
    
一张表如何分成多个 region 的?创建表时进行预分区 
        默认建表时不进行预分区则表只有 1 个 region   
        建表时可以指定一个表的 region 的个数及 region 的 key 的范围
            这个指定的 key 是 rowkey 的前缀
    region 被动 split 分割
        当向某张表的某个 region 中持续写入数据,region 所承载的数据量达到一定的阈值【10G 或小于 10G 的一个动态值】,则会进行被动的 split 分割,1 个 region 分为 2 个 region 
        
表的 region 如何被 master 分配管理的?一个表的多个 region 被 master【散列】分配个 regionserver 集群进行管理
    每个 regionserver 节点可以管理不同表的 region,但是默认情况下每个 regionserver 节点最终管理的 region 的总数量是相同的【负载均衡】region 的内部结构 
    每个 region 是由 1 个 Hlog 及 1 到多个 store 组成 (每个 region 中 store 的数量 = 该表的列簇的数量,每个 store 下存储了某一个列簇下的所有的数据)      
        每个 store 是由 1 个 memstore 及 0 到多个 storefile 组成 
            memstore:写缓存,用来加快 hbase 表的写速度 
                memstore 的阈值是 128M,当 memstore 中缓存的数据量达到 128M 则会 flush 成 storeFile 文件 
            
            storefile
                由 memstore 中 flush 出    
                storefile 文件就是 hbase 表数据的存储文件 
                storefile 文件最终落地到 hdfs 上最终形成 HFile 
                
        Hlog 文件 
            每个 region 中还包含一个 Hlog 文件  
            Hlog 文件是一个预写制日志文件【WAL】写数据时默认情况下数据会先写入到对应 region 的 Hlog 中进行备份
            再将数据写入到 memstore 中 
            当数据成功写入 Hlog+memstore 后则后台判断此次写入数据成功 
            当因为 regionserver 节点宕机导致 memstore 中的数据丢失时可以从 Hlog 进行恢复 
            
            某些场景用可以配置关闭 WAL 预写机制  
            在 1.x 版本中可以配置一个 regionserver 节点对应一个 Hlog 文件(一个 regionserver 节点上所有的 region 共用一个 Hlog 文件)=》可以减少 HLog 文件的寻址次数  


    hbase 表数据在 hdfs 上的存储目录结构 
        
        /hbase/data/default  
            在各个命名空间库下包含有多个以表明命名的目录 
        /hbase/data/default/emp 
            在表目录下包含有 1 个或多个以 region 编号命名的目录(该目录下存储了该表对应 region 下的数据)region 的 name  
                可以在 60010web 平台上查看到  
                region name 的命名规则:表名 +Start Key+ 时间戳 +region 编号   
        /hbase/data/default/emp/8b1ae0ef2208947a238544b4b94ffeaa 
            在 region 编码命名得目录下会出现 1 个或多个以列簇名命名的目录 
        /hbase/data/default/emp/8b1ae0ef2208947a238544b4b94ffeaa/info1 
            在列簇名命名的目录下出现 0 到多个 storefile 文件 
            
            
            

hbase 表的读写流程

 十五章 -46 页 架构图 

组件介绍

1)client  客户端 
    hbase shell -- 交互命令行  
    Hue - web  
    MapReduce  
    hive 
    spark 
    …… 
2)zookeeper  
    监控 master 及 regionserver 的状态,保证 hbase 集群的高可用 
    持有 hbase 集群的节点信息及 meta 表的 region 的位置信息  
3)master  
    负责分配表的 region 给 regionserver  
    负责集群的负载均衡  
    读写过程没有经过 master 节点,所以 master 节点负载率通常比较低 
4)regionserver  
    regionserver 节点通常与 hdfs 的 datanode 节点部署在同一台物理机上 
    regionserver 负责管理表的 region 的节点 
    是真正相应客户端读写请求的节点(响应客户端 IO 请求的节点)5)hadoop 
    hadoop 的 hdfs 为 hbase 提供了一个数据存储平台 
    hdfs 上主要存储了 hbase 的两种文件  
        HFile  
            就是 hbase 的 StoreFile
            表数据的存储文件  
            Hfile 是 hadoop 的二进制格式文件   
        Hlog  
            预写制日志文件  
            Hlog 是 hadoop 的 sequence 格式文件 
            

hbase 表数据的读流程:(根据某个 rowkey 读取 emp 表的数据)

                
1、client 先去访问 zookeeper, 从 zookeeper 里面获取 meta 表的 region 所在的 regionserver 节点信息
                0.96 之前的版本除了 meta 表还有一个 root 表,root 表存储 meta 表位置信息,先通过 zookeeper 获取 root 表位置
    在从 root 表中读取 meta 表位置
    现在直接将 meta 表位置存入 zookeeper 中,减少会话次数
    
2、client 向 meta 表的 region 所在 regionserver 发起读请求, 读取 meta 表数据,获取 hbase 集群上所有用户表的元数据
3、根据 meta 表中 emp 表的 region 的位置信息,client 找到了当前需要访问的 emp 表对应的 region 及所在的 regionserver 服务器
4、client 向对应 regionserver 服务器发起读请求
5、regionserver 收到 client 访问请求,在当前节点上找到 emp 表的 region,并确定目标 store,先扫描 memstore(因为数据此时可能存在 memstore 并未 flush 成 storeFile),在扫描 blockcache(读缓存,近期已经读过的数据会加载到该缓冲区中),最后再读取 storefile[HDFS] 文件,6、regionserver 把数据响应给 client


            

hbase 表数据的写流程:(根据某个 rowkey 向 emp 表写数据)

                    
1、client 先去访问 zookeeper, 从 zookeeper 里面获取 meta 表的 region 所在的 regionserver 节点信息
2、client 向 meta 表的 region 所在 regionserver 发起读请求, 读取 meta 表数据,获取 hbase 集群上所有用户表的元数据
3、根据 meta 表中 emp 表的 region 的位置信息,client 找到了当前需要访问的 emp 表对应的 region 及所在的 regionserver 服务器
4、client 向对应 regionserver 服务器发起写请求   
5、regionserver 收到 client 写请求并响应,默认情况下 regionserver 先把数据写入目前 region 的 HLog 中, 防止数据丢失
6、再把数据写入到目标 store 下的 memstore 内存缓存区,memstore 内存缓存区的默认大小 128M 
    $ vi hbase-common/src/main/resources/hbase-default.xml      
    <name>hbase.hregion.memstore.flush.size</name> => 128M  
    
7、当 Hlog 和 memstore 都写入成功, 则此次数据写入成功    
8、当 memstore 内的数据量达到阀值 [128M],会把 memstore 里面的数据 Flush 成 storefile
9、当持续写入数据,导致 store 下的 storefile 越来越多, 当 store 文件数量或每间隔一段时间则会触发 compact 合并操作,hbase 会把每个 store 下的所有的 storefile 合并成一个大的单一的 storefile
10、当合并后的单个 storefiles 文件越来越大, 达到一定阀值(10G 或其他小于 10G 的动态阈值)时会触发 split 操作,region 被一分为二,并由 master 将新的 region 分配给 regionserver 节点管理 

写数据过程中发送的三大机制 flush、compaction、split

1、flush 机制

 当 memstore 内的数据量达到阀值 [128M],会把 memstore 里面的数据 Flush 成 storefile
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
<description>
Memstore will be flushed to disk if size of the memstore
exceeds this number of bytes.  Value is checked by a thread that runs
every hbase.server.thread.wakefrequency.</description>
</property>
    思考:例如一个 regionserver 节点上同时管理了 100 个 region,平均每个 region 内有 2 个 memstore,则该 regionserver 需要同时维护 100*2=200 个  
        
        假如单个 memstore 内的数据量都没有达到 128M,但是都在 100M 以上,则此时所有的 memstore 内的缓存的数据量将超过  200*100M = 20G  
        
        占用的内容空间是 regionserver -jvm -heap 堆内存空间 
<property>
<name>hbase.regionserver.global.memstore.size</name>
<value></value>
<description>Maximum size of all memstores in a region server before new
    updates are blocked and flushes are forced. Defaults to 40% of heap (0.4).
    Updates are blocked and flushes are forced until size of all memstores
    in a region server hits hbase.regionserver.global.memstore.size.lower.limit.
    The default value in this configuration has been intentionally left emtpy in order to
    honor the old hbase.regionserver.global.memstore.upperLimit property if present.</description>
</property>
        默认值为 regionserver -jvm -heap 堆内存空间的 40%   
        当一个 regionserver 节点上的所有的 memstore 内缓存的数据量超过 regionserver-jvm -heap 堆内存空间的 40% 时,将会阻塞此 regionserver 节点上所有的 region 的更新操作
        
        如何配置 regionserver-jvm -heap 堆内存空间 大小 
            $ vi conf/hbase-env.sh 
            export HBASE_HEAPSIZE=1G 
            正常大小都在  4G-20G 
<property>
<name>hbase.regionserver.global.memstore.size.lower.limit</name>
<value></value>
<description>Maximum size of all memstores in a region server before flushes are forced.
    Defaults to 95% of hbase.regionserver.global.memstore.size (0.95).
    A 100% value for this value causes the minimum possible flushing to occur when updates are
    blocked due to memstore limiting.
    The default value in this configuration has been intentionally left emtpy in order to
    honor the old hbase.regionserver.global.memstore.lowerLimit property if present.</description>
</property>
        默认值为 regionserver -jvm -heap 堆内存空间的 40%*0.95=38%  
        当一个 regionserver 节点上的所有的 memstore 内缓存的数据量超过 regionserver -jvm -heap 堆内存空间的 38% 时,会进行强制的 flush 操作
    

    

2、compaction 机制

    当 flush 到 store 下的 storefile 文件过多过小将影响到 hbase 后续的读效率
    hbase 后启动 compaction 合并机制,最终将每个 store 下的所有的 storeFile 文件合并为一个大的单一的 storeFile 文件 
        
    compaction 机制 分割两个合并过程
        minor compaction -- 小合并  
            系统后台会有一个专用的线程监控每个 store 下的 storeFile 文件的数量
            当数量超过 3 个时会进行小合并 
            小合并只是一个归类合并没有用扫描读取文件内的内容也没有进行排序操作 
            该过程较快并且不会消耗集群资源 
        major compaction -- 大合并 
            所有的 region 每隔 3.5 天~10.5 天之间会进行一次大合并 
            大合并过程中会加载读取一个 store 下所有的 storefile 文件并进行以下操作
                会依据 cell 中的 rowkey 值的字典顺序进行排序 
                会对打上’删除‘标签的 cell 或者过期的 cell 进行彻底的删除  
                    执行的删除 cell 的操作并没有将 cell 立即从 storeFile 文件中删除
                    只是打上一个’删除‘标签,在大合并期间 cell 才会被彻底的删除 
    <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>
    <!-- 跳动系数
    最终 region 大合并间隔时间为 7 *(1-0.5) ~ 7*(1+0.5)        3.5~10.5 天 -->
    <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>
    <!-- 小合并 -->
    <property>
        <name>hbase.hstore.compactionThreshold</name>
        <value>3</value>
        <description>
        If more than this number of HStoreFiles in any one HStore
        (one HStoreFile is written per flush of memstore) then a compaction
        is run to rewrite all HStoreFiles files as one.  Larger numbers
        put off compaction but when it runs, it takes longer to complete.</description>
    </property>
    <property>
        <name>hbase.hstore.blockingStoreFiles</name>
        <value>10</value>
        <description>
        If more than this number of StoreFiles in any one Store
        (one StoreFile is written per flush of MemStore) then updates are
        blocked for this HRegion until a compaction is completed, or
        until hbase.hstore.blockingWaitTime has been exceeded.</description>
    </property>
    <property>
        <name>hbase.hstore.blockingWaitTime</name>
        <value>90000</value>
        <description>
        The time an HRegion will block updates for after hitting the StoreFile
        limit defined by hbase.hstore.blockingStoreFiles.
        After this time has elapsed, the HRegion will stop blocking updates even
        if a compaction has not been completed.</description>
    </property>
    <property>
        <name>hbase.hstore.blockingWaitTime</name>
        <value>90000</value>
        <description>
        The time an HRegion will block updates for after hitting the StoreFile
        limit defined by hbase.hstore.blockingStoreFiles.
        After this time has elapsed, the HRegion will stop blocking updates even
        if a compaction has not been completed.</description>
    </property>
    <property>
        <name>hbase.hstore.compaction.kv.max</name>
        <value>10</value>
        <description>How many KeyValues to read and then write in a batch when flushing
                or compacting.  Do less if big KeyValues and problems with OOME.
                Do more if wide, small rows.</description>
    </property>
            大合并过程会消耗大量的 hbase 集群资源(IO,cpu,内存)大合并可以看做是以短期的集群资源消耗换取以后长期的读效率的提升 

            

3、split 机制

 当大合并后的某个 region 下的某个 store 下的 storefile 文件大小达到一定的阈值,则会引起该 region 的 split 分割 

0.94 版本之前
    是一个定值
    单纯使用 hbase.hregion.max.filesize 控制的是某个 store 下 storeFile 的大小,默认是 10G 时自动 split 分割
    默认配置文件 hbase-common/src/main/resources/hbase-default.xml 
        $ echo $((10737418240/1024/1024/1024))    
    结论:对小表不友好
        数据量较小的表就有可能永远不会触发分裂,容易产生数据热点
    
0.94 版本 -1.2 版本
    由 hbase.hregion.max.filesize 和 hbase.regionserver.region.split.policy 共同控制
    split.policy 的默认值 IncreasingToUpperBoundRegionSplitPolicy 底层是根据一个公式来计算是否要 split
    公式:Min (R*R* hbase.hregion.memstore.flush.size ,“hbase.hregion.max.filesize”) 
        R 为同台 regionserver 上同一个表的 region 的个数
        hbase.hregion.memstore.flush.size 默认值是 128M
        假如有一张表 tableA,其多个 region 分布在 regionserver 集群上:如果在某一个 regionserver 上持有了 tableA 的 1 个 region,则该 regionserver 上的 tableA 的 region 触发分割时的大小 => min(10G,1*1*128M)=128M
        如果在某一个 regionserver 上持有了 tableA 的 2 个 region,则该 regionserver 上的 tableA 的 region 触发分割时的大小 => min(10G,2*2*128M)=512M
        … 3… =>:min(10G,3*3*128M)=1152M
        ……
        ……
        … 9… =>min(10G,9*9*128M= 10.125G)=>10G              
        结论:
            同一个 regionserver 节点上持有某张表的 region 数量达到 9 个时,则该表的 region 分割触发值才开始按照 hbase.hregion.max.filesize 最大值 10G 进行分割
            这种切分策略很好的弥补了 ConstantSizeRegionSplitPolicy 的短板,能够自适应大表和小表
            但是很多小表会在大集群中产生大量小 region,最终表数据在集群中过于分散
                例如节点数量为 10 个,一个表最终达到分割阈值 10G 前将会有产生 90 个 region,前期分割过频繁
            
1.2 版本 
    由 hbase.hregion.max.filesize 和 hbase.regionserver.region.split.policy 共同控制
    split.policy 默认值改为 SteppingSplitPolicy
    控制策略:某台 regionserver 上持有某个表的 region 个数为 1 个时 =》split 切分阈值为 flush size * 2
        其他情况为 hbase.hregion.max.filesize=》10G 
    结论:弥补 IncreasingToUpperBoundRegionSplitPolicy 带来的问题
        小表前期随数据量增加不会过度过多分割
            例如节点数量为 10 个,一个表最终达到分割阈值 10G 前将会有产生 10 个 region
            每台上存在 1 个 region 后,以后分割阈值即按照 10G 执行 

        
region 在分割时:消耗集群资源 
    region 在 split 时会短暂的 offline 下线导致客户端访问不稳定 
    
    
为什么在建表时表的列簇的数量不宜过多???因为一个 region 中 store 的数量等于该表的列簇的数量 
    region 的 split 分割的依据是 region 下的某个 store 下的 storeFile 文件的大小 
    如果一个 region 下有多个 store,其中一个 store 下的 storeFile 文件大小逐渐变大
    而其他的 store 下的数据量比较小
    如果数据量较大的 store 下的文件大小达到阈值则会触发整个 region 的 split 分割 
    数据量少的 store 会被迫分裂在多个 region 中,导致数据过于分散增加 hbase 对该 store 下数据的检索成本 
    
        
        
        

hbase java api

 使用 java 编程对 hbase 进行增删改查操作 

1、配置 maven 工程依赖

1)关闭 eclipse,并合并 repository 目录  
    
2)新建 maven 工程或使用之前的 maven 工程并再 pom.xml 文件中添加以下依赖 
        <!-- hadoop start -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.6.0</version>
        </dependency>
        <!-- hadoop end -->
        <!-- hbase start -->
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!-- hbase end -->
        <!-- hive start -->
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>1.1.0</version>
        </dependency>
3)下载 hadoop 及 hbase 的配置文件并加入到 maven 工程中 

    在 maven 工程中创建 source funder 
        src/main/resources  
    将 hadoop 及 hbase 的配置文件加入到 src/main/resources 下 

            

2、hbase java api 演示

如何通过 hbase java api 创建表、删除表、修改表

http://hbase.apache.org/book…. =》95. Examples

package com.example.hbase.admin;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
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.io.compress.Compression.Algorithm;

public class Example {

  private static final String TABLE_NAME = "MY_TABLE_NAME_TOO";
  private static final String CF_DEFAULT = "DEFAULT_COLUMN_FAMILY";

  public static void createOrOverwrite(Admin admin, HTableDescriptor table) throws IOException {if (admin.tableExists(table.getTableName())) {admin.disableTable(table.getTableName());
      admin.deleteTable(table.getTableName());
    }
    admin.createTable(table);
  }

  public static void createSchemaTables(Configuration config) throws IOException {try (Connection connection = ConnectionFactory.createConnection(config);
         Admin admin = connection.getAdmin()) {HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
      table.addFamily(new HColumnDescriptor(CF_DEFAULT).setCompressionType(Algorithm.NONE));

      System.out.print("Creating table.");
      createOrOverwrite(admin, table);
      System.out.println("Done.");
    }
  }

  public static void modifySchema (Configuration config) throws IOException {try (Connection connection = ConnectionFactory.createConnection(config);
         Admin admin = connection.getAdmin()) {TableName tableName = TableName.valueOf(TABLE_NAME);
      if (!admin.tableExists(tableName)) {System.out.println("Table does not exist.");
        System.exit(-1);
      }

      HTableDescriptor table = admin.getTableDescriptor(tableName);

      // Update existing table
      HColumnDescriptor newColumn = new HColumnDescriptor("NEWCF");
      newColumn.setCompactionCompressionType(Algorithm.GZ);
      newColumn.setMaxVersions(HConstants.ALL_VERSIONS);
      admin.addColumn(tableName, newColumn);

      // Update existing column family
      HColumnDescriptor existingColumn = new HColumnDescriptor(CF_DEFAULT);
      existingColumn.setCompactionCompressionType(Algorithm.GZ);
      existingColumn.setMaxVersions(HConstants.ALL_VERSIONS);
      table.modifyFamily(existingColumn);
      admin.modifyTable(tableName, table);

      // Disable an existing table
      admin.disableTable(tableName);

      // Delete an existing column family
      admin.deleteColumn(tableName, CF_DEFAULT.getBytes("UTF-8"));

      // Delete a table (Need to be disabled first)
      admin.deleteTable(tableName);
    }
  }

  public static void main(String... args) throws IOException {Configuration config = HBaseConfiguration.create();

    //Add any necessary configuration files (hbase-site.xml, core-site.xml)
    config.addResource(new Path(System.getenv("HBASE_CONF_DIR"), "hbase-site.xml"));
    config.addResource(new Path(System.getenv("HADOOP_CONF_DIR"), "core-site.xml"));
    createSchemaTables(config);
    modifySchema(config);
  }
}    

作业:

region 的结构及 region 相关概念  -- 简述
hbase 表数据读写流程及写过程中的三种机制  -- 简述 
hbase java api 的使用  -- 提交  
    

    
    


day3:

hbase 与 MapReduce 集成使用  

hbase 与 hive 集成使用  

hbase 与 sqoop 集成使用    

hbase 与 Hue 集成使用  





hbase 与 MapReduce 集成使用

 利用 MapReduce 并行计算框架对 hbase 表数据进行读写操作         
MapReduce 程序 
    使用 hbase 自带 mr-jar 功能包 lib/hbase-server-1.2.0-cdh5.14.2.jar,实现向 hbase 中批量的导入或统计数据  
    自定义 MapReduce 程序向 hbase 表实现读写操作 
        

        

一、使用使用 hbase 自带 MapReduce 功能 jar 包

lib/hbase-server-1.2.0-cdh5.14.2.jar              
    借助此 jar 包中的 MapReduce 程序可以实现利用 MapReduce 并行计算框架

                                    
        

1、使用自带的 MapReduce 功能 jar 包需要进行环境配置

 配置 jar 包中的 MapReduce 程序在读写 hbase 表时作为一个客户端所需要的 hbase 的 jar 包依赖 
使这些 MapReduce 程序在执行时可以在当前环境中加载读取到 hbasejar 包 

找一个固定的会话窗口执行命令

$ export HBASE_HOME=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2    
$ export HADOOP_HOME=/opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2
$ bin/hbase  mapredcp  -- 返回 MapReduce 程序在读写 hbase 表数据时所需要的 hbase 的 jar 包的本地路径 


$ export HADOOP_CLASSPATH=`bin/hbase  mapredcp`
将 MapReduce 程序读写 hbase 表数据时所需要的 jar 包本地路径加入到 hadoop 的环境变量中。之后 MapReduce 在执行时会自动获取 HADOOP_CLASSPATH 的值并从中获取所需的 hbase jar 包的路径 

$ echo ${HADOOP_CLASSPATH}   验证 
            

2、测试执行 hbase 自带的 MapReduce-jar 包程序

$ /opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2/bin/yarn jar /opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2/lib/hbase-server-1.2.0-cdh5.14.2.jar
An example program must be given as the first argument.
Valid program names are:
CellCounter: Count cells in HBase table. 统计 hbase 表 cell 详情
WALPlayer: Replay WAL files.
completebulkload: Complete a bulk data load.
copytable: Export a table from local cluster to peer cluster. 利用 MapReduce 程序实现拷贝 hbase 表
export: Write table data to HDFS. 利用 MapReduce 程序从 hbase 表中导出数据
exportsnapshot: Export the specific snapshot to a given FileSystem.
import: Import data written by Export. 利用 MapReduce 程序向 hbase 导入数据
importtsv: Import data in TSV format. 利用 MapReduce 程序向 hbase 导入 tsv 格式数据
rowcounter: Count rows in HBase table. 利用 MapReduce 程序统计行

1)利用 hbase 自带的 MapReduce 程序统计 hbase 表的行

2)利用 hbase 自带的 MapReduce 程序实现向 hbase 表中批量导入数据 — 直接导入方式


在 hbase 上创建目标表 
    > create 'student' ,'info'  
    
创建 tsv 测试文件并上传到 hdfs 上 
    $ vi student.tsv    
        10003   tom     boy     20
        10004   lili    girl    19
        10005   lio     boy     21
        10007   litao   boy     18
        10008   mary    girl    20
        
    $ bin/hdfs dfs -put student.tsv /user  
        
执行导入 MapReduce 任务 

    $ /opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2/bin/yarn jar /opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2/lib/hbase-server-1.2.0-cdh5.14.2.jar  \
    importtsv \
    -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:sex,info:age   \
    student \
    hdfs://192.168.134.101:8020/user/student.tsv               





3)使用 bulk load 方式将一个 tsv 格式的文件导入到 hbase 表中

    
直接导入方式:regionserver 响应客户端的写请求 -》regionserver 将数据写入 Hlog-》写入 memstore 中 =》从 memstore 从 flush 成 storeFile 文件 -》compaction 合并
    
    
bulk load 方式导入:先使用 MapReduce 程序将到导入的 tsv 文件直接转化为最终的 storeFile 文件并存储到 hdfs 上的某个目录下 =》将最终的 storeFile 文件写入(移动)到目标表的 store 下
    
    
使用 bulk load 方式导入的好处:bulk load 方式导入可以避开直接导入数据时的 hbase 集群的资源消耗(内存、io、cpu)(避开了数据先写入 hlog-》memstore-》flush 等),将 tsv 文件转化为 storeFile 文件的工作压力转移给 MapReduce 分布式实现,最终 hbase 只需将转化后的 storeFile 文件移动写入到目标表中 


            
            
创建新的目标表 
        > create 'student1','info'


        
过程 1:文件格式转化(将 tsv 文件转化为 storeFile 文件)执行转化文件格式任务     
        -Dimporttsv.bulk.output=/path/for/output  在直接导入方式基础上次参数则当前 MapReduce 任务将会把要导入到 hbase 表中的 tsv 文件转换为 Hfile 文件,而不是直接写入到 hbase 表 
        /path/for/output 为由 MapReduce 转化后的 Hfile 文件在 hdfs 上的存储路径 
        
        yarn jar /opt/modules/hbase-1.2.0-cdh5.14.2/lib/hbase-server-1.2.0-cdh5.14.2.jar importtsv -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:sex,info:age -Dimporttsv.bulk.output=/path/for/output student1 hdfs://centos01:8020/user/student.tsv 
        
    现象:此任务是 MapReduce 任务 
        > scan 'student1'  此时数据还未写入到 hbase 表中  
        /path/for/output/info  --- Hfile 文件  

        

过程 2:将由 MapReduce 转化好的 Hfile 文件写入(移动)到目标 region 的 store 下 

    用 completebulkload 完成 bulkload 上传

    yarn jar /opt/modules/hbase-1.2.0-cdh5.14.2/lib/hbase-server-1.2.0-cdh5.14.2.jar completebulkload /path/for/output student3


    现象:此任务不是 MapReduce 任务, 此过程由 hbase 自行完成  
        > scan 'student1'   数据已经写入 
        /path/for/output/info  --- Hfile 文件已经消失
        /hbase/data/default/student1/1b9a3a4e47cc3859a0909dc096685260/info - 出现新的文件  
        
        

二、自定义 MapReduce 程序完成读写 hbase 表数据

 需求:自定义 MapReduce 程序将 hbase 中的 student 表中的 10004 行到 10007 行之间的 info:name 和 info:age 写入到 hbase 的 user 表中 basic:XM ,basic:NL,rowkey 不变  

    
    
 ReadStudentMapper extends  TableMapper<ImmutableBytesWritable, Put> 
    如果 map 从 hbase 表中读数据则自定义的 Mapper 类需要继承 TableMapper  
    <ImmutableBytesWritable, Put>
        声明的是 map 端输出的 key 和 value 类型  
        ImmutableBytesWritable 是对字节数组的封装类并继承了 WritableComparable 类型

        
        
map(ImmutableBytesWritable key, Result value, Context context)        
    ImmutableBytesWritable key  - 是从目标表中读取到的某行数据的 rowkey 值    
    Result value  -- 是从目标表中读取到的对应行所有 cell 的封装实例 
package com.hadoop.hbase;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class Student2UserMapReduce extends Configured implements Tool {

    // Step 1 : Mapper
    public static class ReadStudentMapper extends  TableMapper<ImmutableBytesWritable, Put> {@SuppressWarnings("deprecation")
        @Override
        protected void map(ImmutableBytesWritable key, Result value,
                Context context) throws IOException, InterruptedException {

            // create put
            Put put = new Put(key.get()); // 直接引用原表中的 rowkey 作为目标表的 rowkey
            // 将 student 表中该行中的 info:name info:age 两列数据过滤数并添加 put 实例中 
            for (Cell cell : value.rawCells()) {
                // get CF:info 过滤出特定列簇因为可能有多个列簇
                if ("info".equals(Bytes.toString(CellUtil.cloneFamily(cell)))) {
                    // add name
                    if ("name".equals(Bytes.toString(CellUtil
                            .cloneQualifier(cell)))) {put.add(Bytes.toBytes("basic"), Bytes.toBytes("XM"),
                                CellUtil.cloneValue(cell));
                    }
                    // add age
                    else if ("age".equals(Bytes.toString(CellUtil
                            .cloneQualifier(cell)))) {put.add(Bytes.toBytes("basic"), Bytes.toBytes("NL"),
                                CellUtil.cloneValue(cell));
                    }
                }
            }

            // context output
            context.write(key, put);
        }
    }

    // Step 2 : Reducer

    public static class WriteUserReducer extends
            TableReducer<ImmutableBytesWritable, Put, NullWritable> {

        @Override
        protected void reduce(ImmutableBytesWritable key, Iterable<Put> values,
                Context context) throws IOException, InterruptedException {for (Put put : values) {context.write(NullWritable.get(), put);
            }

        }

    }

    // Step 3 : Driver
    public int run(String[] args) throws Exception {// 1) Configuration
        Configuration conf = this.getConf();
        // 2) create job
        Job job = Job.getInstance(conf, this.getClass().getSimpleName());
        job.setJarByClass(Student2UserMapReduce.class);

        // 3) set job
        // set scan 设置 map 端的查询条件,Scan scan = new Scan();
        scan.setStartRow(Bytes.toBytes("10004"));
        scan.setStopRow(Bytes.toBytes("10007"));
        scan.addFamily(Bytes.toBytes("info"));  // 只扫描特定的列簇
        // setMapper
        TableMapReduceUtil.initTableMapperJob(
                "student", // input table
                scan, // Scan instance
                ReadStudentMapper.class, // mapper class
                ImmutableBytesWritable.class, // mapper output key
                Put.class, // mapper output value
                job
                );

        // setReducer
        TableMapReduceUtil.initTableReducerJob(
                "user", // output table
                WriteUserReducer.class, // reducer class
                job);

        // set reduce nums
        job.setNumReduceTasks(1); // at least one, adjust as required

        boolean isSuccess = job.waitForCompletion(true);
        if (!isSuccess) {throw new IOException("error with job!");
        }
        return isSuccess ? 0 : 1;

    }

    public static void main(String[] args) throws Exception {Configuration conf = HBaseConfiguration.create();
        int status = ToolRunner.run(//
                conf, //
                new Student2UserMapReduce(), //
                args //
                );
        System.exit(status);

    }

}
 测试执行 
    在 hbase 上提前创建目标表 
         create 'user','basic'
    导出 jar 包并上传到 linux 集群并提交执行
        $ bin/yarn jar jars/s2u.jar  
    检查执行结果
        > scan 'user'   







如何为 hbase 的 master 节点配置备份节点(tar 包安装方式搭建的集群)HMaster HA

防止 master 单节点故障
    虽然 hbase 表数据的读写不经过 master,master 宕机一段时间内集群还可以正常读写,当时还是有不可或缺的作用
如何实现
    Master HA 的实现是借助于 zookeeper 基于观察者模式监控 master 状态
    一旦 active master 节点宕机后 zookeeper 会第一时间感知并从其他的多个备份 master 节点(backup-master)中选举出一个 master 作为后续的 active master 节点 
    

1、搭建 Apache Hadoop 集群并启动

$ sbin/start-dfs.sh              -- 启动 HDFS

2、搭建 zookeeper 集群并启动

$ bin/zkServer.sh start

3、部署 HBase 集群

 先在某台节点上操作,完成后再拷贝给其他节点
    $ vi conf/regionservers  // 添加 regionserver 服务器主机名或 IP
    $ vi conf/backup-masters     // 在 HABASE_HOME/conf 目录下添加 backup-masters 文件,里面定义哪些服务器是备用 master
    $ vi conf/hbase-site.xml   // 向 hbase-site.xml 中添加配置信息 
            <property>  
                <name>hbase.rootdir</name>  
                <value>hdfs://hadoop-senior01.bf.com:8020/hbase</value>
            </property>
            <property>
                <name>hbase.cluster.distributed</name>
                <value>true</value>
            </property>            
            <property>
                <name>hbase.zookeeper.quorum</name>
                <value>blue01.mydomain,blue02.mydomain,blue03.mydomain</value>
            </property>
            <property>
                <name>hbase.master</name>
                <value>hdfs://192.168.134.101:60000</value>
            </property>
            
            <name>hbase.master</name>  => 声明集群中的哪台服务器作为最初的 active master 节点 
 拷贝此台 hbase 安装目录到其他两个节点:$ scp -r hbase-0.98.6-hadoop2/ blue02.mydomain:/opt/modules/
    $ scp -r hbase-0.98.6-hadoop2/ blue03.mydomain:/opt/modules/

4、启动 hbase 服务进程

$ bin/start-hbase.sh  // 在 <name>hbase.master</name> 定义的服务器上执行该命令 

5、观察每个服务器的角色

 启动 hbase 服务后,会发现除 <name>hbase.master</name> 定义的服务器上有 Hmaster 进程外
在 conf/backup-masters 内定义的服务器上也有 master 进程
active  master 默认在 <name>hbase.master</name> 定义的服务器上

6、测试
http://192.168.122.128:60010/master-status

 可以看到:Master 192.168.122.128
    Backup Masters 192.168.122.129

关闭 192.168.122.128 服务器上的 HMaster:$ kill -9 12978
可以看到
    Masters 192.168.122.129

     

hbase 与 hive 集成使用

 目的:利用 hive 的分析功能接口,使用 hql 语句分析 hbase 表中的数据 
    
hbase 本身是不支持 sql 查询语句,仅仅支持简单的 get scan 查询数据 
    聚合查询(max avg min max)、分组、联合、子查询 --- 不支持  
    
如何实现使用 sql 或类 sql 语句分析 hbase 表中数据呢?(sql on hbase)与 hive 进行集成
        在 hive 端创建一个与 hbase 表的关联映射表
        使用 hive sql 语句间接分析 hbase 表中的数据 
        hive sql =》hive-》MapReduce 程序 =》比较慢适合离线分析
    使用 phoniex 插件 
        phoniex 插件是专为 hbase 开发的一款第三方插件,Apache TM   
        通过此插件可以使用标准的 sql 语句分析 hbase 表中的数据 
        sql =》phoniex 插件 =》转化为 Get 或 Scan 查询 =》非常快可以实现实时交互查询

        

============================hbase 与 hive 集成测试 ===================



STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'   
    创建 hive 与 hbase 的管理映射表
    hive 与 hbase 之前的数据映射通信依靠的是 HBaseStorageHandler 类 
    hive-hbase-handler-1.1.0-cdh5.14.2.jar


官方部署使用参考资料:

 官方文档步骤在 hive 的官网上:https://cwiki.apache.org/confluence/display/Hive/Home#Home-UserDocumentation
    

1、hive-env.sh 中声明 HBASE_HOME 路径

hive 需要获取 hbase 的配置文件及 lib 目录下的 jar 包 

export HBASE_HOME=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2

2、修改 hive-site.xml

    声明 zookeeper 的地址,根据自己的集群有几个声明几个;hive 分析的数据在 hbase 里,hive 关联 hbase 需要联系 zookeeper       
<property>
    <name>hbase.zookeeper.quorum</name>
    <value>bigdata01.project.com</value>
</property>

3、测试

 在 hive 端的操作
在 hive 端执行 hql 去映射并分析 hbase 上已经存在的一张表
    

实现:HBase 中已经存储一张 student 信息表
    在 hive 创建一张与 hbase 上的 student 表的关联映射表
    使用 hql 分析 hive 端此关联表中的数据


    

1)创建外部关联映射表

 需求是使用 hql 语句去分析 hbase 中已经有了一张包含数据的表
hive 需要创建一个外部表与之映射
假如仅仅是希望使用 hql 语法分析 hbase 表的数据
    需要创建一个外部表
    在 hive 端删除此关联表时不会影响 hbase 表的数据
    
$ bin/hive --service metastore & 

CREATE EXTERNAL TABLE hive_hbase_student (
id int,
name string,
sex string,
age string
)
STORED BY ‘org.apache.hadoop.hive.hbase.HBaseStorageHandler’
WITH SERDEPROPERTIES (“hbase.columns.mapping” = “:key,info:name,info:sex,info:age”)
TBLPROPERTIES (“hbase.table.name” = “student”);

说明:

 第一个:key 是固定格式,默认为 hbase 的 rowkey,不需要指定列簇;其他字段会与 hive 表的字段顺序一一对应映射;可以只映射 hbase 表中的部分列




2)验证:

select * from hive_hbase_student ;    



总结:

 执行创建关联表时包找不到类异常说明 hive 缺少 hbase 某些 jar 包
    一般创建 hive 与 hbase 表映射表都是外部表
    因为需求通常都是使用 hql 分析 hbase 上已经有数据的表 
    在 hive 端删除映射表时 hbase 中表不会被同步删除;

思考:

 在 hive 端向关联表中中插入数据或 load 加载数据后 hbase 表中数据是否会同步更新?能否通过 hive 端间接向 hbase 中导入 / 插入数据?

测试:

1)向 hive_hbase_student 表中 load 加载数据

$ vi student01.txt // 新建测试数据
20111 tom1 boy 21
20112 lio1 boy 20

load data local inpath '/opt/cdh-5.14.2/hive-1.1.0-cdh5.14.2/student02.txt' into table hive_hbase_student ;  
报错:非本地表不能使用 load 加载数据
    不能使用 load 从本地加载数据到 hive 与 hbase 的映射表中(为非本地表)解决:先创建一个临时表,使用 load 导入本地数据到临时表中
    然后在通过 insert into 语句向关联表中插入数据
    hive 端支持 insert 语句向 hive 与 hbase 的关联表中插入数据 

    

2)向 hive_hbase_student 表中 inster 插入数据

CREATE TABLE temp3(
id int,
name string,
sex string,
age string
)
row format delimited fields terminated by ‘t’;

load data local inpath ‘/opt/cdh-5.14.2/hive-1.1.0-cdh5.14.2/student02.txt’ into table temp3 ;

insert into table hive_hbase_student select * from temp3 ;

验证:

select * from hive_hbase_student ;
scan ‘student’

结论:

 数据插入成功
可以通过 hive 作为接口向 hbase 表中插入数据 
可以通过此途径快速将 hive 表数据插入到 hbase 表  





        
    

hbase 与 sqoop 集成使用

 使用 sqoop 将 RDBMS 中的数据导入到 hbase 表中 
sqoop -》hdfs、hive、hbase   


1、修改 sqoop 的配置文件

#Set path to where bin/hadoop is available
export HADOOP_COMMON_HOME=/opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2

#Set path to where hadoop-*-core.jar is available
export HADOOP_MAPRED_HOME=/opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2

#set the path to where bin/hbase is available
export HBASE_HOME=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2

#Set the path to where bin/hive is available
export HIVE_HOME=/opt/cdh-5.14.2/hive-1.1.0-cdh5.14.2

#Set the path for where zookeper config dir is
export ZOOCFGDIR=/opt/cdh-5.14.2/zookeeper-3.4.5-cdh5.14.2

2、在 mysql 上创建一个测试表(订单表)

> create database my_db ;
> use my_db; 
将 sql 文件上传到 linux 上
> source  /opt/data/so_detail.sql ;

3、执行 sqoop 命令将订单表表数据导入到 hbase 表中

bin/sqoop import --connect jdbc:mysql://centos01:3306/mydb --username root --password 123456 --table so_detail --columns "id, product_id, price" --hbase-table "sqoop" --hbase-create-table --column-family "info" --hbase-row-key "id" --num-mappers 1
HBase arguments:
   --column-family <family>    Sets the target column family for the
                               import
   --hbase-bulkload            Enables HBase bulk loading
   --hbase-create-table        If specified, create missing HBase tables
   --hbase-row-key <col>       Specifies which input column to use as the
                               row key
   --hbase-table <table>       Import to <table> in HBase

Caused by: java.lang.ClassNotFoundException: org.json.JSONObject

 将 java-json.jar 包上传到 sqoop 的 lib 目录下 




hbase 与 Hue 集成使用

 通过 hue 的 web 平台实现对 hbase 表数据的增删改查操作 

    

1、启动 hbase 的 thrift server 服务进程

hue 需要通过 hbase 的 thrift server 进行底层的信息交互 
$ bin/hbase-daemon.sh start thrift 


2、修改 hue 的配置文件

$ vi desktop/conf/hue.ini  

    1241 行 
  # If using Kerberos we assume GSSAPI SASL, not PLAIN.
  hbase_clusters=(Cluster|192.168.134.101:9090)

  # HBase configuration directory, where hbase-site.xml is located.
  hbase_conf_dir=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2/conf    

启动 hue server 

针对表的操作 
    删除表 
    创建表 
    启动或禁用表 
针对表数据的操作 
    查看表数据 
        筛选查看 
        排序查看 
    删除 cell 或添加 cell 
    修改 cell 值 
    删除行或添加行 
    加载数据  



            
    
    








    

hbase 表的设计

表的预分区的设计

    
默认建表时不进行预分区则表的 region 只有 1 个,并且该 region 没有 start 和 end key,后续向该表中写数据时无论 rowkey 如何设定则数据都会写入到该表的唯一的 region 中 , 该 region 持有的数据量达到一定的阈值则会被动 split 分割。此过程中会有另个问题出现
    1、在 region 没有 split 之前只有 regionserver 节点在响应客户得读写请求(数据热点、倾斜产生)数据热点、倾斜 =》某时刻大量的读写操作都集中在 hbase 集群上某 1 台或几台 regionserver 节点上
    2、region 的被动 split 分割会消耗集群的资源,并且会短暂的 offline 下线,导致 region 访问不稳定

如何解决 
    在创建表对表进行预分区操作,将表直接生成多个 region,并且指定每个 region 的 key 的范围   
    
    

创建表预分区的方式 1:

create ‘t11’, ‘info1′, SPLITS => [’10’, ’20’, ’30’, ’40’]

Name    Region Server    Start Key    End Key    Locality    Requests
ns1:t11,,1557480097230.eb7d269860267eb22376c716f6e68864.    centos01:60030        10    0.0    0
ns1:t11,10,1557480097230.268f79be12cd6a1f3307138180ed092e.    centos01:60030    10    20    0.0    0
ns1:t11,20,1557480097230.c1b7ba5c4b5be9f01511d624bfde39fc.    centos01:60030    20    30    0.0    0
ns1:t11,30,1557480097230.c3f164315567ab636f264601edc058f4.    centos01:60030    30    40    0.0    0
ns1:t11,40,1557480097230.2f8719d1b3c38f8ec83eec51921acd5a.    centos01:60030    40        0.0    0

插入查看效果

put ‘ns1:t11’, ‘2502’, ‘f1:age’, ’25’ –> region3

结论:前缀匹配、左闭右开

创建表预分区的方式 2:
$ vi splits.txt
create ‘t12’, ‘info1’, SPLITS_FILE => ‘/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2/splits.txt’

表的 rowkey 的设计

hbase 表数据的读写默认都是基于 rowkey(hbase 的二次索引)hbase 表的数据最终都是以 rowkey 的字典顺序排序存储  
一张 hbase 的表的 rowkey 的设计好坏直接影响到后续对此表的读写效率  


举例说明 
    有一张 t11,建表时已经进行预分区  
    有一批 1 千万条的数据需要插入到 t11 表中   
    1 千万条的数据的 rowkey 设计:rowkey= 时间戳_2 位随机数字 
            1544085846_26  xxx  xx  xx   
            1544085999_33  xxx  xx  xx  
            1544087902_62  xxx  xx  xx  
            ……  
    结果:根据 rowkey 与 region 的 key 的前缀匹配结果数据都会写入到 region02 
        会出现数据热点及被动的 split 分割问题 
        
    如何解决 
        rowkey 设计 
            两位随机数字 (00-50)_时间戳
            散列的分布式的插入到每一个 region 中  
            
            

rowkey 设计原则 
    rowkey 必须唯一  
    rowkey 的长度官方建议 10-100 字节之间,实际使用 rowkey 的长度 不超过 16 字节 
        rowkey 值、列簇、列名存在与每一个 cell 单元格中   
        rowkey 过大的影响
            浪费存储空间 
            将会导致索引数据变大,影响到 hbase 表的读写效率 
    rowkey 设计时要考虑集群在读写数据时避免数据热点产生, 不建议全部使用随机数字
    最重要 rowkey 的设计要考虑实际的业务场景  
    
rowkey 设计时可以选择的思路 
    生成随机数  
        固定长度的随机数字(crc32/md5)通常不会将 rowkey 全部使用随机数,会拼接一些与业务需求相关的数据 

    与业务需求相关的数据 + 固定长度的随机数字 
        例如:日期(业务需求相关的数据)_固定长度的随机数字  
            
        订单数据的 rowkey 设计 
            ……
            2018101012_r4      55.0    4354664        2       
            2018111012_ry      55.0    4354664        2       
            2018111115_uu      55.0    4354664        2   
            2018111709_ii      55.0    4354664        2   
            2018120311_hu      55.0    4354664        2                   
            ……  
            
        可以根据客户提供的日期范围快速查询到自己的订单或消费或话单数据等  
        
            客户要查询自己在 11 月份期间的订单数据
            
                Scan san = new Scan () ; 
                scan.setStaryKey(20181111);
                scan.setStopKey(20181112);
            
    翻转字符串  
        假如业务需要将一组连续的数字作为 rowkey  
            连续的数字的特点 
                低位变化快高位变化慢  
                比如时间戳、日期  
            
            翻转前的 rowkey 
                1544085846  
                1544085999  
                1544087902   
                …… 
                                    
            

电信公司用户话单记录表的 rowkey 设计案例

 表的数据量非常大     
    每天新增话单记录 160 亿条  
        
业务需求:要求客户可以根据自己提供的电话号码及时间范围快速查询自己的话单记录信息 
    如果为所有用户的话单记录表设计 rowkey?思路:每个用户的话单记录存在一个 hbase 的一个大表中  
        个人查询的话单记录信息量比较少(1 个人 1 个月的话单记录 600 条),而 hbase 大表的数据量 160 亿条 *365 天  
            希望每个客户的话单记录在 hbase 大表中连续存储(尽量存储在同一个 region 中)还是散列在多个 region 中存储呢?为了减少 regionserver 节点的响应次数,减少 regionserver 节点检索数据时的会话及检索次数
        需要将用户的数据尽可能连续存储在同一个 region 中
        如果要查询 100 亿条数据,最好是散列存储以便分布式查询  
        
        又因为需求中需要以用户提供的日期范围查询话单记录,rowkey 中需要包含日期数据以便进行前缀匹配查询  
        
    rowkey 最终设计 
        电话号码_日期_随机数字 
            电话号码 =》在前可以保证每个用户的话单记录在 hbase 表中连续存储 
            日期 =》放中间可以满足用户根据提供的日期范围匹配查询  
            随机数字 =》为了避免 rowkey 重复 
            

13600000000_20180519_34 xx xx xx xx
13600000000_20180621_55 xx xx xx xx
13600000000_20180621_30 xx xx xx xx
13600000000_20180621_39 xx xx xx xx
13600000000_20180626_36 xx xx xx xx
13600000000_20180629_34 xx xx xx xx
13600000000_20180711_34 xx xx xx xx
13600000000_20180719_34 xx xx xx xx
13600000000_20180819_34 xx xx xx xx




    例如电话号码为 13600000000 用户要求查询自己在 6 月份的话单记录 
    
    例如电话号码为 13600000000 用户要求查询自己在 6 月 21 号的话单记录 

    
    

hbase 表的属性(列簇的属性)

{
NAME => ‘info1’, 列簇名称
BLOOMFILTER => ‘ROW’, 过滤器类型
VERSIONS => ‘1’, 该列簇下的 cell 最大可保留版本数量
KEEP_DELETED_CELLS => ‘FALSE’, 打上删除标签的 cell 在大合并被彻底删除前可以被访问,FALSE 不可以
DATA_BLOCK_ENCODING => ‘NONE’, 数据块的编码方式
TTL => ‘FOREVER’, 数据存活期,该列簇下的数据的有效期,到期的 cell 和被打上删除标签的 cell 会在大合并期间彻底删除
COMPRESSION => ‘NONE’, 针对该列簇下的 Hfile 文件的压缩格式
MIN_VERSIONS => ‘0’, cell 中最少保留版本数量
BLOCKSIZE => ‘65536’(字节),

Hfile 文件中数据块的默认大小,默认大小是 64kb,不同的业务需求场景需要设置不同的数据块大小以提高读性能减少延迟率 
    对于以 Get 读(某一行内)为主的业务场景 =》适当减小数据块的默认大小
    对于以 Scan 读(大范围读取)为主的业务场景 =》适当增大数据块的默认大小    
请描述下 Hfile 文件中的数据块的相关概念及结构?????hfile 的 block 块




REPLICATION_SCOPE => ‘0’
BLOCKCACHE => ‘true’,

 读缓存  
如果该列簇的 BLOCKCACHE 属性的值为 true,则该列簇下的 storeFile 文件中的数据在被读取后可以进入到该读缓存中,目的是为了加快下次读取同样数据时的速度 

IN_MEMORY => ‘false’,

 blockcache 中的三个分区:single、multi、inmemory
读缓存中的激进内存
表示在 blockcache 读缓存中享有较高的等级 
如果一个列簇的 IN_MEMORY 属性值为 true,则该列簇下的数据会被持久加载到 blockcache 读缓存区中的 In-memory 区中    
desc 'hbase:meta'   meta 元数据表的该属性值默认为 true   

}

COMPRESSION 压缩

 是针对某个表某个列簇下的 Hfile 文件进行的压缩配置 
默认支持的压缩格式有 
    Block Compressors
        none  -- 默认  
        Snappy
        LZO
        LZ4
        GZ
企业中最常使用的压缩格式为 Snappy 压缩 
    在各种压缩格式中 Snappy 压缩的编码和解码的综合效率是最高的,对 cpu 消耗最小的 
配置 Hfile 文件的压缩好处:节省 hdfs 的存储空间
    节省磁盘 io 及网络 io  

如何为 hbase 的表数据文件 Hfile 文件配置 Snappy 压缩

1)因为 hbase 的数据最终是存储在 hadoop 上,所以需要先确保 hadoop 支持压缩

$ bin/hadoop checknative // 检查 Hadoop 支持的压缩
/opt/modules/hadoop-2.5.0-cdh5.3.6/lib/native  //hadoop 的压缩依靠 native 包的支持
    

2)检查当前 hbase 是否可以引用 Hadoop 的压缩格式

$ bin/hbase --config conf/ org.apache.hadoop.util.NativeLibraryChecker
    //http://hbase.apache.org/book.html   搜索 NativeLi 

3)配置 hbase 支持 Snappy 压缩

    配置 hbase 可以引用 hadoop 的 native 库
    

下载 hadoop-snappy-0.0.1-SNAPSHOT.jar 包,上传并放入 $HBASE_HOME/lib 下
$ export HBASE_HOME=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2
$ export HADOOP_HOME=/opt/cdh-5.14.2/hadoop-2.6.0-cdh5.14.2
$ mkdir $HBASE_HOME/lib/native
$ ln -s $HADOOP_HOME/lib/native $HBASE_HOME/lib/native/Linux-amd64-64

 将 Hadoop 的 native 包创建链接到 hbase 的 native 下并命名为 Linux-amd64-64
其实 hbase 的 native 使用的就是 Hadoop 的 native 包

4)重启服务进程

 重启 hadoop 及 hbase 服务进程 


5)使用压缩
create ‘t13’ ,{NAME => ‘f1′, VERSIONS => 5,COMPRESSION=>’SNAPPY’} // 如果报错考虑 Hadoop 进程是否刚才替换 native 后重启过
put ‘t13′,’1001′,’f1:name’,’zhangsan’
flush ‘t13’

BLOCKCACHE 读缓存


总结每个 regionserver 节点需要维护的内存空间 
    memstore  
        写缓存,加快写速度  
        每个 regionserver 节点上需要维护多少个 memstore 空间 =》n 个  
        当个 memstore 的空间的阈值 =》128M  
        整个 regionserver 节点上的总的 memstore 的空间的默认阈值 =》regionserver-jvm-heap 空间的 40% 
                
    blockcache
        读缓存,用来加快读速度 
        每个 regionserver 节点上需要维护多少个 blockcache 空间 =》1 个  
        单个 regionserver 节点上的 blockcache 的缓存空间的阈值 
             <name>hfile.block.cache.size</name> => 40%   
  <property>
    <name>hfile.block.cache.size</name>
    <value>0.4</value>
    <description>Percentage of maximum heap (-Xmx setting) to allocate to block cache
        used by HFile/StoreFile. Default of 0.4 means allocate 40%.
        Set to 0 to disable but it's not recommended; you need at least
        enough cache to hold the storefile indices.</description>
  </property>
            blockcache 空间的最大值为 regionserver-jvm-heap 空间的 40%   
            
    加入 blockcache 读缓存后的读数据流程
        向目标 region 下的 对应的 store 下的 memstore 中扫描数据
        再到 blockcache 中扫描数据  
        最终到 storefile 中读取数据 
        如果该列簇的 BLOCKCACHE 属性值为 true,则读取的数据会被加载到 blockcache  

根据读写要求可以设置 memstore 和 blockcache 的大小,若注重读,可将 blockcache 设大些,若注重写,可将 memstore 设大些,总之 blockcache+memstore 要 <=80%
        
LRU 缓存淘汰机制(近期最少使用算法)为了保证 blockcache 读缓存空间的高效使用 
        blockcache 空间中的 single 区和 multi 区实行 LRU 缓存淘汰机制 
        当整个 blockcache 读缓存空间被占用量达到 85% 时才会开始 LRU 缓存淘汰
            
        
blockcache 读缓存空间的分区分级  
保护各个区安全、相互不受影响
    single 区  
        默认大小为 blockcache_size*25% 
        当某个列簇下的 storeFile 文件中的某些 block 块进行被第一次读取则该块中的 cell 会被加载到 blockcache 的此 single 区中  
        此缓存区域中的数据参与 LRU 缓存淘汰机制
    multi 区 
        默认大小为 blockcache_size*50% 
        当数据近期被多次读取则会被加载到 multi 区中 
        此缓存区域中的数据参与 LRU 缓存淘汰机制
    在整个 blockcache 空间占到 85% 时,对 single、multi 采取 LRU 机制
    in-memory 区 
        默认大小为 blockcache_size*25% 
        当某个列簇下的 IN_MEMORY => 'true',则该列簇下的数据会持久加载到 blockcache 的 in-memory 区 中,并且不会参与 LRU 缓存淘汰 
        可以将一些重要的或读取频率非常高的列簇标记为 true 
        in-memory 区等级最高
        不可以随便将某些列簇此属性标记为 true,否则会影响到 meta 元数据表的读写效率 
    

        

compaction 机制

    当 flush 到 store 下的 storefile 文件过多过小将影响到 hbase 后续的读效率
    hbase 后启动 compaction 合并机制,最终将每个 store 下的所有的 storeFile 文件合并为一个大的单一的 storeFile 文件 
        
    compaction 机制 分割两个合并过程
        minor compaction -- 小合并  
            系统后台会有一个专用的线程监控每个 store 下的 storeFile 文件的数量
            当数量超过 3 个时会进行小合并 
            小合并只是一个归类合并没有用扫描读取文件内的内容也没有进行排序操作 
            该过程较快并且不会消耗集群资源 
        major compaction -- 大合并 
            所有的 region 每隔 3.5 天~10.5 天之间会进行一次大合并 
            大合并过程中会加载读取一个 store 下所有的 storefile 文件并进行以下操作
                会依据 cell 中的 rowkey 值的字典顺序进行排序 
                会对打上’删除‘标签的 cell 或者过期的 cell 进行彻底的删除  
                    执行的删除 cell 的操作并没有将 cell 立即从 storeFile 文件中删除
                    只是打上一个’删除‘标签,在大合并期间 cell 才会被彻底的删除
                    
            大合并过程会消耗大量的 hbase 集群资源(IO,cpu,内存)大合并可以看做是以短期的集群资源消耗换取以后长期的读效率的提升 

    compaction 机制相关的属性配置 
    
        大合并 
            <name>hbase.hregion.majorcompaction</name>  
            <value>604800000</value>
                region 进行大合并的间隔时间为 7 天  
                
            <name>hbase.hregion.majorcompaction.jitter</name>
            <value>0.50</value>                    
                默认值为 0.5,是一个浮动值  
                最终同一个 regionserver 节点上 region 的大合并间隔时间为 
                    7*(1-0.5)天~ 7*(1+0.5)天 
                    3.5 天~10.5 天  
                    
            <name>hbase.hstore.compactionThreshold</name>
            <value>3</value>                        
            

            
                    

hbase 表的管理命令

hbase 的一些表管理的 hbase shell 命令  

flush 操作     
  hbase> flush 'TABLENAME'  将某张表下所有的 region 中的 memstore 中的数据 flush 成 storeFile 文件
  hbase> flush 'REGIONNAME'  将某个 region 中的 memstore 中的数据 flush 成 storeFile 文件
  hbase> flush 'ENCODED_REGIONNAME'  将某个 region 中的 memstore 中的数据 flush 成 storeFile 文件

compaction 操作 
    compact  小合并 
    storefile 个数 >3 时自动合并  
    
    major_compact        大合并        
            Examples:
            Compact all regions in a table:
            hbase> major_compact 't1'
            hbase> major_compact 'ns1:t1'
            Compact an entire region:
            hbase> major_compact 'r1'
            Compact a single column family within a region:
            hbase> major_compact 'r1', 'c1'
            Compact a single column family within a table:
            hbase> major_compact 't1', 'c1'  
        
split 操作 
    

Examples:

split 'tableName'
split 'namespace:tableName'
split 'regionName' # format: 'tableName,startKey,id'
split 'tableName', 'splitKey'
split 'regionName', 'splitKey' 

> split "sqoop"  
    当一个表的 region 下的 store 下的 storeFile 文件大小小于一个 block_size(64kb)时将不能被 split 分割 
    
其他操作

> move 'ENCODED_REGIONNAME'  
> move 'ENCODED_REGIONNAME', 'SERVER_NAME'        
    将一个 region 移动到其他的 regionserver 节点上 
        
    
> balancer  查看当前 hbase 集群的 负载均衡策略是否开启,true 表示开启 
> balance_switch   false   关闭负载均衡 


> close_region 'REGIONNAME'
> close_region 'REGIONNAME', 'SERVER_NAME'
> close_region 'ENCODED_REGIONNAME'
> close_region 'ENCODED_REGIONNAME', 'SERVER_NAME'        
    
                                
……  


什么情况下需要进行 region 的手动管理操作

            
更换 regionserver 节点服务器时  
    先对此台节点上的所有的 region 进行 flush,再使用 move 将 region 移动到其他 regionserver 节点上 
针对写入及读取比较频繁的业务场景  
    写入数据过程中可能会发生大合并及 split 机制,可能会影响到 hbase 集群的稳定性 
        region 的 split 过程会消耗集群资源并且对应的 region 还会短暂的 offline 下线
        大合并过程短时间内会消耗大量的集群资源  
    如何解决
        需要将被动的大合并及 split 机制关闭
        改成定时的用户访问底峰时段进行  
        
    如何关闭大合并及 split 的自动(被动)机制           
        大合并 
            <name>hbase.hregion.majorcompaction</name> =》0  
        split 
             <name>hbase.regionserver.region.split.policy</name> =》DisabledRegionSplitPolicy  
             <name>hbase.hregion.max.filesize</name> =>1000G 
             
    如果人为控制在访问底峰时段定时进行大合并及 split  
        将 hbase shell 命令转化 linux shell 命令  
        再将 linux shell 命令  定义到一个 shell 脚本中 
        在使用 crontab 定时执行 shell 脚本  
        
        
    $ echo "hbase shell 命令" | bin/hbase shell -n     
    $ echo "create't15','info' "  |  bin/hbase shell -n        
        
#!/bin/sh
# HBASE_HOME 
HBASE_HOME=/opt/cdh-5.14.2/hbase-1.2.0-cdh5.14.2 
/bin/echo "hbase shell 命令"     |  ${HBASE_HOME}/bin/hbase shell -n                     

HBase 调优

hbase 场景的调优思路

【针对客户端的查询】

 主要是体现在 hbase java API 中

1)批量写和读

table.put(Put)
table.put(List<Put>) --- 批量的写入 

table.get(Get) 
table.get(List<Get>)  --- 批量的读数据     

2)多线程多并发读写

3)客户端手动缓存查询结果

scan.setCacheBlocks(true); -- 可以将扫描读到的结果数据缓存到 blockcache 中  

4)关闭 table 释放资源

table.close()  
    

【针对服务端 hbase 集群资源及表的设计】

1)合理设置预分区及 Rowkey 设计

 根据实际的需求场景对表的预分区与 rowkey 的设计综合考虑  
    建议 regiond 的数量 =regionserver 节点数量 *2
rowkey 的长度小于 16 字节  
列簇名及列名都尽可能短小 

2)控制自动 splitmajorcompact 改为手动的访问底峰时段进行

 针对写入同时查询比较频繁的在线业务场景        
    

3)合理设置列簇的最大支持版本数和存活期

 在满足业务需求的前提下尽可能设置少的版本数及短的存活期 
    

4)合理设置 BlockCache 缓存策略(setCaching)

    可以将一些重要的或读取频率非常高用户表的某些列簇的 in-memory 属性值标记为 true      
    

5)设置 RegionServer 处理客户端 IO 请求的线程数

    <name>hbase.regionserver.handler.count</name>
    <value>30</value>
30=> 100 左右     
    

6)合理配置 BlockCache 和 memstore 的可以占用的 regionserver-jvm-heap 空间的阈值

    针对写数据为主的业务场景  
        将一个 regionserver 节点上所有的 memstore 可占用的 regionserver-jvm-heap 空间的最大值由 40% =》60% 
        将一个 regionserver 节点上的 blockcache 可占用的 regionserver-jvm-heap 空间的最大值由 40% =》20%
    针对读数据为主的业务场景 
        将一个 regionserver 节点上所有的 memstore 可占用的 regionserver-jvm-heap 空间的最大值由 40% =》20% 
        将一个 regionserver 节点上的 blockcache 可占用的 regionserver-jvm-heap 空间的最大值由 40% =》60%      
        

7)WAL 预写日志机制设置

 可以将数据写入到 Hlog 的机制关闭  
适合写入的数据量较多并且数据运行丢失的业务场景(用户行为日志数据)

8)合理使用 Compression 压缩

 合理使用压缩
    节省 hdfs 的存储空间
    节省网络和磁盘 io

bhase shell =》表属性 {COMPRESSION => 'SNAPPY'}        

        
    
        
        
    
    
    
        
        
        
        
    
        
        
        
        
        
        
        
        









正文完
 0