大数据-HBase
HBase(Hadoop Database)基于Google的BigTable论文,依赖HDFS进行存储。适合存储大体量数据。HBase是高可靠性(数据安全)、高性能(存取效率)、面向列、可扩展的分布式存储系统,实现利用廉价设备搭建大规模集群。
HBase是面向列的存储系统。适用于存放半结构化或者非结构化的数据。
-
结构化:每条记录具备相同的数据结构,类似于类和对象关系
-
非结构化的数据:数据之间不需要保持相同的属性,可以自行定义属性(KV)
-
半结构化的数据:介于两者之间
一个表中使用四个维度定义一个数据
-
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 查询
-
客户端发送请求查询数据到KZ集群
-
KZ根据-root-数据找到.meta.表,meta根据查询请求找到所需要的Region及其在所在的RegionServer,建立与RegionServer的链接
-
Region内部数据有序(基于rowkey)采用二分查找直接找到数据所有的store(store对应列族)
-
最终在列族的子列与时间戳定位到cell
注:为了效率尽量将相近属性的列放在一个列族中便于查找
HBase快速的定位到Region与HRegionServer的方案:
.meta.
存放的都是table与region与regionserver的对应关系;能够切分,降低访问压力
-
rowkey 表,起始rowkey值,时间戳
-
info:regioninfo 起始rowkey值,结束rowkey值,列族
-
info:server 对应的HRegionServer
region | info:regioninfo | info: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优化
表设计优化
-
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个分区
-
代码方式:二维数组方式,具体见代码实现部分
-
-
Rowkey
-
Rowkey是以byte数组存储的部定长字符串,最大长度64K。
-
Rowkey是按字典序排列的,一般将其设置为定长的,将时间戳作为Rowkey的一部分,便于将相邻时间的数据一起查出。
-
优化规则
越短越好(每条记录中都存了rowkey的值,大量冗余)
根据业务需求设置rowkey,以提高查询效率为考虑准则
对于存在访问过于集中的rowkey端,为了避免热点产生,需要对rowkey散列处理:取反方式,hash方式
-
基于Rowkey的查找方式
通过单个rowkey的get查找,直接定位单个rowkey
通过指定rowkey范围的scan扫描,设置startRowKey和endRowKey
全表scan扫描,效率低
-
-
Column Family
一张表中不要设置超过3个列族
原因包括:
1 当一个列族的数据flush时(men写出到storefile时),相邻的列族也会被关联触发,HRS同时执行多个IO操作,导致效率降低
2 执行查找时,rowkey对应过多的列族,导致查找时必须遍历所有列族的storefile,storefile过多查询缓慢
-
Compact & Split (StoreFile/Hfile的合并与切分)
-
合并
-
问题:StoreFile为memony写出的硬盘中的数据,每次写出的StoreFile内部都是有序的,而StoreFile文件之间无顺序。因此在查询数据时需要遍历每个StoreFile,存在效率问题。
-
合并方式
minor compact 相邻若干StoreFile合并,可以指定数量
major compact HR中的所有StoreFile合并,同时处理标记数据(删除的数据,version过期数据,有效期过期数据),这种方式占用资源较大一般需要手动触发
-
效果:
能够获取到有序的大数据量的StoreFile,降低IO压力,数据占用空间压缩。
-
-
切分
-
StoreFile大于阈值后,StoreFile将被切分
-
-
切分和合并的阈值可以手动设置,使得系统中的StoreFile大小均衡且合并切分次数较小
-
-
In Memory
在RS中设置内存共享区域,创建表时将表存入memory中,增加缓存的命中率
-
关于数据失效
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占比
-
-
数据查询过程说明
-
从每个Region的memstore查询数据
-
若memstore无对应数据,查询Blockcache数据
-
若Blockcache无对应数据,查询DFS的数据
-
若从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();
关于扫描查询
-
设置扫描器
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"));
-
-
-
将过滤器设置到扫描器中
scan.setFilter(filter1);
-
执行查询,并遍历获取结果
-
将扫描器设置到表连接中,以获取查询结果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类名
-