hbase
一.简介
1.来源: Google的关于大数据的三篇论文之BigTable,一般适合于存放千万级别以上的数据.
2.作用:HBase要完全依赖于HDFS,用于存储数据,HBase基于列
3.使用范围:适合存放半结构化或者非结构化的数据
结构化:
我们定义了一个类(结构体),由类创建的对象都保持着统一的结构
非结构化的数据
没有强制要求对象与对象之间的属性相同
对象可以根据自己的需求定义自己的属性
半结构化的数据
介于结构和非结构之间的数据存储方式
HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的[分布式存储系统],利用HBase技术可在廉价PC Server上搭建起大规模[结构]化[存储]集群。
-
高可靠性:单点
-
高性能:
-
面向列:每个对象可以有自己的列。维护的时候直接找某一行数据的某一列
-
可伸缩性:基于HDFS,所有可以在保证数据安全的前提下完成存储的扩容
-
目标:就是在廉价的PC上搭建出大规模集群
它就是个Oracle(安装,SQL,JDBC,性能调优,底层原理)
二.hbase的数据结构
四个维度定义一个数据分别是:rowkey. Timestamp,Column Family,Qualifer
1.Rowkey
类似于关系型数据库中的主键;用来唯一的标识对应列族的数据;列族一般占用64K的数据长度,但是可以动态分配
RowKey会默认按照字典序进行排序(这也是将来优化的一个重点
2.Timestamp
时间戳或者数据的版本,不同的列族有不同的事件版本;默认version使用当前系统时间的毫秒数;也可以自定义version,但是需要考虑版本的设置策略防止出现出重复的版本;然后不同的数据上有对应的时间戳,数据以时间戳为标准降序排列;新插入的数据就会被优先查询到,根据用户设置的版本数据,保留有效的时间戳
清除数据的两种方式:超过版本数;超过存储时间
3.Column Family
可以把它作为有相同意义的列集合体
通过列族才能找到对应的列
一般项目中很少设计超过3个列族
在创建表的时候必须预先给出列族
4.Qualifier
就属于列族中的一个属性
或者可以把它当做列族中的一个子列
查询的时候,通过rowkey找到列族,通过列族在查找子列
5.cell
这就是我们通过4个维度最终定位的数据
单元格由上面四个维度共同决定
所有数据存储在HBase上都是无数据类型的,统一为字节数组
三.hbase的系统结构
1.Client
访问hbase的入口,有多种连接方式,例如:shell脚本,java,MapReduce等等
向hbase的服务器发送命令,在客户端记录服务器的缓存信息,留待下一次的使用
2.zookeeper
监控集群中HMaster的运行情况,保证集群在任何时候只有一个主Master
当我们创建表的时候,表的结构信息会存放在Zookeeper中
防止放在HMaster上节点故障,丢失数据
让主从master共享数据
更快的访问数据
HMaser管理HRegionServer,zookeeper监听HRegionServer上线或者下线
Zookeeper保存了Region的地址
3.HMaster
接受客户端的请求-DDL
监控RegionServer,了解RegionServer的当前节点状态
接受DDL请求之后,会选择一个空闲(内存,空间)的节点执行表的创建
当发现HRegionServer掉线后,会将其上的region切换到其他的HRegionServer
4.HRegionServer
HRegionServer负责管理当前节点的Region
一个HRegionServer有可能存在多个Region
监控Region的大小,达到阈值(10G),将Region进行相对等分
切分数据的时候,以保证数据完整性为前提
新分的Region交给另外的HRegionServer维护
负责维护与客户端的IO请求
5.HLog(WAL日志)
write ahead log 提前写入日志
HLog隶属于HRegionServer
HRegionServer中的所有HRegion共享这个HLog
当达到阈值的时候,会优先写出日志,然后将mem中的数据写出到Storefile
写出之后再Hlog中记录一个检查点,新增加到内存的时候都在这个日志之后
HLOG最终也会被写出到DFS上
6.HRegion
初始情况下,一个表对应一个Region,随着数据增多,region有可能被切分
Region的预分区:在建表时就提前创建N个Region
这些Region会被分配至不同的HRegionServer
即使表的数据没有达到10G的阈值也会有多个HRegionServer提供服务
一个表对应多个Region,但是一个Region只对应一个表
数据存放到表的时候会按照rowkey的字典序排列
那么切分Region的时候,新的Region也是有序的
7.Store
一个Region由多个Store组成
一个Store对应一个列族
Store由memstore和StoreFile组成
memstore:1
StoreFile:N
当数据存放到数据库的时候,数据应该存放到
内存:快,掉电易失
硬盘:持久化,慢
做任何操作之前,先存放日志
将数据写入到内存中,然后找准时机将数据持久化到硬盘
阈值(内存的占比)(日志的条数)
1.MemStore
基于内存的数据存储
当新增数据的时候先写入到日志,然后写入到内存
这样会提高数据插入速度
2.StoreFile
就属于将MemStore中的数据写出到DFS
随着时间的推移,StoreFile的数量会越来越多
当一个数据被修改的时候,并不会直接操作原来的StoreFile,只是对这个记录做一个标识
当StoreFile达到一定数量的时候,会进行合并
客户端进行数据检索时,优先查找MemStore,然后才去StoreFile
具体的关系如下图:
四.hbase数据存储和读取的流程
1.DDL语句
客户端发送请求到HMaster
HMaster
检查创建表的权限等信息是否足够
寻找合适的HRegionServer
将创建Region的任务下发给HRegionServer
当表创建好之后,在Zookeeper中保存两个信息
表的数据结构
存放Region与HRegionServer的对应关系
2.DQL语句
首先客户端发送请求查询数据
查询数据对应的表 student rowkey=666
查询表对应的Region信息,然后查找666对应的region
查找Region对应的HRegionServer
在Hbase为了快速的定位到Region与HRegionServer
于是专门定义了两张表,他们两个有相同的表结构
.meta. 存放的都是table与region与regionserver的对应关系
meta随着表数量增多,region切分增多会导致
切分:去向不知
不切:regionserver压力过大
rowkey | info:regioninfo | info:server |
---|---|---|
student,10,163057 | s:10 e:19 family list{info ,score} | regionserver111 |
student,20,163056 | s:20 e:29 family list{info ,score} | regionserver222 |
student,30,163054 | s:30 e:39 family list{info ,score} | regionserver888 |
root-
只记录.meta.的位置
root的数据量完全可控,所以root路径是固定的
rowkey | info:regioninfo | info:server |
---|---|---|
.meta.,student,10,163057 | regionserver111 | |
.meta.,sz,10,163057 | regionserver222 | |
.meta.,szzz,10,163057 | regionserver888 |
和Region所在的RegionServer建立连接
因为Region中的数据都是有序的,所以二分查找时间复杂度为log N
为了效率尽量将相近属性的列放在一个列族中
3.DML-插入数据
我们存储的数据最终以HFILE的方式存放在HDFS上
HFile的数据格式
典型的一个三级的索引结构
trailer
dataindex
-
358
-
415
-
507
-
4:40
-
4:62
-
byte[40]-->key(515)
-
byte[62]-->value(555)
-
-
617
-
719
-
...
-
metablock
-
fileinfo
如果我们插入数据的时候可以直接生成日志和对应的数据文件
我们可以直接将日志写入到HLog,文件拷贝到指定的位置
免除了中间生成,合并的过程,效率会提高
五.HBase环境搭建
1.前提
基于Hadoop集群环境下
基于zookeeper集群环境下
2.上传hbase压缩包
tar -zxvf hbase-0.98.12.1-hadoop2-bin.tar.gz
mv hbase-0.98.12.1-hadoop2 /opt/sxt/
3.修改配置文件
3.1配置RegionServer
vim regionservers
node01
node02
node03
3.2配置备用节点
vim backup-masters(这里以node01为主节点,备用节点不能配置在主节点上.即就是不能设置成node01)
node02
3.3配置hbase环境
vim hbase-env.sh
修改java路径
exprot JAVA_HOME=/usr/java/jdk1.7.0_67
不启用hbase内部的zookeeper
export HBASE_MANAGES_ZK=false
3.4配置hbase核心文件
vim hbase-site.xml
<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>node01:2181,node02:2181,node03:2181</value>
</property>
3.5拷贝hdfs-site.xml到habse的配置目录下
cp /opt/sxt/hadoop-2.6.5/etc/hadoop/hdfs-site.xml /opt/sxt/hbase-0.98/conf/
3.6将配置好的hbase赋值到另外的两台主机上
scp -r /opt/sxt/hbase-0.98/ root@node02:/opt/sxt/
scp -r /opt/sxt/hbase-0.98/ root@node03:/opt/sxt/
3.7修改环境变量
vim /etc/profile
scp /etc/profile root@node02:/etc/profile
scp /etc/profile root@node03:/etc/profile
在三台主机上同事重新加载文件
[123] source /etc/profile
3.8启动集群
zkServer.sh start
start-all.sh
[3] yarn-daemon.sh start resourcemanager
[1] start-hbase.sh
3.9访问网址查看
六.搭建hbase集群常见的问题
1.问题1
只有两个节点
解决方案:查看时间是否不同步
同步三台主机时间
ntpdate cn.ntp.org.cn
2.问题2
通过hostname设置为regionserver端链接时指定的hostname即可 , 这个就是域名和主机不一致导致的, (hbase分布式部署问题之org.apache.hadoop.hbase.regionserver.HRegionServer: Unable to connect to master)
3.问题3
hbase 未成功启动 1、关闭防火墙:service iptables stop
4.重大问题
问题点
异常数量超过了阈值,然后组长说他那边的网络有点问题,让人看看是什么原因,连上VPN后,发现是HBase的Table对象找不到,无法连接Hbase集群。
分析
Zookeeper的Leader选举一般持续多长时间?
Hbase整个集群挂掉了,如何恢复?
ZK自测
我自己在本机测试,ZooKeeper的Leader重新选举时间很短,但是如果有数据的话,可能要把数据同步到其它的节点时间会比较久。但是如果时间太长,那就是设计之初考虑的较少导致的。
另外在Zookeeper集群由可用变为不可用状态的时候,客户端与ZK服务器的连接也会断掉,并且不断的连接重试。
如果断开连接之后重连耗时太长,超过了会话过期时间,服务器任务会话已经过期,就会进行会话清理。会话过期之后,即使重新连接成功,服务器还是会告知客户端会话过期,请重新连接。然后客户端需要重新恢复临时数据。
但是如果是ZK服务器集群不可用导致的无法重连,那么….客户端还是会不断的重试,直到重连成功。
真实情况
ZK集群因网络抖动导致
公司内部的问题排查记录,遇到问题的时候,先说问题与影响。
问题:集群内通信断开,导致服务器重新Leader选举,选举耗时21672ms
影响:整个Hbase集群的RegionServer及YARN集群挂掉
虽然Leader选举只花费了30s,但是从集群延迟变大直到ZK集群正常工作,期间5分钟内,集群内通信断断续续,可根据日志查看。
HBase恢复
早上6:35分钟,张钧打电话说Hbase有问题,发现RegionServer下线,然后查看HDFS情况正常,首先启动Hbase集群,然后排查具体问题。
原因:Replication模块复制点位信息存储在ZK中,因网络抖动导致的ZK不可用,RegionServer连续4次访问ZK都无法连接,每次sleep 1秒钟,从而RegionServer下线,影响多台RegionServer服务器。
解决方案:6:48分启动Hbase,region打开耗费了大约50分钟,影响持续到7:30分
反思:
Hbase宕机不是负责Hbase的人第一时间发现的,是业务通知的,没有收到报警是因为报警关闭了,这个属于工作的重大失误
Region打开十分耗时,实际上很多Region没有打开的紧急性,比如非当月表的Region,访问需求低。
今后采取措施:
报警已经加上,对于报警关闭的事情应该加以限制,从系统上优化这个事情,不应该关闭
Region优先级的调整,及一些框架上的优化
七.hbaseShell
1.启动命令
hbase shell
2.常见的一些命令
2.1命名空间级别
列出所有命名空间
list_namespace
新建命名空间
create_namespace 'shsxt'
删除命名空间
drop_namespace 'ns1'
2.2表级别
列出所有表
list
新建一个以命名空间shsxt的表student,列族为cf1。
hbase> create 'shsxt:student', 'cf1'
列出指定命名空间shsxt下的所有表
hbase> list_namespace_tables 'shsxt'
删除表
disable 'shsxt:teacher'
创建表 命令格式: create ‘表名称’, ‘列族名称1’,‘列族名称2’,‘列名称N’
创建一张名为Student的表,包含基本信息(baseInfo)、学校信息(schoolInfo)两个列族
create 'Student','baseInfo','schoolInfo' 查看表的基本信息
命令格式:desc ‘表名’
describe 'Student' 表的启用/禁用
enable和disable可以启用/禁用这个表,is_enabled和is_disabled来检查表是否被禁用
禁用表
disable 'Student'
检查表是否被禁用
is_disabled 'Student'
启用表
enable 'Student'
检查表是否被启用
is_enabled 'Student'
检查表是否存在
exists 'Student'
删除表
删除表前需要先禁用表
disable 'Student'
删除表
drop 'Student'
2.3增删改
添加列族
命令格式: alter ‘表名’, ‘列族名’
alter 'shsxt:student', 'teaInfo'
删除列族
命令格式:alter ‘表名’, {NAME => ‘列族名’, METHOD => ‘delete’}
alter 'shsxt:student', {NAME => 'Info', METHOD => 'delete'}
插入数据
命令格式:put ‘表名’, ‘行键’,‘列族:列’,‘值’
put 'shsxt:student', 'rowkey1' , 'teaInfo:name','tom'
获取指定行中所有列的数据信息
get 'shsxt:student', 'rowkey1'
获取指定行中指定列的数据信息
get 'shsxt:student' , 'rowkey1', 'teaInfo:name'
删除指定行中指定列的数据
delete 'shsxt:student', 'rowkey1', 'stuInfo:name'
删除指定行
Hadoop--hbase之二
一.hbase命令练习之二
1.表
查看当前的所有表
-
list
查看"shsxt"下的所有的表
-
list_namespace_tables 'shsxt'
在"shsxt"下创建java表
-
create 'shsxt:java', 'teacher',{NAME => 'room', VERSIONS => 3}
-
create 'grade','clazz', {SPLITS => ['g','n']} //建立预先分区
禁用 'shsxt:java'
-
disable 'shsxt:java'
查看是否禁用 'shsxt:java'
-
is_disabled 'shsxt:java'
查看'student'表是否存在
-
exists 'student'
删除表'student'
-
drop'shsxt:java'
查看'grade'表的所有信息
-
desc 'grade'
2.数据
创建 'user',以及列族'info','role'
-
create 'user','info','role'
往'user'插入数据
-
put 'user','u01','info:uname','admin'
-
put 'user','u01','info:password','123456'
-
put 'user','u01','role:sys','add'
-
put 'user','u02','info:uname','admin2'
-
put 'user','u03','info:uname','admin3'
-
put 'user','u011','info:uname','admin11'
查看 'user'表
-
scan 'user'
查看'user'表中列族'info'
-
scan 'user', {COLUMN=>'info'}
-
scan 'user', {COLUMN=>'info:uname'}
查看'user'表中列族'info'的详细信息
-
scan 'user', {COLUMNS=> 'info',STARTROW => 'u01',STOPROW => 'u02',LIMIT=>3, VERSIONS=>1}
-
scan 'user', FILTER=>"ValueFilter(=,'binary:admin')"
-
scan 'user', FILTER=>"ValueFilter(=,'substring:admin')"
-
scan 'user', FILTER=>"ColumnPrefixFilter('un')",FILTER=>"ValueFilter(=,'substring:admin1')"
-
scan 'user', FILTER=>"ColumnPrefixFilter('un') AND ValueFilter(=,'substring:admin1')"
-
scan 'user', FILTER=>"PrefixFilter('u01')"
-
scan 'user', {COLUMNS => 'info', RAW => true}
获取 'user','u01'中的数据
-
get 'user','u01'
获取 'user','u01''info'中的数据
-
get 'user','u01','info'
获取 'user','u01''info'中的详细数据
-
get 'user','u01','info:password'
删除 'user','u01'中的数据
-
delete 'user','u01'
删除'user','u01''info'中的数据
-
delete 'user','u01','info'
删除 'user','u01''info'中的详细数据
-
delete 'user','u01','info:uname'
truncate 'user'
//创建表查看版本信息
-
create 'student',{NAME => 'info', VERSIONS => 3},'score'
-
put 'student','s01','info:name','zs1'
-
put 'student','s01','info:name','zs2'
-
put 'student','s01','info:name','zs3'
-
put 'student','s01','info:name','zs4'
-
get 'student','s01'
-
get 'student', 's01', {COLUMN => 'info', VERSIONS => 3}
二.eclipse连接hbase
1.准备
解压 hbase-0.98.12.1-hadoop2-bin.tar压缩包
获取其中lib下的115个架包
自定义UserLibrary并导入架包
2.连接hbase
创建项目
创建HelloHBasejdbc类
获取配置文件信息
//客户端连接对象
private HBaseAdmin hBaseAdmin;
//表链接对象
private HTable hTable;
//声明表的名字
private final static String SXT_TABLE_NAME = "school";
创建表
//@Test
public void sxtCreateTable() throws IOException{
//获取表
HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(SXT_TABLE_NAME));
//创建列族
HColumnDescriptor infoColumn = new HColumnDescriptor("info");
//设置保存3个版本
infoColumn.setMaxVersions(3);
HColumnDescriptor historyColumn = new HColumnDescriptor("history");
//将列族添加到表对象
hTableDescriptor.addFamily(infoColumn);
hTableDescriptor.addFamily(historyColumn);
//创建表对象
hBaseAdmin.createTable(hTableDescriptor);
}
插入数据
/**
* 插入数据
* @throws Exception
*/
//@Test
public void sxtPut() throws Exception{
//创建数据对象
Put put = new Put("sc01".getBytes());
//设置本次插入的列族,列,值
put.add("info".getBytes(), "name".getBytes(), "上海市中学".getBytes());
put.add("info".getBytes(), "studentNum".getBytes(), "888".getBytes());
put.add("info".getBytes(), "createTime".getBytes(), String.valueOf(new Date().getTime()).getBytes());
//执行插入操作
hTable.put(put);
}
批量插入数据
/**
* 批量插入数据
* @throws InterruptedIOException
* @throws RetriesExhaustedWithDeRtailsException
*/
获取数据
/**
* 获取数据
* @throws IOException
*/
关闭连接
三.表结构的设计
1.通话记录
从1年的通话记录(360亿条)中查询到该号码的指定时间段的通话记录
-
涉及到的参数:
-
主叫号码,被叫号码,通话日期,通话时长,呼叫类型
-
主叫号码 被叫号码 通话日期 通话时长 类型 18001128266(电信) 18081293926(移动) 1568961597025 36 1 18001128266 18081293927 1568962597025 66 2 18001128266 18081293928 1568963597025 88 1 18001128265 18081293929 1568963397025 12 1
-
-
解决方案1
-
Mysql
-
4张表 (,通话记录表)
-
电信号码表
-
移动的通话记录
-
ID自增序列,时间,UUID
-
-
联通的通话记录
-
电信的通话记录
-
-
-
解决方案2
-
通话时长(不具有唯一性)
-
主叫号码_被叫号码 rowkey 18001128266:18081293927
-
以一小时为单位存放所有(全国)通话记录 rowkey
-
列族:yd lt dx
-
属性: 被叫号码:主叫号码
-
18081293999_ts :18001128266
-
18081293999_ts :18001128266
-
-
-
以一小时为单位存放所有(全国)通话记录 rowkey
-
列族:主叫号码(上亿个列族)
-
属性: 时间毫秒
-
1568962597025:
-
-
-
主叫号码前7位 rowkey 1800112
-
列族:主叫号码 (上万个列族)
-
-
手机号码为rowkey 18001128266
-
列族:yd dx lt
-
属性:
-
通话日期+被叫号码:通话时长+类型
-
2019年3月5号七点42分36秒456毫秒+18081293999:
-
-
-
-
-
解决方案3
-
手机号码+通话时间(毫秒)+(4位随机) rowkey
-
180001128266_1568962597025
-
180001128266_1568962590000
-
180001128266_1568962599999
-
-
2.权限管理
职位:
体育委员
生活委员
学习委员
人物:
小红
翠花
小强
类似于为微博账户3.3亿
ID | 职位名称 |
---|---|
zw1 | 体育委员 |
zw2 | 生活委员 |
zw3 | 学习委员 |
人物ID | 人物姓名 |
---|---|
p1 | 小红 |
p2 | 翠花 |
p3 | 小强 |
-
要求
-
用户可以查看职位
-
用户可以添加职位
-
用户可以删除职位
-
职位可以查看用户
-
职位可以添加用户
-
职位可以删除用户
-
用户可以设置职位的优先级
-
-
解决方案1
-
Mysql
-
三张表:人物,职位,中间表
-
如果相互关注度高,中间表压力大
-
-
解决方案2
-
HBase
-
3张表: 人员表 职务表 中间表
-
rowkey:用户+职位
-
列族:cf : top
-
p1_zw1
-
p1_zw3
-
p2_zw2
-
zw1_p1
-
zw1_p2
-
zw2_p2
-
111--》666
-
666-》111
-
-
-
解决方案3
-
两张表: 人员表 职务表
-
人员表(person)
-
列族:info人员信息
-
列族:zw
-
内部维护一个数组,数组查找删除值比较麻烦(zws:[zw1,zw2])
-
职务ID就是属性:值全部为null
-
zw1:null
-
zw3:null
-
-
-
-
职务表(zw)
-
列族:info职务信息
-
列族:person
-
ps:[p1,p3,p4]
-
-
-
-
微博:一张表
-
weibo_person
-
列族:info
-
列族:我关注的
-
列族:关注我的
-
-
111
-
info:
-
关注数:8
-
被关注数:99
-
-
我关注的
-
222
-
333
-
444
-
-
关注我的
-
222
-
333
-
-
-
666
-
info
-
我关注的
-
555
-
-
关注我的
-
111
-
333
-
-
-
-
weibo_person_guanzhu
-
rowkey personid p1 p2
-
我关注了谁
-
-
weibo_person_beiguanzhu
-
谁关注了我
-
-
weibo_zhuanfa
-
rowkey personid p1 p2
-
我转发了那条微博
-
-
weibo_beizhuanfa
-
rowkey 微博的id 发送人_发送时间
-
列族中记录转发人的名单
-
-
3.发送微博
-
微博:发送人,文本,多媒体,时间,发送端
-
rowkey 发送人_发送时间
-
列族: 微博info
-
点赞:
-
@谁:
-