只有光头才能变强。文本已收录至个人GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3yhtml
在我还不了解分布式和大数据的时候已经据说过HBase了,但对它一直都半知不解,这篇文章来说讲吧。java
在真实生活中,最开始听到这个词是个人一场面试,当年我仍是个『小垃圾』,如今已是个『大垃圾』了。git
面试官当时给了一个场景题问我,具体的题目我忘得差很少了,大概就是考试与试题的一个场景,问我数据库要如何设计。github
我答了关系型数据库的设计方案,他大概说:这个场景比较复杂多变,为何不考虑一下HBase这种NoSQL的数据库来存储呢?面试
我就说:“对对对,能够的” (虽然我当时不知道HBase是什么,可是气势必定要有,大家说是否是)算法
最后面试官仍是给我发了offer,但我没去,缘由就是离家太远了。数据库
Apache HBase™ is the Hadoop database, a distributed, scalable, big data store.HBase is a type of "NoSQL" database.apache
Apache HBase 是 Hadoop 数据库,一个分布式、可伸缩的大数据存储。后端
HBase是依赖Hadoop的。为何HBase能存储海量的数据?由于HBase是在HDFS的基础之上构建的,HDFS是分布式文件系统。缓存
截至到如今,三歪已经学了很多的组件了,好比说分布式搜索引擎「Elasticsearch」、分布式文件系统「HDFS」、分布式消息队列「Kafka」、缓存数据库「Redis」等等...
可以处理数据的中间件(系统),这些中间件基本都会有持久化的功能。为何?若是某一个时刻挂了,那还在内存但还没处理完的数据不就凉了?
Redis有AOF和RDB、Elasticsearch会把数据写到translog而后结合FileSystemCache将数据刷到磁盘中、Kafka自己就是将数据顺序写到磁盘....
这些中间件会实现持久化(像HDFS和MySQL咱们自己就用来存储数据的),为何咱们还要用HBase呢?
虽然没有什么可比性,可是在学习的时候总会有一个疑问:「既然已学过的系统都有相似的功能了,那为啥我还要去学这个玩意?」
三歪是这样理解的:
MySQL?MySQL数据库咱们是算用得最多了的吧?但众所周知,MySQL是单机的。MySQL能存储多少数据,取决于那台服务器的硬盘大小。以如今互联网的数据量,不少时候MySQL是无法存储那么多数据的。
Elasticsearch?Elasticsearch是一个分布式的搜索引擎,主要用于检索。理论上Elasticsearch也是能够存储海量的数据(毕竟分布式),咱们也能够将数据用『索引』来取出来,彷佛已是很是完美的中间件了。
上面这些技术栈三歪都已经写过文章了。学多了你会发现它们的持久化机制都差不太多,有空再来总结一下。
文中的开头已经说了,HBase是基于HDFS分布式文件系统去构建的。换句话说,HBase的数据其实也是存储在HDFS上的。那确定有好奇宝宝就会问:HDFS和HBase有啥区别阿?
HDFS是文件系统,而HBase是数据库,其实也没啥可比性。「你能够把HBase当作是MySQL,把HDFS当作是硬盘。HBase只是一个NoSQL数据库,把数据存在HDFS上」。
数据库是一个以某种 有组织的方式存储的数据集合。
扯了这么多,那咱们为啥要用HBase呢?HBase在HDFS之上提供了高并发的随机写和支持实时查询,这是HDFS不具有的。
我一直都说在学习某一项技术以前首先要了解它能干什么。若是仅仅看上面的”对比“,咱们能够发现HBase能够以低成本来存储海量的数据而且支持高并发随机写和实时查询。
但HBase还有一个特色就是:存储数据的”结构“能够地很是灵活(这个下面会讲到,这里若是没接触过HBase的同窗可能不知道什么意思)。
听过HBase的同窗可能都听过「列式存储」这个词。我最开始的时候以为HBase很难理解,就由于它这个「列式存储」我一直理解不了它为何是「列式」的。
在网上也有不少的博客去讲解什么是「列式」存储,它们会举咱们现有的数据库,好比MySQL。存储的结构咱们很容易看懂,就是一行一行数据嘛。
转换成所谓的列式存储是什么样的呢?
能够很简单的发现,无非就是把每列抽出来,而后关联上Id。这个叫列式存储吗?我在这打个问号。
转换后的数据从个人角度来看,数据仍是一行一行的。
这样作有什么好处吗?很明显之前咱们一行记录多个属性(列),有部分的列是空缺的,可是咱们仍是须要空间去存储。如今把这些列所有拆开,有什么咱们就存什么,这样空间就能被咱们充分利用。
这种形式的数据更像什么?明显是Key-Value
嘛。那咱们该怎么理解HBase所谓的列式存储和Key-Value
结构呢?走进三歪的小脑壳,一探究竟。
在看HBase数据模型的时候,其实最好仍是不要用「关系型数据库」的知识去理解它。
In HBase, data is stored in tables, which have rows and columns. This is a terminology overlap withrelational databases (RDBMSs), but this is not a helpful analogy.
HBase里边也有表、行和列的概念。
好了,如今比较抽象了。在HBase里边,定位一行数据会有一个惟一的值,这个叫作行键(RowKey)。而在HBase的列不是咱们在关系型数据库所想象中的列。
HBase的列(Column)都得归属到列族(Column Family)中。在HBase中用列修饰符(Column Qualifier)来标识每一个列。
在HBase里边,先有列族,后有列。
什么是列族?能够简单理解为:列的属性类别
什么是列修饰符?先有列族后有列,在列族下用列修饰符来标识一列。
还很抽象是否是?三歪来画个图:
咱们再放点具体的值去看看,就更加容易看懂了:
这张表咱们有两个列族,分别是UserInfo
和OrderInfo
。在UserInfo下有两个列,分别是UserInfo:name
和UserInfo:age
,在OrderInfo
下有两个列,分别是OrderInfo:orderId
和OrderInfo:money
。
UserInfo:name
的值为:三歪。UserInfo:age
的值为24。OrderInfo:orderId
的值为23333。OrderInfo:money
的值为30。这些数据的主键(RowKey)为1
上面的那个图看起来可能不太好懂,咱们再画一个咱们比较熟悉的:
HBase表的每一行中,列的组成都是灵活的,行与行之间的列不须要相同。如图下:
换句话说:一个列族下能够任意添加列,不受任何限制
数据写到HBase的时候都会被记录一个时间戳,这个时间戳被咱们当作一个版本。好比说,咱们修改或者删除某一条的时候,本质上是往里边新增一条数据,记录的版本加一了而已。
好比如今咱们有一条记录:
如今要把这条记录的值改成40,实际上就是多添加一条记录,在读的时候按照时间戳读最新的记录。在外界「看起来」就是把这条记录改了。
HBase本质上其实就是Key-Value
的数据库,上一次咱们学Key-Value
数据库仍是Redis呢。那在HBase里边,Key是什么?Value是什么?
咱们看一下下面的HBaseKey-Value
结构图:
Key由RowKey(行键)+ColumnFamily(列族)+Column Qualifier(列修饰符)+TimeStamp(时间戳--版本)+KeyType(类型)组成,而Value就是实际上的值。
对比上面的例子,其实很好理解,由于咱们修改一条数据其实上是在原来的基础上增长一个版本的,那咱们要准肯定位一条数据,那就得(RowKey+Column+时间戳)。
KeyType是什么?咱们上面只说了「修改」的状况,大家有没有想过,若是要删除一条数据怎么作?实际上也是增长一条记录,只不过咱们在KeyType里边设置为“Delete”就能够了。
扯了这么一大堆,已经说了HBase的数据模型和Key-Value了,咱们还有一个问题:「为何常常会有人说HBase是列式存储呢?」
其实HBase更多的是「列族存储」,要谈列族存储,就得先了解了解HBase的架构是怎么样的。
咱们先来看看HBase的架构图:
一、Client客户端,它提供了访问HBase的接口,而且维护了对应的cache来加速HBase的访问。
二、Zookeeper存储HBase的元数据(meta表),不管是读仍是写数据,都是去Zookeeper里边拿到meta元数据告诉给客户端去哪台机器读写数据
三、HRegionServer它是处理客户端的读写请求,负责与HDFS底层交互,是真正干活的节点。
总结大体的流程就是:client请求到Zookeeper,而后Zookeeper返回HRegionServer地址给client,client获得Zookeeper返回的地址去请求HRegionServer,HRegionServer读写数据后返回给client。
咱们来看下面的图:
前面也提到了,HBase能够存储海量的数据,HBase是分布式的。因此咱们能够判定:HBase一张表的数据会分到多台机器上的。那HBase是怎么切割一张表的数据的呢?用的就是RowKey来切分,其实就是表的横向切割。
说白了就是一个HRegion上,存储HBase表的一部分数据。
HRegion下面有Store,那Store是什么呢?咱们前面也说过,一个HBase表首先要定义列族,而后列是在列族之下的,列能够随意添加。
一个列族的数据是存储在一块儿的,因此一个列族的数据是存储在一个Store里边的。
看到这里,其实咱们能够认为HBase是基于列族存储的(毕竟物理存储,一个列族是存储到同一个Store里的)
Store里边有啥?有Mem Store、Store File、HFile
,咱们再来看看里边都表明啥含义。
HBase在写数据的时候,会先写到Mem Store
,当MemStore
超过必定阈值,就会将内存中的数据刷写到硬盘上,造成StoreFile,而StoreFile
底层是以HFile
的格式保存,HFile
是HBase中KeyValue
数据的存储格式。
因此说:Mem Store
咱们能够理解为内存 buffer,HFile
是HBase实际存储的数据格式,而StoreFile
只是HBase里的一个名字。
回到HRegionServer上,咱们还漏了一块,就是HLog
。
这里其实特别好理解了,咱们写数据的时候是先写到内存的,为了防止机器宕机,内存的数据没刷到磁盘中就挂了。咱们在写Mem store
的时候还会写一份HLog
。
这个HLog
是顺序写到磁盘的,因此速度仍是挺快的(是否是有似曾类似的感受)...
稍微总结一把:
咱们在上面的图会看到有个Hmaster,它在HBase的架构中承担一种什么样的角色呢?读写请求都没通过Hmaster呀。
那HMaster在HBase里承担什么样的角色呢??
HMaster
is the implementation of the Master Server. The Master server is responsible for monitoring all RegionServer instances in the cluster, and is the interface for all metadata changes.
HMaster会处理 HRegion 的分配或转移。若是咱们HRegion的数据量太大的话,HMaster会对拆分后的Region从新分配RegionServer。(若是发现失效的HRegion,也会将失效的HRegion分配到正常的HRegionServer中)
HMaster会处理元数据的变动和监控RegionServer的状态。
到这里,咱们已经知道RowKey是什么了。不难理解的是,咱们确定是要保证RowKey是惟一的,毕竟它是行键,有了它咱们才能够惟一标识一条数据的。
在HBase里边提供了三种的查询方式:
首先咱们要知道的是RowKey是会按字典序排序的,咱们HBase表会用RowKey来横向切分表。
不管是读和写咱们都是用RowKey去定位到HRegion,而后找到HRegionServer。这里有一个很关键的问题:那我怎么知道这个RowKey是在这个HRegion上的?
HRegion上有两个很重要的属性:start-key
和end-key
。
咱们在定位HRegionServer的时候,实际上就是定位咱们这个RowKey在不在这个HRegion的start-key
和end-key
范围以内,若是在,说明咱们就找到了。
这个时候会带来一个问题:因为咱们的RowKey是以字典序排序的,若是咱们对RowKey没有作任何处理,那就有可能存在热点数据的问题。
举个例子,如今咱们的RowKey以下:
java3y111 java3y222 java3y333 java3y444 java3y555 aaa bbb java3y777 java3y666 java3y...
Java3yxxx
开头的RowKey不少,而其余的RowKey不多。若是咱们有多个HRegion
的话,那么存储Java3yxxx
的HRegion的数据量是最大的,而分配给其余的HRegion数量是不多的。
关键是咱们的查询也几乎都是以java3yxxx
的数据去查,这会致使某部分数据会集中在某台HRegionServer上存储以及查询,而其余的HRegionServer却很空闲。
若是是这种状况,咱们要作的是什么?对RowKey散列就行了,那分配到HRegion的时候就比较均匀,少了热点的问题。
HBase优化手册:
建表申请时的预分区设置,对于常用HBase的小伙伴来讲,HBase管理平台里申请HBase表流程必然不陌生了。
'给定split的RowKey组例如:aaaaa,bbbbb,ccccc;或给定例如:startKey=00000000,endKey=xxxxxxxx,regionsNum=x'
第一种方式:
是本身指定RowKey的分割点来划分region个数.好比有一组数据RowKey为[1,2,3,4,5,6,7],此时给定split RowKey是3,6,那么就会划分为[1,3),[3,6),[6,7)的三个初始region了.若是对于RowKey的组成及数据分布很是清楚的话,可使用这种方式精确预分区.第二种方式 :
若是只是知道RowKey的组成大体的范围,能够选用这种方式让集群来均衡预分区,设定始末的RowKey,以及根据数据量给定大体的region数,通常建议region数最多不要超过集群的rs节点数,过多region数不但不能增长表访问性能,反而会增长master节点压力.若是给定始末RowKey范围与实际误差较大的话,仍是比较容易产生数据热点问题.最后:生成RowKey时,尽可能进行加盐或者哈希的处理,这样很大程度上能够缓解数据热点问题.
上面的状况是针对经过RowKey单个查询的业务的,若是咱们是根据RowKey范围查询的,那不必上面那样作。
HBase将RowKey设计为字典序排序,若是不作限制,那极可能相似的RowKey存储在同一个HRegion中。那我正好有这个场景上的业务,那我查询的时候不是快多了吗?在同一个HRegion就能够拿到我想要的数据了。
举个例子:咱们会间隔几秒就采集直播间热度,将这份数据写到HBase中,而后业务方常常要把主播的一段时间内的热度给查询出来。
我设计好的RowKey,将该主播的一段时间内的热度都写到同一个HRegion上,拉取的时候只要访问一个HRegionServer就能够获得所有我想要的数据了,那查询的速度就快不少。
最后三歪再来带着你们回顾一下这篇文章写了什么:
参考资料:
下面的文章都有对应的 原创精美PDF,在持续更新中,能够来找我催更~
若是你们想要实时关注我更新的文章以及分享的干货的话,微信搜索Java3y。
PDF文档的内容均为手打,有任何的不懂均可以直接来问我(公众号有个人联系方式)。
给三歪点个赞,对三歪真的很是重要!