HBase的架构
Zookeeper
HBase通过zk来做Master的高可用,RegionServer的监控、元数据的入口以及集群配置的维护工作;具体如下:
通过ZK保证集群中只有一个Master可用,在主的Master的出现异常后,会通过竞争机制产生新的Master
通过ZK监控RegionServer的状态,当RegionServer有异常时,同时Master RegionServer上下线信息。
通过ZK存储元数据
ZK作为统一的入口地址
HMaster
为RegionServer分配Region,维护集群的负载均衡,维护集群的元数据信息,发现失效的Region,将失效的Region分配到正常的RegionServer上,当RegionServer失效时,协调对应的HLog的拆分。
HRegionServer
HRegionServer直接对接用户的读写请求,是真正的干活的额节点,功能如下:
管理Master为其分配的Region
处理来自客户端的读写请求,负责与HDFS进行交互,负责Region变大后的拆分。
Region
- HBase 表根据Rowkey 划分成Region,理论上一个Region包含该表格从起始行到结束之间的所有行。
- 但是往往有可能一个Region没有办法存储这个表中所有的行,会进行切分 数据大小>=N^2*128MB(N为Region数量),当N=9时,切分大小会超过10GB,此时就按照10GB进行切分。
- Region由Store 组成
- Region会被分配到称之为“HRegionServer”的节点上
Store
每一个Region由一个或者多个Store组成,至少是一个Store 。HBase会把经常访问的数据放在一个Store里面,即一个列簇组成一个Store ,有多少个列簇就多少个Store 。
在Store 中,由一个memStore 和 0个或者多个StoreFile 组成
MemStore
- 写缓存。数据先写入到MemStore,触发flush机制后写到磁盘中
- 以key-value的形式存储到内存中
- MemStore的6种Flush机制(当MemStore Flush触发后,同一Region下的所有的MemStore都会刷新)
- MemStore级别:当Region中有一个MemStore的大小达到上限(默认为128MB hbase.hregion.memstore.flush.size),会触发MemStore刷新
- Region级别:当Region中所有的MemStore大小总和达到的上限(256MB hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M)
- RegionServer级别:当RegionServer中所有的MemStore大小总和达到上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
- 当一个RegionServer中HLog数量达到了上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
- HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
- 手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。
StoreFile|HFile
HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、 Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。
由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,由于不需要不停的移动磁盘指针,因此磁盘的顺序写性能很高。
HFile的组成分为6部分,分别是数据块、元数据块、FileInfo块、数据索引块、元数据索引块、HFile文件尾,它们的详细描述如下:
名称 | 描述 |
---|---|
数据块 | 由多个Block(块)组成,每个块的格式为: [块头] + [Key长] + [Value长] + [Key] + [Value]。 |
元数据块 | 元数据是Key-Value类型的值,但元数据块只保存元数据的Value值,元数据的Key值保存在第五项(元数据索引块)中。 该块由多个元数据值组成。 |
FileInfo块 | 该块保存与HFile相关的一些信息。 FileInfo是以Key值排序Key-Value类型的值,基本格式为: KeyValue元素的个数 + (Key + Value类型id + Value) + (Key + Value类型id + Value) + …… |
数据索引块 | 该块的组成为: 索引块头 + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + …… |
元数据索引块 | 该块组成格式同数据块索引,只是部分的意义不一样,组成格式: 索引块头 + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + …… |
HFile文件尾 | 该块记录了其他各块在HFile文件中的偏移信息和其他一些元信息。组成格式如下: 文件尾 + Fileinfo偏移 + 数据块索引偏移 + 数据块索引个数 + 元数据索引偏移 + 元数据索引个数 + 数据块中未压缩数据字节数 + 数据块中全部数据的Key-Value个数 + 压缩代码标识 + 版本标识 |
WAL
Write Ahead Log | 又称 HLog
- 一个文件
- 0.94之前叫做HLog,存储在/hbase/.logs/目录中
- 0.94之后存储在HDFS上的/hbase/WALs/{HRegionServer_name}中
- 记录RegionServer 上所有的编辑信息(Puts/Deletes,属于哪个Region),在写入MemStore之前。
- 理论上一个RegionServer上只有一个WAL实例,数据操作为串行,造成性能瓶颈。在1.0之后 ,可以通过使用底层HDFS的多管道实现了多WAL并行写入。提高了吞吐量(但是并行化是通过对多个Region进行分区实现的,如果只有一个Region那么该方案无效)下面是配置多WAL示例:hbase-site.xml 配置
<property>
<name>hbase.wal.provider</name>
<value>multiwal</value>
</property>
BlockCache|HBase Block
- 读缓存,数据被读取之后仍然缓存在内存中
- 有LruBlockCache(效率较高,GC压力大)和BucketCache(效率较低,没有GC压力)两种BlockCache,默认为LruBlockCache
- 每个RegionServer中只有一个BlockCache实例
HDFS
HDFS为HBase提供最终的底层数据存储服务,同时为HBase提供高可用(HLog存储在HDFS)的支持,具体功能概括如下:
提供元数据和表数据的磁层分布式存储服务
数据多副本,保证高可靠性和高可用性
HBase的特点
- 大:一个表可以有上亿行,上百万列
- 面向列:面向列表(簇)的存储和权限控制,列(簇)独立检索。 结构稀疏:对于为空(NULL)的列,并不占用存储空间,因此,表可以设计的非常稀疏。
- 无模式:每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可 以有截然不同的列。
- 数据多版本:每个单元中的数据可以有多个版本,默认情况下,版本号自动分配,版本号就是单元格插入时 的时间戳。
- 数据类型单一:HBase中的数据都是字符串,没有类型。
HBase和关系数据库区别
- 数据库类型:HBase中的数据类型都是字符串类型(string)
- 数据操作:HBase只有普通的增删改查等操作,没有表之间的关联查询
- 存储模式:HBase是基于列式存储模式,而RDBMS是基于行式存储的
- 应用场景:HBase适合存储大量数据,查询效率极高
HBase 数据结构
Hbase 数据结构其实很简单,涉及4个名词:RowKey|Column Family |Cell|TimeStamps,在说数据结构之前先看一张真正的HBase的表,有助于理解HBase的数据结构
行键 | 时间戳 | 列簇 | 单元格(值) |
---|---|---|---|
“database.software.www” | t1 | anchor:name | John |
“database.software.www” | t2 | info:address | BeiJing |
“database.software.www” | t3 | anchor:name | James |
“database.software.www” | t4 | anchor:tel | 01012345678 |
“database.software.www” | t4 | info:PC | info:PC |
“c.software.www” | t1 | anchor:name | anchor:name |
“c.software.www” | t2 | anchor:tel | 01012345678 |
“c.software.www” | t3 | info:address | BeiJing |
观察上述表格 ,这就是HBASE中存储数据的样子,可以看到HBASE行键是域名倒置的命名方式,时间戳就是当前时间,有不用的时间戳,在冒号之前就是列蔟的名字,但是你会发现,并不是每个的人列蔟都是相同的,你会发现每个人的列蔟都有可能不同,所以这就是体现出了HBase在存储数据的时候可以做到不存储某个属性的数据(在关系型数据中创建表之后,即使该属性没有数据也需要显示为null),这就是Hbase表存储稀疏的特点。
Rowkey | 行键
其实行键就是用来检索记录的主键
与 NoSQL 数据库一样,Row Key 是用来检索记录的主键 。访问HBase中的行,只有三种方式:
1. 通过单个RowKey访问
2. 通过Rowkey的range(正则)
3. 全表扫描
Rowkey可以是任意字符串(注意:最大长度为64 KB),在HBase内部,Rowkey保存为字节数组。存储时,数据按照Rowkey的字典顺序(byte order)排序存储。设计Rowkey时,要充分考虑排序存储这个特性,将经常一起读取的行(IO特性相似的行)存储放到一起。(位置相关性)
Column Family | 列蔟
列蔟:HBase表中的每个列,都归属于某个列蔟,其实就是列的集合。列蔟是表的 Schema 的一部分(而列不是),必须在使用表之前定义。 列名都以列蔟作为前缀。
Cell
关键字:无类型、字节码
由rowkey、column Family :column (值),version 组成的唯一的单元,值得注意是这里的Cell并不单单指当前列蔟的值,而是指好几个属性加起来所表示唯一的值。Cell 中的数据是没有类型 的,全部是字节码形式存储。
TimeStamps
HBase中通过rowkey和columns确定的为一个存储单元成为Cell。每个cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是64位整形。时间戳可以由HBase(在数据写入时 自动)赋值, 此时时间戳是精确到毫秒的当前系统时间。时间戳也 可以由客户显示赋值。如果应用程序要避 免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 Cell 中,不同版本的数据按照时间倒序排序, 即最新的数据排在最前面。
HBase 读写流程
读流程
HBASE读数据流程
1 客户端先访问ZK 从meta表读取Region的位置
2 根据相关信息获取到Regiog所在的位置
3 找到Region对应的RegionServer
4 查找对应的Region
5 在对应的节点中区获取数据(如果MemStore没有刷新,则先从内存中获取数据,如果没有则向HFile中获取数据)
写流程
(1)Client 向HRegionServer发送请求
(2)HRegionServer写入数据到HLog中
(3)HRegionServer写入数据MemStore中
(4)向Client反馈写成功
HBase写数据流程
1 Client首先访问zookeeper,从meta表获取相应region信息,然后找到meta表的数据
2 根据namespace、表名和rowkey根据meta表的数据找到写入数据对应的region信息
3 找到对应的regionserver
4 把数据分别写到HLog和MemStore上一份
4 MemStore达到一个阈值后则把数据刷成一个StoreFile文件。(若MemStore中的数据有丢失,则可以从HLog上恢复)
5 当多个StoreFile文件达到一定的大小后,会触发Compact合并操作,合并为一个StoreFile,(这里同时进行版本的合并和数据删除。)
6 当Storefile大小超过一定阈值后,会把当前的Region分割为两个(Split),并由Hmaster分配到相应的HRegionServer,实现负载均衡
HBase 集群
- 必须安装Hadoop并且配置HADOOP_HOME
- 必须安装Zookeeper,并且保证的ZK正常启动
- 安装配置HBase
[root@HadoopNodeX ~]# mkdir /home/hbase
[root@HadoopNodeX ~]# tar -zxvf hbase-1.2.4-bin.tar.gz -C /home/hbase/
[root@HadoopNodeX ~]# vi .bashrc
export JAVA_HOME=/home/java/jdk1.8.0_181
export HADOOP_HOME=/home/hadoop/hadoop-2.6.0
export HBASE_HOME=/home/hbase/hbase-1.2.4
export HBASE_MANAGES_ZK=false
export MAVEN_HOME=/home/maven/apache-maven-3.3.9
export M2_HOME=/home/maven/apache-maven-3.3.9
export PROTOBUF_HOME=/home/protobuf/protobuf-2.5.0
export FINDBUGS_HOME=/home/findbugs/findbugs-3.0.1
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin:$M2_HOME/bin:$PROTOBUF_HOME/bin:$FINDBUGS_HOME/bin:$HADOOP_HOME/sbin:$HADOOP_HOME/bin:$HBASE_HOME/bin
[root@HadoopNodeX ~]# source .bashrc
[root@HadoopNodeX ~]# vi /home/hbase/hbase-1.2.4/conf/hbase-site.xml
<property>
<name>hbase.rootdir</name>
<value>hdfs://mycluster/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>HadoopNode01,HadoopNode02,HadoopNode03</value>
</property>
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>
</property>
[root@HadoopNode01 ~]# vi /home/hbase/hbase-1.2.4/conf/regionservers
HadoopNode01
HadoopNode02
HadoopNode03
启动HBase
先启动HDFS YRAN zk
[root@HadoopNode02 zookeeper-3.4.6]# yarn-daemon.sh start resourcemanager # 在2节点上启动resourcemanager
[root@HadoopNode03 zookeeper-3.4.6]# yarn-daemon.sh start resourcemanager # 在3节点上启动resourcemanager
[root@HadoopNodeX hadoop]# yarn-daemon.sh start nodemanager # 每个节点上启动nodemanager
[root@HadoopNodeX ~]# hbase-daemon.sh start master