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,但是需要考虑版本的设置策略防止出现出重复的版本;然后不同的数据上有对应的时间戳,数据以时间戳为标准降序排列;新插入的数据就会被优先查询到,根据用户设置的版本数据,保留有效的时间戳

HDFS不支持数据的修改,所以对于修改操作其实就是新增操作

清除数据的两种方式:超过版本数;超过存储时间

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压力过大

 

rowkeyinfo:regioninfoinfo: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路径是固定的

 

rowkeyinfo:regioninfoinfo: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访问网址查看

http://192.168.31.201:60010/

六.搭建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'

删除指定行

delete 'shsxt:student','rowkey1'

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";
@Before
public void init() throws Exception{
//获取配置文件
Configuration configuration = new Configuration();
configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
//创建数据库的连接
hBaseAdmin = new HBaseAdmin(configuration);
//获取到表的连接
hTable = new HTable(configuration, SXT_TABLE_NAME);

}

创建表

//@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
*/
@Test
public void sxtPuts() throws Exception {
//创建批处理容器
List<Put> puts = new ArrayList<>();

for (int i = 0; i < 1000; i++) {
//创建插入数据对象
Put put = new Put(("sc00" + i).getBytes());
//设置本次插入的列族、列、值
put.add("info".getBytes(), "name".getBytes(), ("上海市中学" + i).getBytes());
//将其添加到容器
puts.add(put);
}
//执行插入操作
hTable.put(puts);
}

获取数据

/**
* 获取数据
* @throws IOException
*/
@Test
public void sxtGet() throws IOException {
//创建get对象
Get get = new Get("sc01".getBytes());
get.addColumn("info".getBytes(), "name".getBytes());
//获取数据
Result result = hTable.get(get);
//获取数据
Cell cell = result.getColumnLatestCell("info".getBytes(), "name".getBytes());
System.out.println(new String(CellUtil.cloneValue(cell)));
}

关闭连接

@After
public void destory() throws IOException {
//关闭连接
if (hBaseAdmin != null) {
hBaseAdmin.close();
}
if (hTable != null) {
hTable.close();
}
}

三.表结构的设计

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

    • 点赞:

    • @谁:

posted @ 2019-12-30 13:40  数据阮小白  阅读(145)  评论(0编辑  收藏  举报