A Bigtable is a sparse, distributed, persistent multidimensional sorted map.
The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes.
HBase uses a data model very similar to that of Bigtable. Users store data rows in labelled tables. A data row has a sortable key and an arbitrary number of columns. The table is stored sparsely, so that rows in the same table can have crazily-varying columns, if the user likes.
the design of the row key is very important. The goal is to store data in such a way that related rows are near each other.rowKey字典序全局排序,设计目标是让相关的行排在一起,并且、让不相关的行尽量分散开、分布到多台集群节点机上去、避免热点、均衡负载,也就是高内聚低耦合。对于物联网,rowKey典型设计就是传感器ID+时间戳,利于查询某个时间点的传感数值、也利于查询某个传感器在一段时间的数据,唯一麻烦一点的是查询多个传感器,如果不算太多可以HBase Client直接撸,如果有点多的话,考虑Spark开发“大数据时代的HBase存储过程”。
列族也有类似的高内聚低耦合特征,一张表的不同列族基本没有相关性(这两个列族下的列没有相关性,不会被同时读写,这种情况才考虑分列族),但是一个列族内的列是读写相关的,往往会一起写入一起读取,比如传统RDB中典型的分为两个表设计的学生/成绩表,在HBase中可以“反范式”地设计为一个统一BigTable:
对于时序数据来说是一种解放和简化,传统RDB存储时序数据其实是一种误用或者:
一个学生的课程学分数据行一定与学生信息数据行在一起,更便于scan因为列族是真正的物理存储单元,它对应到Store,扫描一个列族内的列只要扫描一个Store:
Regions are the basic element of availability and distribution for tables, and are comprised of a Store per Column Family.A StoreFile is a facade of HFile. Store就是列族,HBase中列族是承上启下的关键概念,向上是Region/架构运维领域、向下是缓存和HFile/物理存储领域。
列只是个逻辑含义,仅仅是HBase这个多维Hash表的索引之一,算是和关系数据库差别最大的术语了、在HBase中其约束性弱得多、使用也相当的随意,创建表时可以一列不建,插入数据时随便写,不同行之间的列也可以差异很大,可以说没有半点约束作用,列名“元数据”仅仅存在于列族内部,具体来说是每一条Cell数据。正因为如此,HBase不仅可以支持百万列,也支持不同row之间差异巨大的列,列完全不符合传统的对“元数据”的理解和定义,它完全依附于列族,实质上更像是Cell数据的内容,甚至可以说是属于Cell的和版本号一样的“属性”而已,HBase根本不存在一个全局的或者表级别的schema,一张表有多少列连这张表自己都不知道,所以HBase某种程度上也可以说是schemaless,想知道一张表/一个列族到底有多少列?唯一的方式就是遍历这张表/列族的所有数据!HBase也没有类似关系库或者redis的那种总览数据的客户端或者schema查看器,话说回来,海量时序数据的查看方式是统计和图表,你根本不需要查看原始数据你也看不懂,你只要知道所有数据一直onLine就够了。如果你想有个简单的schema概览: it is your responsibility to keep track of the column names.
Cell是最终定位的数据单元,说HBase是多维Hash表,是指多层主键去映射定位Cell,按照实际的Cell范围来看这个映射:
表名 -> rowKey -> 列族 -> { Cell -> 列名 -> version -> 数据 }
version 是数据值的TimeStamp版本(By default, the timestamp represents the time on the RegionServer when the data was written),上述映射中的Cell实际上就是HBase的核心类:org.apache.hadoop.hbase.KeyValue,KeyValue中的Key包含rowKey、列族、列和timestamp,其中rowKey和列族在KeyValue之外也存在,但是列和timestamp在KeyValue之外根本就不存在,到列族这一维度映射就进入了一个具体的Cell,剩下的列和timestamp维度上的映射,都在这个具体Cell内部了,这就是为什么HBase的列可以这么随意,因为列只是数据。同时,rowKey和列族也都存在于每一个KeyValue中,KeyValue就代表实际物理存储,所以rowKey和列族大量冗余,特别是列族,因此列族名必须要短。
HBase可以理解为多层/多维HashMap,外层是有序LinkedHashMap,键是表名 -> rowKey -> 列族 ->,值KeyValue又是一个HashMap,它的Key是列名 -> version ->,定位到最终Value,这就是Multidimensional Map的含义,JSon表达:
逻辑上HBase和Json类似KeyValue包括keylength;valuelength;key;value,它本质是一个二进制通讯协议
KeyValue的内部结构非常像TCP通讯Region分区
表按照row行split分裂成多个Region,这些Region均衡分布在集群RS中,每个Region都是一段连续rowKey范围的数据、都具备start key 和 end key属性。Region会包含多个Store/列族,Store又包含一个MemStore和0~N个StoreFile,一张表不同列族的数据,物理上是分开存储、各自访问的,每个列族都是独立的(独立存储rowKey)。一张表会包含多个Region分区、这些Region由多台RS管理,所以说Region是数据读写的负载均衡机制,首次创建一张表会为其分派一个Region,对这张表的初期读写都会落到这一个Region所属的RS节点机上,因此会有Pre-splitting预分裂的需求。
HBase的设计更偏好每台RS上有少量(20~200)且比较大(5~20G)的Region,控制Region数量的考量是:
1、MSLAB (MemStore-local allocation buffer) 对于每个MemStore也就是每个Region的列族默认都需要2M内存来做缓存(没有数据也有这个缓存,所以说StoreFile可能没有但是MemStore一定有),假设两个列族、一台RS有1k个Region就至少需要大概3.9G的堆空间也就是4个G,这个内存占用就有点过多了。
2、如果所有Region都以大概相同的速率写入,那么过多Region的整体内存占用会强制进行频繁的小数据量flush、以及compaction压缩,这是你不想看到的。比如写入1k个Region(一个列族),RS的堆内存配置为5G,那么一旦整体内存占用达到5G就会强制Flush最大的Region,但是此时所有Region都有大概5M待Flush的数据,所以flush一个、随后又写入大概5M的数据就会再次触发flush、一个接一个以此类推,系统忙于频繁的小数据量flush,这种情况需要避免,这是需要控制每个RS的Region数量的主要因素。
3、Master如果处理过多的Region会手忙脚乱,它会花费大量性能分配、移动成批的Region,这是因为它对zk的重度依赖,而zk的异步性并不好(导致大量阻塞)
LoadBalancer也像AkkaRegion如何分派给RS
当HBase启动时:
1、Master在启动之前调用 AssignmentManager
2、 AssignmentManager在hbase:meta表查看现有的Region分派情况
3、都正常则保持现状
4、如果有RS不在线,则调用 LoadBalancerFactory 重新分派Region
5、如果需要则更新hbase:meta表
当RS失效时:
1、Region立即变为不可用
2、Master探测到RS下线
3、Region会重新分派,和初次启动时一样
4、正在进行的查询会重试,不会丢失
5、在以下时间内,操作会转移到一个新的RS:
ZooKeeper session timeout + split time + assignment/replay time
所以HBase的RS和Region是“最终本地化”的,哈哈有点类似最终一致,比如当RS宕机失效,另一个RS会被分派不属于自己的StoreFile也就是可能不在本机,但是随着新数据持续写入、或者当表Compaction时,StoreFile会被重写、变成本地。
Region分裂
预分裂:对于初次创建的空表HBase无法在其rowKey分布空间中创建分裂点(create the split points within the row key space.),所以新表都只有一个Region。HBase Client提供工具去做预分裂,支持你在建表时即指定分裂点、预分裂可以确保初始负载更加均衡合理,如果你了解你的键值空间,你就应该作预分裂。不过预分裂也有风险,由于数据倾斜,这些区域可能不能真正均匀地分配负载,或者存在非常热或较大的行。如果初始的Region分裂点集选择不当,可能会导致负载分布不均匀,从而限制集群的性能。
你可以使用RS的低倍数作为预分裂Region个数,之后让自动分裂去处理。预分裂的关键是计算表的分裂点,可以使用RegionSplitter工具,RegionSplitter会用一个可插拔的SplitAlgorithm( HexStringSplit and UniformSplit are two predefined algorithms.)创建分裂点,如果rowKey有一个16进制字符串前缀可以使用前者,后者假设键空间是随机字节数组,然后平均分割它们,也可以自定义算法。
网友评论