大数据-HBase

HBase

HBase(Hadoop Database)基于Google的BigTable论文,依赖HDFS进行存储。适合存储大体量数据。HBase是高可靠性(数据安全)、高性能(存取效率)、面向列、可扩展的分布式存储系统,实现利用廉价设备搭建大规模集群。

HBase是面向列的存储系统。适用于存放半结构化或者非结构化的数据。

  • 结构化:每条记录具备相同的数据结构,类似于类和对象关系

  • 非结构化的数据:数据之间不需要保持相同的属性,可以自行定义属性(KV)

  • 半结构化的数据:介于两者之间

HBase数据结构

一个表中使用四个维度定义一个数据

  • RowKey

    • 一条记录的ID,用来唯一标明对应列族的数据

    • RowKey默认为64K长度,长度可动态分配

    • RowKey默认按照字典序进行排序(例如:10 ,111,12)

  • Column Family

    • 列的集合体,通过列族能够找到对应的子列

    • 一个RowKey一般少于3个列族,提高HRegion的查询效率

    • 建表时必须定义好列族

  • Qualifier

    • 子列/属性,相当于数据的key

    • 隶属于对应的列族,列可以动态添加

    • 查询时,通过rowkey找到列族,通过列族在查找子列

    • 在一个列族内可以维护大量的KV数据存储(基于版本与合并)

  • Timestamp

    • 事件戳 、数据版本

    • 不同列族具备不同的时间版本

    • 时间戳默认为系统的毫秒数

    • hbase能够默认存储若干版本的数据

      • 每条数据都具备对应的时间戳

      • 数据以时间戳为标准降序排列,使得最新的数据优先查出

      • 可自定义version存储版本数目

      • 可以自定义Timestamp的格式,需要注意防止重复版本

    • 原理:DFS不支持数据修改,数据修改实际执行添加操作,导致每个数据都存在若干以时间戳来区分的历史数据版本

    • 触发数据清除:超过版本数(修改次数),超过存储的时间(数据有效期)

  • Cell

    • 四个维度最终定位到的数据

    • 存储在HBase中的数据无类型,均为字节数组,通过偏移量定位

HBase系统架构

  • Client

    • 访问方式:shell,java,MR

    • 向HBase服务器发送命令

      DDL 数据库定义语言——向HMaster发送

      DML 数据库管理语言——向ZK发送

      DQL 数据库查询语言——向ZK发送

    • 保留查询结果作为缓存数据,待以后相同查询时调用

  • Zookeeper

    • Zookeeper是一个集群,监控HMaster状态并控制HMaster的主从切换,任何时候只有一个主HMaster

    • 存放表结构信息,在表创建时就记录下来

      • 避免HMaster故障丢失数据

      • 表结构信息共享给主从HMaster

    • 监听HRegionServer上/下线

    • 保存每个Region的地址

    • Zookeeper用于辅助client执行DML与DQL

      使用表结构与Region地址数据,提高数据访问效率

  • HMaster

    • 监控HRegionServer

      • 负载均衡

      • 表创建

      • Region分裂时,分配不同的HRegionServer

      • HRegionServer故障时,任务切换

    • 处理DDL请求,调度空闲(内存,空间)HRegionServer执行DDL

  • HRegionServer

    • 管理节点中的所有Region

    • 监控HRegion状态与大小

      • 单个HRegion大小达到阈值(10G)后拆分

      • 采用相对等分切分HRegion,标准切分后两侧数据完整

      • 新分出的Region交给HMaster分配给其他HRegionServer

    • 直接处理客户端的IO请求

  • HLog

    • HLog基于write ahead log(提前写入日志)模型

    • 每个HRegionServer都对应一个HRegionServer,内部的HRegion共享这个HLog

    • 检查点:触发执行内存转硬盘(dfs)后,HLog会记录检查点,新存入的数据都在检查点之后。

    • 若HRegion宕机,HRegionServer将硬盘数据交给其他HRS节点管理,新节点执行检查点后的日志,实现故障切换

  • HRegion

    • 一个表对应多HRegion——每个HRegion只对应一个表

      • 默认表创建时只分配一个HRegion

      • HRegion到达10G后自动切分,使得多个HRegion对应一个表

      • 可以在建表时自定义多个HRegion,避免反复切分操作和热点操作

    • HRegion内部是有序的

      • 按照 Rowkey 字典序排列

      • 切分HRegion时,两个HRegion有序,HRegion内部也有序

  • Store

    • 一个HRegion由多个Store构成

    • 一个Store对应一个列族——一个列族可能有多个Store

    • 通过Store的维护能够实现自动维护列族内大量的KV数据

    • 每个Store由 1个MemStore(内存)+n个StoreFile(硬盘文件)构成

    • 数据存储过程为:

      • 数据写入前先写日志,再将数据存入内存

      • 内存占用或日志条数达到阈值后,触发内存写出到硬盘

      • 写出前修改日志中该数据的状态,后将数据转存到硬盘中

      • 每次转存触发都会生成一个StoreFile文件,并在日志中生成检查点

      • 上述的两次日志都是基于HLog实现

    • MemStore 基于内存的数据存储,提高数据的插入速度

    • StoreFile 基于硬盘的数据存储

    • 对于Store中数据的增删改,都默认作为添加数据处理,对原数据只是加了标识,新旧数据都存放在Store中

    • StoreFile的合并

      • 随之MemStore写出增多,StoreFile文件变多,需要进行合并

        • MinorCompaction小合并,10个相邻的StoreFile相互合并

        • MajorCompaction大合并

          • 合并Store中所有StoreFile,并将失效数据剔除

          • 失效数据包括:删除的数据,查过有效期,查过版本的数据

          • 占用大量系统资源,需要手动触发

HBase数据存取流程

DDL 定义表

  • client向HMaster发送任务

  • HMaster

    • 检查创建表的权限,名称,表空间等信息

    • 寻找合适的HRegionServer执行创建操作

  • HRegionServer执行完成后,Zookeeper保存如下内容

    • 表结构信息

    • Region与HRegionServer的对应关系

DQL 查询

  1. 客户端发送请求查询数据到KZ集群

  2. KZ根据-root-数据找到.meta.表,meta根据查询请求找到所需要的Region及其在所在的RegionServer,建立与RegionServer的链接

  3. Region内部数据有序(基于rowkey)采用二分查找直接找到数据所有的store(store对应列族)

  4. 最终在列族的子列与时间戳定位到cell

    注:为了效率尽量将相近属性的列放在一个列族中便于查找

HBase快速的定位到Region与HRegionServer的方案:

.meta.

存放的都是table与region与regionserver的对应关系;能够切分,降低访问压力

  • rowkey 表,起始rowkey值,时间戳

  • info:regioninfo 起始rowkey值,结束rowkey值,列族

  • info:server 对应的HRegionServer

regioninfo:regioninfoinfo:server
student,10,163057 s:10 e:19 family list regionserver111
student,20,163056 s:20 e:29 family list regionserver222
student,30,163054 s:30 e:39 family list regionserver888

-root-

存储.meta.各切片的位置,大小可控,直接存放子ZK中

DML 增删改

数据通过HRegion存入到HFile中作为增加数据,在HFile合并时才对数据进行真实处理

DML-快速插入数据

依据HFile的文件格式将数据存储到HDFS上,直接生成Hlog,将Hlog存到Hbase中进行管理。

直接跳过HRegion的存储操作建立HFile

HFile分为六个部分:

  • Trailer段 其长度恒定,记录下面每个段的偏移量,找到下面各段的位置

  • Data Block 段 保存表中的数据,这部分可以被压缩;

  • Meta Block 段 用户自定义的kv对,可以被压缩。

  • Data Block Index 段 Data Block的索引,每条索引的key是被索引的block的第一条记录的key。

  • Meta Block Index段 Meta Block的索引

  • File Info 段 Hfile的元信息,不被压缩,用户也可以在这一部分添加自己的元信息

HFile使用三级索引结构:首先读取Trailer来获取Data Block Index位置,将Data Block Index读取到内存中,检索DBI的key来找到目标数据的索引,再通过索引找到Data Block中的具体数据。

环境搭建

  • 前提

    • Hadoop环境健康

    • Zookeeper集群正常

  • 上传解压HBase压缩包

    • tar -zxvf hbase-0.98.12.1-hadoop2-bin.tar.gz

    • mv hbase-0.98.12.1-hadoop2 /opt/sxt/

    • mv hbase-0.98.12.1-hadoop2 hbase-0.98

  • 开始修改配置文件

    • 配置RegionServer

      • vim regionservers

        • node1

        • node2

        • node3

    • 配置备用节点

      • backup-masters不要和主节点在一起

        • vim backup-masters

        • node2

    • 配置Hbase环境

      • vim hbase-env.sh

        • exprot JAVA_HOME=/usr/java/jdk1.7.0_67

        • export HBASE_MANAGES_ZK=false

    • 配置Hbase的核心配置文件

      • vim hbase-site.xml

        注:hbase.rootdir的值与hdfs的集群名一致

        hdfs://集群名/hbase

        该路径也就是Hbase中dfs中的存放路径

          <property>
         <name>hbase.rootdir</name>
         <value>hdfs://shsxt/hbase</value>
         </property>
         <property>
         <name>hbase.cluster.distributed</name>
         <value>true</value>
         </property>
         <property>
        <name>hbase.zookeeper.quorum</name>
         <value>node1:2181,node2:2181,node3:2181</value>
        </property>
    • 拷贝hdfs-site.xml到habse的配置目录下

      • cp /opt/sxt/hadoop-2.6.5/etc/hadoop/hdfs-site.xml /opt/sxt/hbase-0.98/conf/

  • 拷贝Hbase到其他主机

    • scp -r /opt/sxt/hbase-0.98/ root@node2:/opt/sxt/

      • scp -r /opt/sxt/hbase-0.98/ root@node3:/opt/sxt/

    • 修改环境变量

      • vim /etc/profile

      • export HBASE_HOME=/opt/sxt/hbase-0.98

        • 将HBASE_HOME/bin载入PATH

    • scp /etc/profile root@node2:/etc/profile

      • scp /etc/profile root@node3:/etc/profile

      • [123] source /etc/profile

  • 启动集群

    • zkServer.sh start

      • start-all.sh

    • [3] yarn-daemon.sh start resourcemanager

  • 启动HBase

    • [1] start-hbase.sh

    • 网络访问

    • 集群关闭

      [1] stop-hbase.sh

      [1] stop-all.sh

      [3] yarn-daemon.sh stop resourcemanager

      [1-3] zkServer.sh stop

  • 拍摄快照

HBase的shell

参考https://cloud.tencent.com/developer/article/1336648

系统相关

help '命令' 查看某个命令的帮助文档

进入系统:hbase shell

退出系统:quit ,exit

HBase中删除需要按住ctrl键

涉及字符串注意用单引号

表的使用:'命名空间:表' 若无命名空间名则默认default

命名空间及表

  • 查看命名空间 list_namespace

  • 创建命名空间 create_namespace 'jay'

  • 删除命名空间 drop_namespace 'sxt'

  • 查看表指定命名空间的表 list_namespace_tables 'shsxt'

  • 创建表

    • create 'jay:java', 'teacher',{NAME => 'room', VERSIONS => 3}

      在jay命名空间下创建java表,列族包括teacher和room,room版本为3

    • create 'grade','clazz', {SPLITS => ['g','n']}

      在默认命名空间下创建grade表,列族clazz,预先基于g,n分隔3个HRegion

  • 查看表状态:desc 'jay:java' jay空间下的java表

  • 禁用 disable 'jay:java' 启用 enable 'jay:java'

  • 删除表 drop 'jay:java' 需要预先禁用表才能删除表

以下默认省略命名空间

  • 扫描

    scan '表' {COLUMNS=> '列族',STARTROW => '起始rowkey',STOPROW => '结束rowkey',LIMIT=>显示几条, VERSIONS=>显示几个版本}

    scan '表', {COLUMNS => ['列族1:子列', '列族2']} 查询出指定的列族和列

    scan '表', 'rowkey', {FILTER => "ValueFilter(=, 'binary:值')"} 查看值为指定值的列

    scan '表', FILTER=>"PrefixFilter('xxx')" 指定表的列族前缀为xxx

    scan '表', FILTER=>"ColumnPrefixFilter('xxx') " 指定表的列前缀为xxx

    scan '表', {COLUMNS => '列族', RAW => true} 将删除数据也显示出来

    scan '表', FILTER=>"ColumnPrefixFilter('yyy') AND ValueFilter(=,'substring:xxx')" 包含xxx字符串的类

    { }与" "可以替换使用

  • 指定查找

    get '表', 'keyrow'

    get '表', 'keyrow', '列族:列'

    get 'person', '0001', {COLUMNS => 'name:firstname', VERSIONS => 3}

    指定查3个版本

    get 'person', '0001', {COLUMN => 'name:first', VERSIONS => 3, TIMERANGE => [1392368783980, 1392380169184]}

    限定在某些时间戳内

增删改

插入数据: put '命名空间:表', 'rowkey', '列族:子列', '值'

删除数据: delete '命名空间:表', 'rowkey', '列族:子列' 可以只写前面一部分来扩大删除范围

清空表 truncate '命名空间:表' 截断不可逆

HBase的表设计

基于rowkey有序性,使得数据的主键key变为有序

基于HBase对列族的维护,在列族内存储大量键值对

通话记录

数据需求:

拉取某个号码某一段事件的所有记录

主叫号码被叫号码通话日期通话时长类型
18001128266(电信) 18081293926(移动) 1568961597025 36 1
18001128266 18081293927 1568962597025 66 2
18001128266 18081293928 1568963597025 88 1
18001128265 18081293929 1568963397025 12 1

存储方案

rowkey:本机号码+通话日期+随机标识位 其余通过列族存储

原理:基于rowkey实现快速定位

微博相互关注

亿级数据x亿级数据

  • rowkey:用户ID

  • 列族:info 用户相关信息

  • 列族:关注人 子列的key为关注人ID,value为null

  • 列族:被关注人 子列的key为被关注人ID,value为null

处理关注和取关:根据ID快速定位用户,级联修改 关注用户的(列族:关注人)、被关注用户的(列族:被关注人)

原理:基于列族的维护,在列族内存储巨量键值对。

微博信息

微博:发送人,文本,多媒体,时间,发送端

  • rowkey 发送人_发送时间

  • 列族: info

  • 列族: 点赞

  • 列族: 转发

  • 列族:多媒体 。。。列族

拆分为多个表,避免列族过多影响查询效率

weibo-people表

  • rowkey:发送人ID_发送毫秒数+标识位

  • 列族:文本info

  • 列族:多媒体info

weibo-点赞表

  • rowkey:发送人ID_发送毫秒数+标识位

  • 列族:点赞人

  • 列族:转发人 key为用户ID ,value为转发文本

原理:创建多个表,将列族分配到不同的表中,表具备相同的keyrow。

HBase优化

表设计优化

  1. Pre-Creating Regions预分区

    • 问题:hbase在创建一个表时默认值创建一个Region分区,待regin到达阈值后切分,导致该表的所有请求都会集中到一个RegionServer中,降低性能。

    • 方案:在创建表时建立多个Region分区,分区按照指定key划分,之后rowkey根据key存入不同分区,实现负载均衡。

    • 系统自动对n个key排序,分配(n+1)个区,第一个区范围为(-∞,最小key],中间(key1,key2],最后一个分区为(最大key,+∞)

    • 命令方式:create '表','列族', {SPLITS => ['g','n']} 按照g和n分为3个分区

    • 代码方式:二维数组方式,具体见代码实现部分

  2. Rowkey

    • Rowkey是以byte数组存储的部定长字符串,最大长度64K。

    • Rowkey是按字典序排列的,一般将其设置为定长的,将时间戳作为Rowkey的一部分,便于将相邻时间的数据一起查出。

    • 优化规则

      越短越好(每条记录中都存了rowkey的值,大量冗余)

      根据业务需求设置rowkey,以提高查询效率为考虑准则

      对于存在访问过于集中的rowkey端,为了避免热点产生,需要对rowkey散列处理:取反方式,hash方式

    • 基于Rowkey的查找方式

      通过单个rowkey的get查找,直接定位单个rowkey

      通过指定rowkey范围的scan扫描,设置startRowKey和endRowKey

      全表scan扫描,效率低

  3. Column Family

    一张表中不要设置超过3个列族

    原因包括:

    1 当一个列族的数据flush时(men写出到storefile时),相邻的列族也会被关联触发,HRS同时执行多个IO操作,导致效率降低

    2 执行查找时,rowkey对应过多的列族,导致查找时必须遍历所有列族的storefile,storefile过多查询缓慢

  4. Compact & Split (StoreFile/Hfile的合并与切分)

    • 合并

      • 问题:StoreFile为memony写出的硬盘中的数据,每次写出的StoreFile内部都是有序的,而StoreFile文件之间无顺序。因此在查询数据时需要遍历每个StoreFile,存在效率问题。

      • 合并方式

        minor compact 相邻若干StoreFile合并,可以指定数量

        major compact HR中的所有StoreFile合并,同时处理标记数据(删除的数据,version过期数据,有效期过期数据),这种方式占用资源较大一般需要手动触发

      • 效果:

        能够获取到有序的大数据量的StoreFile,降低IO压力,数据占用空间压缩。

    • 切分

      • StoreFile大于阈值后,StoreFile将被切分

    • 切分和合并的阈值可以手动设置,使得系统中的StoreFile大小均衡且合并切分次数较小

  5. In Memory

    在RS中设置内存共享区域,创建表时将表存入memory中,增加缓存的命中率

  6. 关于数据失效

    Max Version:设置表的最大版本数

    Time To Live:设置表中数据的有效时间

数据写入优化

  • 打开多个表链接增加写入速度

    为了减小创建链接开销,使用pool技术

  • auto flush 批处理时用一个事务完成所有操作,HTable.setAutoFlush(false)

  • wal Flag 不写日志存储,适用于不重要的数据,提升存储速度牺牲数据安全性

  • writeBuffer 设置客户端刷出缓存,当buffer的数据量超过设定值client将数据flush到服务端,减少服务端重复IO

  • 单线程批量插入数据,HTable.put(List<Put>),通过list批量插入

  • 多线程批量插入数据,HTable线程不安全,需要多个htable连接才能使用多线程

参考:https://www.cnblogs.com/panfeng412/archive/2012/03/08/hbase-performance-tuning-section2.html

数据读取优化

  • 创建多个htable,增加数据的吞吐量,为了减小创建链接开销,使用pool技术,与写入过程类似

  • 单线程批量读取数据,通过list批量读取

  • 多线程批量读取数据,HTable线程不安全,需要多个htable连接才能使用多线程

  • Blockcache

    • 读缓存,设置在每个HRS中,HRS内的所有HR共享

    • Blockcache设置了类似于LRU的算法,将不常用的数据清理出缓存,保证数据不断更新,提高Blockcache的命中率

  • MemStore

    • 每个HR对应一个MemStore与多个FileStore

    • 数据存入HR时,先存入MemStore,在MemStore达到阈值后Flush到FileStore中。

    • 阈值一般设置为:单MemStore占用满128M;全局内存占用超过90%,全局MemStore刷出

    • 数据存入HR中直接加快的数据的写入速度,也增加了(flush前)写入数据的读取速度

  • MemStore与Blockcache分配

    • 在HRS节点中内存存在物理限制,需要分配Blockcache与MemStore的占用比

    • 默认Blockcache * 1+memstore * n < heapsize * 0.8

    • 要求相应速度快,增加Blockcache占比

    • 要求写入速度快,增加MemStore占比

  • 数据查询过程说明

    1. 从每个Region的memstore查询数据

    2. 若memstore无对应数据,查询Blockcache数据

    3. 若Blockcache无对应数据,查询DFS的数据

    4. 若从DFS找到数据,将数据存入Blockcache,并将数据返回Client

HBase代码实现

jdbc实现

将HBase115个jar与测试jar导入

初始化init与关闭

//初始化主要过程
//创建配置文件
Configuration conf=new Configuration();
//设置zookeeper集群及rpc地址(地址在hadoop的core-site.xml的配置中)
conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
//获取数据库连接
HBaseAdmin hBaseAdmin=new HBaseAdmin(conf);
//获取表连接
Htable htable=new Htable(conf,"table_name");
//关闭代码
hBaseAdmin.close();
hTable.close();

关于扫描查询

  1. 设置扫描器

    Scan scan =new Scan();

    • 对于过滤查询,设置过滤器,一般使用行过滤器

      RowFilter filter1=new RowFilter (CompareFilter.CompareOp.比较类型常量,比较对象);(RowFilter实现自Filter,可以使用Filter的多态)

      上述比较对象包括:

      new BinaryComparator("rowkey".getBytes()) 行主键比较对象

      new RegexStringComparator(".*aaa") 正则比较对象(获取结尾为aaa的行)

      new SubstringComparator("xxx") 包含有xxx子串的row的比较对象

      new BinaryPrefixComparator("xxx".getBytes()) 开头为xxx的行的比较对象

    • 对于全表查询,直接设置全表查询范围

      • scan.setStartRow(Bytes.toBytes("start_rowkey"));

      • scan.setStopRow(Bytes.toBytes("stop_rowkey"));

  2. 将过滤器设置到扫描器中

    scan.setFilter(filter1);

  3. 执行查询,并遍历获取结果

    • 将扫描器设置到表连接中,以获取查询结果ResultScanner

    • 通过增强for遍历ResultScanner获取每个ResultRow

    • 遍历ResultRow,得到每一个cell元素

    • 通过CellUtil从cell中解析出值,列,列族,rowkey

    //载入扫描器,执行查询,并获取结果
    ResultScanner rowResults= hTable.getScanner(scan);
    for(Result result: rowResults){
       //从行数据中直接获取cells
    List<Cell> cells=result.listCells();
       for (Cell cell : cells) {
           //CellUtil从cell中解析出:值,列,列族,rowkey
           Bytes.toString(CellUtil.cloneValue(cell));
           Bytes.toString(CellUtil.cloneQualifier(cell));
           Bytes.toString(CellUtil.cloneFamily(cell))
           Bytes.toString(CellUtil.cloneRow(cell));
      }
    }

关于get查询数据

  • 创建get对象

    Get get=new Get("rowkey".getBytes());

  • 指定列族和列

    get.addColumn("family".getBytes(),"qualifier".getBytes());

  • 执行查询

    Result result=hTable.get(get);

  • 通过CellUtil解析result

关于单条插入

  • 创建Put对象,通过rowkey构造

    Put put=new Put("rowkey".getBytes());

  • 置入列族,列,值(一次插入,可以同时置入多个cell)

    put.add("family".getBytes(),"qualifier1".getBytes(),"value1".getBytes());

    put.add("family".getBytes(),"qualifier2".getBytes(),"value2".getBytes());

  • 通过表连接执行插入

    hTable.put(put)

关于批量插入

设置Put对象的list容器,根据插入的数据创若干Put对象,存入list中,将list容器置入表连接

List< Put> puts = new ArrayList();
......
hTable.put(puts);

关于表创建

创建表对象,列族对象,通过数据库连接创建表

//创建表对象
HTableDescriptor htd= new HTableDescriptor(TableName.valueOf("table_name"));
//创建列族对象
HColumnDescriptor c1=new HColumnDescriptor("column_famliy");
HColumnDescriptor c2=new HColumnDescriptor("column_famliy2");
//设置列族属性(最大保留版本数)
c1.setMaxVersion(2);
//将列族对象载入表对象
htd.addFamily(c1);
htd.addFamily(c2);
//通过hbase数据库,执行创建
hBaseAdmin.createTable(htd);

设置预分区表

需要说明的是,若指定n个key,则划分了n+1个分区。由于字典序通过选择省略第一个key(最小值)执行分区

HTableDescriptor htd= new HTableDescriptor(TableName.valueOf("table_name"));
HColumnDescriptor c=new HColumnDescriptor("column_famliy");
htd.addFamily(c);
//设置与分区数组,由于只接受byte数据,需要将分区节点转为字节数组,存入二维数组中。
String[] keys = "11,13,115".split(",");
byte[][] range=new byte[keys.length - 1][];
for(int i;i<keys.length;i++){
   range[i-1]=keys[i].getByte;
}
//将分区信息与表数据存入其中
hBaseAdmin.createTable(htd,range);

HBase与MR

  • 将HBase115个jar与Hadoop121个jar导入

  • 在conf资源文件夹中导入Hadoop4个xml配置

  • 在job类,将MR置入配置对象中载入hbase配置,,配置具体数据在hbase的hbase-site.xml文件中

    conf.set("hbase.zookeeper.quorum", "node1:2181,node2:2181,node3:2181")

HBase作为源数据

  • job类

    • 设置scan对象,将scan结果作为map的输入

    • TableMapReduceUtil对象设置map,(org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil)

      //设置扫描器
      Scan scan = new Scan();
      scan.setStartRow("A".getBytes());
      scan.setStopRow("ZZ".getBytes());
      //设置reduce,参数为:job名,扫描器,map类,map输出key类型,map输出value类型,job对象
      TableMapReduceUtil.initTableMapperJob("job_name",scan,WyMapper.class,Text.class, Text.class, job);
  • map类

    • 继承TableMapper类(org.apache.hadoop.hbase.mapreduce.TableMapper)范型给出输出KV的类型

    • 重写map,内部通过result获取cells(reuslt表示一个rowkey中所取出的数据)

      也可以通过指定列族和列直接获取值

      //继承TableMapper类
      public class MyMapper extends TableMapper<Text, Text> {
          //重写map方法
      	@Override
      	protected void map(ImmutableBytesWritable key, Result value, Mapper<ImmutableBytesWritable, Result, Text, Text>.Context context) throws IOException, InterruptedException {
      		//通过value获取所有Cell,再通过cell取出(rowkey,列族,列,值)
      		List<Cell> list = value.listCells();
      		for (Cell cell : list) {
                  String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
                  String family = Bytes.toString(CellUtil.cloneFamily(cell));
                  String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
                  String value = Bytes.toString(CellUtil.cloneValue(cell));
      			//写出数据
      			context.write(new Text(qualifier), new Text(value));
      		}
      	}
      }
      

HBase作为结果存放

  • Job类中,通过TableMapReduceUtil设置reduce,使得reduce结果输出到hbase中

    TableMapReduceUtil.initTableReducerJob("job_name",MyRedce.class,job)

  • 在Reduce类中

    • reduce继承TableReducer(org.apache.hadoop.hbase.mapreduce.TableReducer),重写reduce方法

    • 通过put对象存入写出数据,再通过上下文写出,key置为null

      //继承TableReducer类,指定reduce输入数据类型以及reduce输出类型
      public class MyReducer extends TableReducer<Text,IntWritable,ImmutableBytesWritable>{
          //重写reduce方法
          @Override
          protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, ImmutableBytesWritable, Mutation>.Context context) throws IOException, InterruptedException {
              //执行普通reduce操作
              int count = 0;
              Iterator<IntWritable> iterator = values.iterator();
              while (iterator.hasNext()) {
      			count += iterator.next().get();
      		}
              //使用put插入数据
              Put put=new Put("Harry".getBytes());
              put.add("info".getBytes(),key.getBytes(),String.valueOf(count).getBytes());
              //通过上下文写出,执行put操作,注意key为null
              context.write(null,put);
          }
      }
      

Protobuf的安装与使用

Protocol Buffers 是一种轻便高效的结构化数据存储格式,用于对hbase 的Hfile进行压缩,压缩过程是将文件的高频词使用特定字符引用替换,减小文件的硬盘占用。

注意:压缩后的数据只能整个文件一次性读取,无法基于Hbase快速定位单个数据在文件中的位置,需要解码遍历文件内容。

安装

  • 解压 tar -zxvf protobuf-2.5.0.tar.gz

  • 添加依赖 yum install gcc-c++ -y

  • 进入解压文件执行配置安装 ./configure --prefix=/opt/sxt/protobuf/

  • 编译安装 make && make install

使用

  • 编写proto文件 xxx.proto

    package com.shsxt.util; 
    
    //创建一个消息对象
    message PhoneRecord 
    { 
    required string		otherphone = 1;
    optional int32		time = 2;
    optional int64		date = 3;
    optional string     type = 4;
    }
    
    message PhoneRecordDay
    {
    repeated PhoneRecord	phoneRecord=1;
    }
    
    message PhoneRecordMonth
    {
    repeated PhoneRecordDay	phoneRecordDay=1;
    }
    
  • 将proto文件编译成java类

    文件存入liunx,执行命令 ./protoc --java_out=/root/ Xxx.proto

    文件名就是编译后的java类名

  • 将编译后的文件加入java项目中

posted @ 2019-10-19 09:51  小布大佬  阅读(170)  评论(0编辑  收藏  举报