HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储。
官方网站:http://hbase.apache.org
HBase是依赖hadoop的,依赖hdfs的,HBase存储的所有数据其实是在DataNOde上面,数据是存在RegoinServer上面的,RegionServer里面的数据具体是存在DataNode上面的,HBase视觉上能看到的是RegoinServer和Hmaster两个节点,类似于NameNode和DataNode节点。HBase在操作表的时候有原数据,原数据存到zookeeper上
HBase内置有zookeeper,但一般我们会有其他的Zookeeper集群来监管master和regionserver,Zookeeper通过选举,保证任何时候,集群中只有一个活跃的HMaster,HMaster与HRegionServer 启动时会向ZooKeeper注册,存储所有HRegion的寻址入口,实时监控HRegionserver的上线和下线信息。并实时通知给HMaster,存储HBase的schema和table元数据,默认情况下,HBase 管理ZooKeeper 实例,Zookeeper的引入使得HMaster不再是单点故障。一般情况下会启动两个HMaster,非Active的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。
一个RegionServer可以包含多个HRegion,每个RegionServer维护一个HLog,和多个HFiles以及其对应的MemStore。RegionServer运行于DataNode上,数量可以与DatNode数量一致,请参考如下架构图:
客户端拿一个数据过来,首先写到Hlog上面,从上图能够看到Hlog中的数据最终还是写到了hdfs上面,为什么写到hdfs上面?数据量很大,如果都写到Hlog中,保存到本地,本地磁盘不一定够用,而且本地的管理系统和hdfs的管理系统不一样当Hlog使用成功之后,就已经写到hdfs上面了,hdfs这个系统适用于一次写入多次读取,所以不能使直接在hdfs上面创建一个文件,然后一点一点的往里面写,而应该是在Hlog中写入成功了,然后一次上传到hdfs中,成功之后把Hbase里面的数据和数据的操作写入到Memory store(内存)中,内存里面的数据写到StoreFile HFile文件(region)中,最终这个文件是保存到hdfs上的,表明某条数据成功的写入到了region上面,region保存到DataNode中。RegionServer维护Region的生命周期,Region的存储本质是在DataNode上。
问题:
1 在Hlog的时候已经写入到了hdfs,为什么在region的时候还是写入到了hdfs?
Hlog中的文件是临时的,会d是为了防止数据丢失的预写入文件,region是持久化存储的地方
2 有的人认为写完到Hlog之后就直接写到Region中了
中间还有Memory Store
在对比的结果出来之前,先读完这大段话:
RegionServer中有Hlog(WAL技术–write-ahead logging),预写入日志,假如现在,有一条数据需要写到磁盘上,首先这条数据的数据先进入到内存中,然后内存中所有的数据再写到磁盘中,按照实际经验应该是这样的,但是HBase不是,内存中的数据可能会出现原地爆炸,然后丢了,NameNode在处理这块的时候怎么做的?因为丢的是很短时间内的内存里面的数据,丢的频率非常高,数据库里面不能这么做,在数据库中有ACID,不允许出现脏数据,HBase是不支持这样的操作的,不支持事物的回滚和撤销,,所以不支持怎么办?所以不能把数据先写到内存里面,应该先写到本地,本地有一个文件叫Hlog,Hlog写到本地之后,放到HDFS上面,所以本地是放在了HDFS,写完日志之后,如果写成功的话,会把数据放在内存里面(注意:先写Hlog,如果Hlog都没有写成功,就不往内存走),如果写入到内存成功了,内存中默认的阈值的64m,如果到达了这个阈值,就会把内存里面的数据写到Region上(一个RegionServer上有多个Region),还有一个阈值,40%,如果当前内存里面存储的数据达到了当前虚拟机所分配的堆内存总大小的40%的话,也会把内存里面的数据写到Region上,一个Region就相当于一张表,如果数据从一开始历经坎坷最终到了Region中,认为这条数据是成功写入的,否则是不成功写入的。
为什么要实行WAL机制?如果现在执行了WAL机制,断电了,假如提交一个数据的变更,数据写到了WAL日志里面了,然后往内存里面写也成功了,然后内存中往Region里面写,内存中往Region中写的时候断点了,这个时候Region中存的是脏数据,等会断电恢复了,内存重新去Hlog中拿数据,把刚才原本的操作的数据重新恢复到内存里面,恢复到内存里面之后,会删除掉Region之前操作错误的脏数据,然后重新把数据写进去,这样就保证了在断电的时候的恢复,当然了,任何东西都没有完美的,这个机制也会导致丢数据,在原数据往Hlog里面写数据的时候断电了,Hlog是不完整的,就会造成数据的丢失,但是丢失的是很小很小的一部分的数据,这样是不会出现类似我转一百块,那边没有收到,我还付钱了,因为整个流程就没成功,我这里扣钱也不会成功的,更改原数据的前提是,完整的写入了而且成功了,包括供Hlog到内存再到Region,所以是到不了这步的,如果直接写到内存里面的话,很可能到内存里面之后就会直接改变原数据,所以确保了HBase数据的有效性。
WAL什么时候用?能不能不用?
可以不用,如果业务程序对数据的损失要求的条件不高的话,可以吧WAL机制断掉,关掉之后可以有效的提升HBase的速度,大概提高20%到120%,如果对数据的严密性要求非常高,不允许数据的大面积丢失,一定要把这个机制打开
HBase是可以接入在线业务使用的,不像hive是离线的,假如把hive接到线上,用户写好了查询,查十秒,没有给用户结果,用户以为自己掉线了,所以hive是不能在线的,HBase是可以在线的,HBase比hive稍稍快一点,但是快的速度还是没有达到关系型数据库那么快的,所以HBase直接在线上使用的话也是比较少的,一般还是关系型数据库处理实时的在线数据,处理完之后,定期再把数据弄到HBase上面,这个定期的频率很低,所以一般称之为,HBase接入的就是在线的业务, 比如facebook在聊天的时候,短消息到关系型数据库之后,把消息最终布在HBase里面,布在HBase里面的好处是查询非常快,为什么查询非常快?需要继续探讨Region的机制
HBase的原数据是存在zookeeper中的,现在假设用户有一个操作,这个操作是启动HBase,当HBase启动的时候,不管是先启动RegionServer还是Hmaster,是无所谓的,如果HMaster启动成功的话,会到zookeeper里面拿RegionServer的所有的信息,也就是Region里面存的信息,也就是说HMaster启动的时候会向zookeeper请求,去拿RegionServer的region的信息(region在那个地方存着,在哪个RegionServer上存着,在RegionServer的哪个位置上存着),HMaster启动的时候会向zookeeper写入当前的运行状态,为了使后面的高可用以及RegionServer和HMaster之间的一个通信,说白了HMaster和RegoinServer里面的所有的信息都会写到zookeeper里面,在HMaster初始化的时候,会把zookeeper里面的所有的信息一次性加载到自己的机器上,当客户端有一个对数据操作请求的时候,首先会请求HMaster,为什么请求HMaster,因为访问原数据,原数据是存在RegionServer上的,如果直接去RegionServer上拿,很不方便,就好像去图书馆拿一本书,直接去拿,很不方便,但是通过问管理员在哪一层的哪个书架上很快就能找到,zookeeper就是图书管理员的角色的充当,zookeeper告诉HMaster所有的RegionServer和Region都是在哪里分布的,HMaster在启动的时候把这些信息都读到自己的机器上,当客户端发送一个请求,要操作某个数据的时候,会去HMaster里面拿到刚才初始化的时候从zookeeper里面拿到的东西,注意并不是客户端去HMaster里面拿的时候,HMaster采取收集数据,而是HMaster初始化的时候就已经拿到了数据,准备给客户端用,HMaster把客户端要请求的数据所在的RegionServer里面的Region的位置返回给客户端,客户端拿到Region的地址之后,客户端直接去操作RegionServer,客户端的读和写是直接和RegionServer对接的,不再走HMaster了,和NameNode不一样,NameNode如果在操作的过程中挂掉了,立刻就不能成功了,这个“立刻”是有延迟性的,比如HDFS系统,跟NameNode通完信之后,开始跟DataNode通信,开始写入数据了,在写入某一个副本块的时候,按道理说不需要跟NameNode通信,这个时候NameNode挂掉,其实还是可以正常运行的,为什么NameNode挂了,就没法用了,其实这里说的是宏观上的如果细节到每一个时间单位的话,会发现其实很多操作在提交完成之后,大脑挂掉了,但是还能完成工作,就好像人一样,心脏坏了,但是还能活几秒,但是广义上认为这个人已经死了。现在客户端和RegionServer进行交互的时候,HMaster挂掉了这个时候还是能够同步交互的,还能写还能读,这个时候会出现一个问题,HMaster挂掉了,但是RegionServer还活着跟客户端进行往来和交互,下次再去启动HBase的时候,就会导致zookeeper里面的原数据和后面操作的不一致,就找不到了,需要一个解决方案,即高可用,如果没有实现高可用的话,在使用的时候一定要很小心,如果在使用的时候HMaster不小心挂掉了,一定要赶紧启动起来,不然会造成严重的后果,我们可能会开一个守护进程,或者一个脚本去监听HBase端口,如果发现HBase端口没了,就报警,报警之后就赶紧排查一下
HBase是行键的
上图中的info和job都是列族,下面包括有自己的列,info和job这两个列族就是一个row key
图中的put “rowkey” “CF:CN(时间戳) 数据” 是HBase简单的语句格式
数据已经上传到hdfs里面了,为什么还要上传到region中?而且region也是在hdfs中。
数据和数据对应的操作,数据要操作的时候,会先把数据对应的操作和数据本身写到Hlog中,Hlog会上传到HDFS,Hlog中保存的不只是数据本身,是数据本身和它本身应该做的但是还没有做的操作,只是当次的没有生效到region里面的操作,叫预写入操作,这一步保存如果成功了,下一步会往内存中走,上面提到,内存中阈值是64m,64m肯定不是一个完整的信息,只能是本次的一部分信息,如果也成功的话,会把数据保存到region中,如果保存到region中成功了,前面的Hlog和内存就删除掉了,数据最终持久化保存到region上面,Hlog和内存部分只是在保存数据的过程中产生的临时的文件,可能本次数据的操作量非常大,1000m,不是一次性全部写入到Hlog中,而是将1000m拆分开一个一个的写入到Hlog中,写入一次,在region中生效一次,如果在region中生效之后,就将Hlog删了,这里说的是HBase就是一份数据,但是region中的数据最终是会上传拿到hdfs上的,hdfs再根据副本数备份相应的个数。
HBase的分片
为什么分成多个region呢?刚开始其实是一个的,region中的数据是通过rowkey唯一标识的,假设当前的region所维护的rowkey1到rowkey2,假如维护的范围是1000-100000(如果不手动指定的话,是根据hash值来决定,加入当前的数据rowkey=10000然后根据1000生成hash值,每个region会有一个相对于hash值的范围,如果当前的rowkey的hash值正好在这个范围中的话,就会存到当前的region里面,),所有数据的rowkey只要符合这个范围的都往这个region里面扔,就会导致region越来越大,就会导致region越来越大,大到一定程度之后就不好维护了,且效率也会下降,所以会自动的切开,切开之后。
存到region中的数据是根据rowkey存的,region维护了rowkey的范围,默认是0到无穷大,假如现在有一个rowkey=1的数据,被扔到了RegionServer中,RegionServer要根据当前的这个rowkey范围,应该放到哪个region中,但是限制只有一个范围是0到无穷大的region,所以当前的这条数据一定会落入到这个唯一的region里面,下面又进来的很多数据,当数据大到一定的程度的时候,比如把rowkey的数据增加到1000的时候把这个表分开了,就意味着,再有第1001个数据进来的时候,就进入到第二个region中去了,第二个region维护的是1001到无穷大,后面会还是如此,这种情况会发生数据倾斜,需要通过人工的干预和设计去解决数据倾斜,如果做大型任务的话,一定会出现数据倾斜。
结构:
功能:
通过上图,可以看到,例如mysql存储的时候是将一整行即 id= xxx name= xxx sex = xxx,一次存储到内存的同一个磁道上,而不是name存储到磁道一,sex存储到磁道二,上图就是一个错误的示范。
结构:
功能:
支持向外扩展(即横向扩展,不需要升级原来集群里面的配置,只需要不停的往上加就可以了,这样的好处就是,如果不停的向外扩展的话,总有一个极限,硬件总会达到一个瓶颈,这个时候可以选择数量变多,这样的扩展就很好,这也是hbase火的一个原因)
使用API和MapReduce来访问HBase表数据(上面看到put xxxx这样的语句,其实底层还是api,可以通过java代码去操作里面的数据,底层集成了MapReduce,比如HBase中间过程会产生一些小文件,这些小文件会严重影响集群性能,它会定期的在闲时的时候自动的通过MapReduce把小文件合并成大文件。)
面向列,即每一列都是在某一页上的一个连续的单元
rowkey是唯一的,但是不是单纯的数字递增的,比如还可以是abc helloworld,rowkey包含了若干个列族,若干个列,在访问某个列的时候,就会把整个rowkey存储单元全部读下来,暴扣里面的列族,这就是hbase的存储单元
数据总量不依赖具体某台机器,而取决于机器数量(依赖于集群的数量)
HBase不支持ACID(Atomicity、Consistency、Isolation、Durability)
适合结构化数据和非结构化数据
*
比如hive中的数据都是tab键隔开的就是结构化数据,rowkey中第一个列族里面有六列,第二个里面只有两列,有点类似java中的map Map
一般都是分布式的
HBase不支持事务
为什么HBase访问很快?
客户端去访问某一条数据的时候,先去内存里面找, MemStore是内存里面的结构化分区,MemStore和内存数据缓冲池,都是内存,只不过这两个东西在内存中的结构区分开了,客户端在访问数据的时候,会先去MemStore里面查看是否有数据,如果有的话直接就返回了,这种情况非常快,不会涉及到磁盘io,第二种情况是客户端去MemStore里面找数据没找到,会去内存缓冲池里面找,这个数据是怎么来的呢?是通过LRU算法,维护的都是最近操作的数据,因为他也是在内存中所以很快,如果没有的话,去region里面读,先读到内存缓冲池中,读完之后直接返回给客户端。如果新读进来内存缓冲池里面没有内存的话,就删除掉之前权重比较小的数据。
LRU算法具体做了什么呢?
上图中是一个列表中有无数个图片,向下加载了一遍,到底部之后,再从上往下加载一遍,如果没有缓存的话,还要再重新加载一遍,这是最不合理的情况,现在需要进行一次优化,把加载过的图片加载到本地,可是这样还是有问题的,每次都需要到本地不停的读取,所以迎来的第二次优化,缓存到内存中,然后在从内存中保存到磁盘上,下次再访问的时候,先看内存中有没有,有的话就直接从内存里面拿,没有的话再去磁盘里面拿,可是随着图片越来越多,内存支撑不下了,内存溢出了,所以又有了第三次优化,使用LRU算法,还是把加载过的图片缓存在内存里面,维护一个单独的队列,用来记录最频繁使用的图片,分配权重,也就是你可能重复看一个比较好看的你妹子,每重复看一次,在这个队列中的权重就会增大一点,对于数据库来讲就是操作数据库比较频繁,然后定期清除内存中缓存的内容,袈裟当前的LUR算法中维护的内存可存储的数据大小是10m,假如当前已经到了10m,可是下次又有新的数据过来了, 这个时候怎么办,将权重最小的数据删除掉,最后队列中存储的内容,要不然就是最新的,要不然就是最常使用的,
HBase运行在HDFS上,所以HBase中的数据以多副本形式存放,数据也服从分布式存放,数据的恢复也可以得到保障。另外,HMaster和RegionServer也是多副本的。
通过MapReduce不断的将小文件合并为大文件,本身就是负载均衡的机制,HMaster是高可用的,可以依赖zookeeper配置HMaster高可用
HBase表是由分布在多个RegionServer中的region组成的,这些RegionServer又分布在不同的DataNode上,如果一个region增长到了一个阈值,为了负载均衡和减少IO,HBase可以自动或手动干预的将region切分为更小的region,也称之为subregion。
虽然HBase也可以运行在其他的分布式文件系统之上,但是与HDFS结合非常之方便,而且HDFS也非常之流行。支持很多的文件系统,这里比较流行的hdfs
HBase采用log-structured merge-tree作为内部数据存储架构,这种架构会周期性地将小文件合并成大文件以减少磁盘访问同时减少NameNode压力。响应速度应该在毫秒级别的。
log-structured merge-tree log日志结构化的合并:
第一次内存中是没有树的,就可以新建一棵树,当里面的树存储到一定程度了,就把这个数据结构存到磁盘里面,存完之后又有新的数据往里面添加,会再建一棵树,然后数据又写满了,又放到磁盘里面,如此往复会产生很多这样的树,通过ls算法把这些树合并到一个,LSM算法是通过B tree和 B+ Tree 这两个算法演化而来的
HBase内建支持MapReduce框架,更加方便快速,并行的处理数据。
HBase本身是不支持join操作的,可以通过MapReduce实现类似的功能,但是不建议这样做,因为效率低。
HBase提供原声的Java API支持,方便开发。
HBase支持横向扩展,这就意味着如果现有服务器硬件性能出现瓶颈,不需要停掉现有集群提升硬件配置,而只需要在现有的正在运行的集群中添加新的机器节点即可,而且新的RegionServer一旦建立完毕,集群会开始重新调整。
HBase是面向列存储的,每个列都单独存储,所以在HBase中列是连续存储的,而行不是。
HBase提供了交互式命令行工具可以进行创建表、添加数据、扫描数据、删除数据等操作和其他一些管理命令。
HBase一种是作为存储的分布式文件系统,另一种是作为数据处理模型的MR框架。因为日常开发人员比较熟练的是结构化的数据进行处理,但是在HDFS直接存储的文件往往不具有结构化,所以催生出了HBase在HDFS上的操作。如果需要查询数据,只需要通过键值便可以成功访问。
HBase是建立在HDFS基础之上的,内嵌支持MapReduce,可以调度在YARN上,HBase可以和hive进行对接,hive和HBase之间可以通过sqoop进行数据的导入导出,为什么hive和HBase很多时候要结合起来访问?HBase适合结构化和非结构化存储,而hive适合分析,HBase是不适合分析的,所以同HBase进行大量数据的存储,用hive进行大量数据的分析,小公司HBase用的不多,关系型数据库就能搞定,
HBase是由row key,column family,column和cell(单元格)组成,row key确定唯一的一行,column family由若干column组成,column是表的字段,cell存储了实际的值或数据,如何访问单元格呢?必须通过列族和列。
HDFS中的数据不允许覆盖,也不允许在原有的基础上修改,那么HBase在修改数据的时候对HDFS做了哪些事情?
为什么不能直接让RegionServer汇报给HMaster,为什么需要zookeeper?
zookeeper首先能够统一状态信息,其次有隔离的作用,每一个RegionServer都和HMaster交互的话,会导致HMaster很卡,所以需要中间有一个托管,去汇总这些信息,汇总完成之后,统一告诉HMaster,这样可以减少HMaster的压力。
Write-Ahead logs
HBase的修改记录,当对HBase读写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。如果机器突然原地爆炸,把数据保存在内存中会引起数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
为了防止数据丢失,但是数据的存储会变得慢一点。
HFile
这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。
内存中的文件始终是要持久化保存的,保存的形式是Hfile这样的一个文件的形式存在DataNode上面。
关系型数据库是表维护数据的,HBase中是region维护数据的,这些表述都是逻辑上的概念,比如过在学习物理的时候,向心力等都是概念,不是实际存在的东西。
这里也是存储的时候一定有一个文件的形式,这个文件就是Hfile。
Store
HFile存储在Store中,一个Store对应HBase表中的一个列族
MemStore
顾名思义,就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在WAL中之后,RegsionServer会在内存中存储键值对。
Region
Hbase表的分片,HBase表会根据RowKey值被切分成不同的region存储在RegionServer中,在一个RegionServer中可以有多个不同的region
HMaster与HRegionServer 启动时会向ZooKeeper注册,存储所有HRegion的寻址入口,实时监控HRegionserver的上线和下线信息。并实时通知给HMaster,存储HBase的schema和table元数据,默认情况下,HBase 管理ZooKeeper 实例,Zookeeper的引入使得HMaster不再是单点故障。一般情况下会启动两个HMaster,非Active的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。
在配置HMaster高可用的时候,不需要配置HDFS的高可用,是彼此独立的。
一般而言数百万行的数据和频率不高的读写操作,是不需要HBase的,如果有几十亿列数据,同时在单位时间内有数以千、万记的读写操作,可以考虑HBase。