HBase工作原理

HBASE原理

一、原理

1、物理存储

1.hregion

    hbase表中的数据按照行键的字典顺序排序,hbase表中的数据按照行的的方向切分为多个region。

    最开始只有一个region随着数据量的增加,产生分裂,这个过程不停的进行。一个表可能对应一个或多个region。

    region是hbase表分布式存储和负载均衡的基本单元,一个表的多个region可能分布在多台HRegionServer上。

2.Store

    region是分布式存储的基本单元,但不是存储的基本单元,其内部还具有结构。一个region由多个Store来组成。

    有几个store取决于表的列族的数量,一个列族对应一个store。之所以这么设计,是因为一个列族中的数据往往数据很类似,方便进行压缩,节省存储空间。

3.memStore

    表的一个列族对应一个store,store的数量由表中列族的数量来决定。一个store由一个memstore和零个或多个storefile组成。

    memStore负责保存内存中的数据。

4.Storefile

    storefile其实就是hdfs中的hfile只能写入不能修改,所以hbase写入数据到hdfs的过程其实是不断追加hfile的过程。

    hfile的结构:

    HFile分为六个部分:

    Data Block段–保存表中的数据,这部分可以被压缩

    Meta Block 段(可选的)–保存用户自定义的kv对,可以被压缩。

    File Info段–Hfile的元信息,不被压缩,用户也可以在这一部分添加自己的元信息。

    Data Block Index段–Data Block的索引。每条索引的key是被索引的block的第一条记录的key。

    Meta Block Index段(可选的)–Meta Block的索引。

    Trailer–这一段是定长的。保存了每一段的偏移量,读取一个HFile时,会首先 读取Trailer,Trailer保存了每个段的起始位置(段的Magic Number用来做安全check),然后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不需要扫描整个HFile,而只需从内存中找到key所在的block,通过一次磁盘io将整个 block读取到内存中,再找到需要的key。DataBlock Index采用LRU机制淘汰。

    HFile的Data Block,Meta Block通常采用压缩方式存储,压缩之后可以大大减少网络IO和磁盘IO,随之而来的开销当然是需要花费cpu进行压缩和解压缩。

    目标Hfile的压缩支持两种方式:Gzip,Lzo。

5.HLog(WAL log)

    WAL意为Write ahead log(http://en.wikipedia.org/wiki/Write-ahead_logging),类似mysql中的binlog,用来做灾难恢复之用,Hlog记录数据的所有变更,一旦数据修改,就可以从log中进行恢复。

    每个Region Server维护一个Hlog,而不是每个Region一个。这样不同region(来自不同table)的日志会混在一起,这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高对table的写性能。带来的麻烦是,如果一台region server下线,为了恢复其上的region,需要将region server上的log进行拆分,然后分发到其它region server上进行恢复。

    HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是”写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue。

2、读写流程

1.hbase写入数据

    数据写入hbase时,先在hlog中记录日志,再修改memstore,然后直接返回成功。这样不需要真正等待写入hdfs的过程,所以很快。

    memstore内存有限,当写入数量达到一定的阈值的时候,就会创建一个新的memstore继续工作,而旧的memstore会用一个单独的线程将数据写出到storefile中,最终清空旧的memstore并在zookeeper中记录最后写出数据时间的redo point信息。

    由于storefile不能修改,所以数据的更新其实是不停创建新的storefile的过程。

    这样多个storefile中可能存在对同一个数据的多个版本,其中部分旧的版本其实是垃圾数据,时间过长,垃圾数据就可能很多,浪费磁盘空间。

    所以当达到一定的阈值的时候,master会自动合并storefile,在合并的过程中将垃圾数据清理。而当合并出来的文件达到一定程度时,再从新进行切分,防止文件过大。

    虽然看起来是小变大再变小,但是经过这个过程垃圾数据就被清理掉了。所以store中的数据,其实是memstore和storefile来组成的。

    而memstore由于是内存中的数据,一旦断电就会丢失。为了解决可能的意外造成数据丢失的问题,hbase在整个hregionserver中,通过记录hlog来保存了所有数据操作的记录。当hbase启动时,会检查zookeeper中的redopoint信息,从hlog中恢复这个时间点之后的数据,解决内存中memStore数据容易丢失的问题。

    hlog文件在每台hregionServer中都有一个,所有这台机器中的所有HRegion都公用这个文件,这样整个机器的磁盘性能都可以为这一个文件提供支持,提升文件的读写效率。

    hlog文件也是存储在hdfs中的,也是分布式存储的,保证了日志文件的可靠性。

2.hbase读取数据

    在查询数据时,先查找内存,如果内存中有当前键对应的数据,获取数据直接返回。

    如果没有在内存中找到数据,就去查找region对应的hfile,注意不是将所有hfile中的数据恢复到内存,而是查找每个hfile的Trailer,通过trailer找到Data Block Index,如果在这里发现了要找的数据,通过索引找到Data Blocks中对应的Data Block,将Data Block数据送回内存组装,最终将多个hfile中获取到的数据进行合并,然后返回最新版本的数据。

    由于hbase中的数据天然排序,再加上底层索引,整个查询也可以非常的快。

3.hbase中region的寻址

    在hbase的hbase名称空间下有一张meta表,其中存放了表和region和regionSever之间的对应关系信息,这个表很特别,规定meta表只能有一个region。并且这个meta表的这个region的位置信息被存放在了zookeeper的meta-region-server节点下。

    在客户端从hbase中查找数据时,需要先联系zookeeper找到meta表对应的region的位置,连接这个位置读取到meta表中的信息,才能知道要查询的表、表的region和region对应的regionServer的信息。再根据这些信息连接真正要查询的表对应的region的regionServer进行读取,这个过程就称之为region的寻址过程。

    这样的寻址过程是相当耗费时间的,为了提高性能,客户端通常会缓存之前已经知道的region寻址信息,当客户端再次读取同一个表中的数据时,可以通过本地缓存的region地址信息,直接定位读取,提高效率。

3、存储系统三种结构

1.hash存储

    哈希存储引擎是哈希表的持久化实现,支持增、删、改以及随机读取操作,但不支持顺序扫描,对应的存储系统为key-value存储系统。对于key-value的插入以及查询,哈希表的复杂度都是O(1),明显比树的操作O(n)快,如果不需要有序的遍历数据,哈希表就是your Mr.Right。

2.B树

    B+树 B-树 B*树

    B树存储引擎是B树的持久化实现,不仅支持单条记录的增、删、读、改操作,还支持顺序扫描(B+树的叶子节点之间的指针),对应的存储系统就是关系数据库(Mysql等)。

3.LSM树

    LSM树(Log-Structured Merge Tree)存储引擎和B树存储引擎一样,同样支持增、删、读、改、顺序扫描操作。而且通过批量存储技术规避磁盘随机写入问题。当然凡事有利有弊,LSM树和B+树相比,LSM树牺牲了部分读性能,用来大幅提高写性能。

    通过以上的分析,应该知道LSM树的由来了,LSM树的设计思想非常朴素:将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,不过读取的时候稍微麻烦,需要合并磁盘中历史数据和内存中最近修改操作,所以写入性能大大提升,读取时可能需要先看是否命中内存,否则需要访问较多的磁盘文件。极端的说,基于LSM树实现的HBase的写性能比Mysql高了一个数量级,读性能低了一个数量级。

    LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。

    以上这些大概就是HBase存储的设计主要思想,这里分别对应说明下:

    因为小树先写到内存中,为了防止内存数据丢失,写内存的同时需要暂时持久化到磁盘,对应了HBase的MemStore和HLog。

    MemStore上的树达到一定大小之后,需要flush到HRegion磁盘中(一般是Hadoop DataNode),这样MemStore就变成了DataNode上的磁盘文件StoreFile,定期HRegionServer对DataNode的数据做merge操作,彻底删除无效空间,多棵小树在这个时机合并成大树,来增强读性能。

4、hbase系统架构:

    hbase中的老大叫hmaster,小弟叫hregionServer,客户端叫Client,Zookeepr为hbase提供集群协调。

1.client

    访问hbase保留一些缓存信息提升效率。

2.zookeeper

    保证任何时候集群只有一个HMaster。

    监控regionServer的状态,将其上线下线信息通知mater。

    存储meta表Region的地址。

    存储hbase的元数据信息。包括有哪些表、有哪些列族等等。

3.Master

    为RegionServer分配Region。

    为RegionServer进行负载的均衡。

    GFS上的垃圾回收。

    处理对Schema数据的更新请求。

4.RegionServer

    维护Master分配给它的region,处理对这些region的IO请求。

    负责切分在运行过程中变得过大的region。

5、总结

1.为什么hbase可以很快

1>从逻辑结构上来说

    表按照行键进行了排序,而且加了索引,所以查询时可以很快定位。

    数据按照行键切分为多个HRegion,分布在多个RegionServer中,查询大量数据时,多个RegionServer可以一起工作,从而提高速度。

2>从物理结构上来说

    HRegion是存活在RegionServer的内存中的,读写会非常的高效。

    还有HFile的支持保证大量的数据可以持久化的保存。

    数据最终落地到HDFS中,分布式的存储,保证数据段可靠性和可扩展性。

2.为什么hbase可以存储很多数据

    基于hdfs,所以支持可扩展性,可以通过增加大量的廉价的硬件提高存储容量。

    按列存储,空的数据不占用空间,当存储稀疏数据时,不会浪费空间。

    按例存储,同一列的数据存放在一起,而同一列的数据一般都是同样的类型的内容相似的数据,可以实现非常高效的压缩,节省空间。

3.为什么hbase的数据是可靠的

    基于hdfs,由hdfs的可靠性保证了hbase的可靠性,即数据可以有多个备份。

    利用zookeeper实现了HA(高可用集群),即使某一台机器挂掉另外的机器也可以很快的替换它。

4.hbase和hive和传统的关系型数据库的比较

    比起传统的关系型数据库,可以存储半结构化非结构化的数据,可以存储和处理更大级别的数据,提供高效的查询,对于稀疏数据的处理更好,具有更好的横向扩展性,免费开源性价比很高。但是不能支持非常好的事务特性,只支持行级的事务。只能通过行键来查询,表设计时难度更高。而mysql用来存储结构化的数据提供更好的事务控制。

    比起hive,hive只是在mapreduce上包了一层壳,本质上还是离线数据的处理的工具,实时查询性能有限,本质上是一个基于hadoop的数据仓库工具,不能支持行级别的新增修改和删除。hbase可以提供实时的数据的处理能力,适用于在线数据查询处理,本质上是一种数据库工具。

二、HBase的表设计

    HBase表的设计会直接影响hbase使用的效率和使用的便利性。

    HBase表的设计主要是列族的设计和行键的设计。

1、列族的设计

    在设计hbase表时候,注意以下事项:

    1.列族不宜过多,越少越好,官方推荐hbase表的列族不宜超过3个。列族设计过多,会非常消耗内存。

    2.经常要在一起查询的数据最好放在一个列族中,尽量的减少跨列族的数据访问。

    3.如果有多个列族,多个列族中的数据应该设计的比较均匀。

2、行键的设计

    hbase表中行键是唯一标识一个表中行的字段,所以行键设计的好不好将会直接影响未来对hbase的查询的性能和查询的便利性,所以hbase中的行键是需要进行设计的。

1.行键设计的基本原则

1>必须唯一

    行键必须唯一才能唯一标识数据。

2>必须有意义

    行键必须有意义,这样才能方便数据的查询。

3>最好是字符串类型

    所有语言平台针对字符串的操作是统一,数值类型在不同的系统中处理的方式可能不同。

4>最好具有固定的长度

    不同长度的数据可能会造成自然排序时排序的结果和预期不一致。

5>不宜过长

    行键最多可以达到64KB,但是最好是在10~100字节之间,最好不要超过16字节,越短越好,最好是8字节的整数倍。

    底层存储是利用key-value存储的,行键会多次出现,如果行键过长会导致大量存储空间被行键消耗掉。

2.行键的最佳实践

    综合来看以下两个原则是存在冲突的,所以在选择的时候只能使用其中之一,具体使用那个原则,要根据实际的业务以及集群的能力来考虑。

1>散列原则

    行键的设计将会影响数据在hbase表中的排序方式,这会影响region切分后的结果,要注意,在设计行键时应该让经常要查询的数据分散在不同的region中,防止某一个或某几个regionserver成为热点。

    当出现热点数据的时候,会导致整个集群的服务器相继挂机,最终导致集群崩溃,即雪崩。

2>有序原则

    行键的设计将会影响数据在hbase表中的排序方式,所以一种策略是将经常连续查询的条件作为行键最前面的数据,这样一来可以方便批量查询。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我是攻城师

一次bug死磕经历之Hbase堆内存小导致regionserver频繁挂掉 编辑

31070
来自专栏较真的前端

关于网络请求的面试题总结

27650
来自专栏Python自动化测试

Python接口自动化测试之动态数据处理(二十一)

在前面的知识基础上介绍了在接口自动化测试中,如何把数据分离出来,并且找到它的共同点,然后依据这个共同点来找到解决复杂问题的思想。我一直认为,程序...

38020
来自专栏枕边书

PHP 调用 Go 服务的正确方式 - Unix Domain Sockets

问题 可能是由于经验太少,工作中经常会遇到问题,探究和解决问题的过程总想记录一下,所以我写博客经常是问题驱动,首先介绍一下今天要解决的问题: 服务耦合 我们在开...

389110
来自专栏Java技术栈

让面试官颤抖的 HTTP 2.0 协议面试题

Http协议,对于拥有丰富开发经验的程序员来说简直是信手拈来,家常便饭。虽然天天见,但是对于http协议的问题,可能很多人在没有积极准备的情况下,不一定能很好的...

21920
来自专栏编程

centos系统安装Python

CentOS 6.5安装Python2.7.14 大家好,周五到了,明天终于可以好好的放松一下了,而且,还是三天的长假哦!自从毕业以后再也没有超过七天的假期了。...

259100
来自专栏Android 研究

Android启动流程——1序言、bootloader引导与Linux启动

前面讲解的很多内容都很抽象,所以本次系列决定"接点地气",准备开始讲解大家熟悉的Activity了,为了让我以及大家更好的理解Activity,我决定本系列...

31510
来自专栏Golang语言社区

PHP调用Go服务的正确方式 - Unix Domain Sockets

作者:枕边书 链接:http://www.cnblogs.com/zhenbianshu/p/7265415.html 來源:博客园 问题 可能是由于经验太少,...

47090
来自专栏前端架构与工程

webpack多页面开发与懒加载hash解决方案

本文内容只适用于webpack v1版本,webpack v2已经修复了hash计算规则。 之前讨论了webpack的hash与chunkhash的区别以及各...

22380
来自专栏机器学习算法与Python学习

Python:爬虫系列笔记(2) -- 基本了解及urllib的使用

1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓...

36560

扫码关注云+社区

领取腾讯云代金券