Hbase 中的每张表都通过行键(rowkey)按照一定的范围被分割成多个子表(HRegion),默认一个HRegion 超过256M 就要被分割成两个,由HRegionServer管理,管理哪些 HRegion 由 Hmaster 分配。HRegion 存取一个子表时,会创建一个 HRegion 对象,然后对表的每个列族(Column Family)创建一个 store 实例, 每个 store 都会有 0 个或多个 StoreFile 与之对应,每个 StoreFile 都会对应一个HFile,HFile 就是实际的存储文件,一个 HRegion 还拥有一个 MemStore实例。
命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase 有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default 表是用户默认使用的命名空间。
HBase 通过 Zookeeper 来做 Master 的高可用、RegionServer 的监控、元数据的入口以 及集群配置的维护等工作。
HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重 要。
HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限 定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。
用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会 自动为其加上该字段,其值为写入 HBase 的时间。
由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节数组形式存贮。
Master 是所有 Region Server 的管理者,其实现类为 HMaster,主要作用如下: 对于表的操作:create, delete, alter 对于 RegionServer的操作:分配 regions到每个RegionServer,接收心跳,监控每个 RegionServer的状态,负载均衡和故障转移。
Region Server 为 Region 的管理者,其实现类为 HRegionServer,主要作用如下: 对于数据的操作:get, put, delete; 对于 Region 的操作:splitRegion、compactRegion。 向Master发送心跳。
类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需 要声明具体的列。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关 系型数据库相比,HBase 能够轻松应对字段变更的场景。
Regin 的纵向拆分,按照表中列族的数量,由于会按照列族拆分,所以一张表查询数据时,如果只有一个列族时,就限定查找范围;一张表中的列族不建议过多。
提高数据的写入速度,MemStore是Regin中的内存区域。用于写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile。
预写日志,由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件 重建。
保存实际数据的物理文件,StoreFile 以 HFile 的形式存储在 HDFS 上。每个 Store 会 有一个或多个 StoreFile(HFile),数据在每个 StoreFile 中都是有序的。
用于存储表数据的文件格式,它的特点包括排序存储、键值对结构、压缩、数据块和索引、布隆过滤器等,提供了高效的数据存储和访问方式。一个StoreFile对应着一个HFile。
HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持。
HBase是一个在Hadoop上构建的分布式列存储系统,它提供了高可靠性、高性能和可伸缩性的数据存储和访问能力。下面是HBase的读写流程的详细解释:
需要注意的是,HBase的写入操作使用了写缓冲区(Write Buffer)来提高写入性能。数据首先被写入到内存中的写缓冲区,然后定期刷写到磁盘上的HFile中。读取操作首先检查内存中的读缓存(MemStore),如果数据在其中存在,则可以直接返回,避免了磁盘的访问。这种机制使得HBase能够提供高性能的写入和读取能力。
另外,HBase的数据存储是按照行键的字典序进行排序的,相邻的行键会存储在相邻的Region中,这样可以提高数据的局部性和访问效率。同时,HBase还支持数据的自动分片和负载均衡,可以根据数据的增长和负载情况自动进行Region的划分和迁移,保证系统的可伸缩性和负载均衡性。
在HBase中,Row Key(行键)的设计非常重要,它直接影响了数据的存储和访问性能。以下是一些设计Row Key的原则:
HBase散列性设计原则是在设计HBase表结构时需要考虑的重要因素,它能避免热点问题,即总是往存储最大行健的Region里写入数据,关乎数据在Region中的分布均衡和查询性能。以下是HBase散列性设计的原则:
1. 均匀散列分布: HBase会将相邻的RowKey存储在不同的Region中,为了实现负载均衡和避免“热点”问题,需要将数据尽可能均匀地分散到不同的Region中。使用散列函数将RowKey映射为一个固定长度的值,然后根据这个值来选择对应的Region。常用的散列函数有MD5、SHA,或者反转rowkey(处理1开头电话号时)。需要注意的是,散列函数的选择要保证散列值的随机性,以避免数据倾斜。
2. 加盐: 加盐处理散列是一种常见的方法,用于在散列过程中增加随机性,从而避免特定模式的数据分布和哈希碰撞问题。在HBase中,加盐处理散列可以通过以下方式实现:
随机盐值:为每个rowkey生成一个随机的盐值,可以是随机数,然后将盐值和原始RowKey组合在一起进行散列。这样相同的数据在加盐后会具有不同的散列值,从而实现更均匀的数据分布。
固定盐值:使用一个固定的盐值作为数据行的前缀或后缀,然后将组合后的值进行散列。这种方式可以保证相同数据行在不同表中的哈希值不同,避免数据倾斜。
在HBase中,相邻的行键会存储在相邻的Region中,因此,设计Row Key时应尽量考虑数据的访问模式,使得相关的数据能够存储在相邻的位置,以提高查询的效率。例如,可以使用时间戳或者具有时间戳的前缀作为Row Key,使得最新的数据能够存储在相邻的Region中。
Row Key的设计应该具有良好的可扩展性,能够应对数据的增长和负载的变化。避免使用过长的Row Key,以减少存储空间的占用和索引的开销。
根据实际的查询需求来设计Row Key,使得常用的查询能够高效地定位到数据。例如,如果经常需要根据某个属性进行范围查询,可以将该属性作为Row Key的一部分。
• 原则:在合理范围内能尽量少的减少列簇就尽量减少列簇,因为列簇是共享region的,每个列簇数据相差太大导致查询效率低下。 • 最优:将所有相关性很强的 key-value 都放在同一个列簇下,这样既能做到查询效率最高,也能保持尽可能少的访问不同的磁盘文件。以用户信息为例,可以将必须的基本信息存放在一个列族,而一些附加的额外信息可以放在另一列族。
在HBase中,compact
是用于合并和优化HBase表的操作。它的主要作用是减少存储空间的占用、提高读写性能并优化数据布局。
compact
操作会合并小的存储单元(HFile)为更大的块,从而减少存储文件的数量和大小。它可以通过合并删除标记(Cell Tombstone)并清理过时的数据来释放存储空间。此外,compact
还可以减少读取时需要扫描的文件数量,从而提高查询性能。
在HBase中,有两种类型的compact
操作:
compact
操作类型。它会合并相邻的HFile,并且只会触发那些小于预设阈值(一般为hbase.hstore.compaction.min.size
参数配置)的HFile进行合并。Minor Compact通常是在后台自动触发的,目的是将小的文件按照一定规则合并成较大的文件。compact
操作。它会合并整个HBase表甚至整个region的所有HFile。Major Compact可以进一步优化存储空间和读写性能,但可能需要更多的时间和资源。 Major Compact通常需要手动触发。主要区别:
一些常见的与compact
相关的配置参数包括:
hbase.hstore.compactionThreshold
: 触发Minor Compact的最小HFile数量。hbase.hstore.compaction.min.size
: 触发Minor Compact的HFile大小下限。hbase.hstore.compaction.max.size
: 触发Major Compact的HFile大小上限。hbase.hstore.compaction.min
和 hbase.hstore.compaction.max
: 用于设置不同级别的压缩策略,以控制Compaction的优先级。这些参数可以根据具体的业务需求和系统性能进行调优。
在HBase中,预分区(Pre-Splitting)是一种在创建HBase表时事先划分表的行键范围,将表的数据分布到多个Region(区域)中的操作。它的作用是提前将数据在多个Region之间均匀分布,以实现负载均衡和并行处理。
预分区的作用是为了解决当一个 region 的大小超过一定的阈值时就会发生 split 的操作,这个过程会消耗大量的磁盘及网络 I/O,因此我们虽然需要 split 带来的稳定的查询性能但是又不希望花费 split 带来的 I/O cost,预分区就可以很好的解决这个问题。
预分区可以通过 HBase shell 命令来创建,例如:
create 't1', 'f1', {SPLITS => ['10', '20', '30', '40', '50']}
这个命令将表 t1 分成了 6 个 region,其中前 5 个 region 的范围是 [0, 10), [10, 20), …, [40, 50),最后一个 region 的范围是 [50, +∞)。⁵
预分区的作用如下:
总之,HBase的预分区使得数据能够在不同的Region上进行并行处理,提高了系统的读写性能、负载均衡和扩展性。通过合理划分行键范围,可以确保数据在各个Region之间分布均匀,减少热点和负载不平衡的问题。
首先一点需要明白:Hbase是基于HDFS来存储的。 HDFS: • 一次性写入,多次读取。 • 保证数据的一致性。 • 主要是可以部署在许多廉价机器中,通过多副本提高可靠性,提供了容错和恢复机制。 HBase: • 瞬间写入量很大,数据库不好支撑或需要很高成本支撑的场景。 • 数据需要长久保存,且量会持久增长到比较大的场景。 • HBase不适用与有 join,多级索引,表关系复杂的数据模型。 • 大数据量(100s TB级数据)且有快速随机访问的需求。如:淘宝的交易历史记录。数据量巨大无容置疑,面向普通用户的请求必然要即时响应。 • 业务场景简单,不需要关系数据库中很多特性(例如交叉列、交叉表,事务,连接等等)。
某个小的时段内,对HBase的读写请求集中到极少数的Region上,导致这些region所在的RegionServer处理请求量骤增,负载量明显偏大,而其他的RgionServer明显空闲。
HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。
热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。
为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。常见的方法有以下这些:
• 加盐:在rowkey的前面增加随机数,使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。
• 哈希:哈希可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据
• 反转:第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题
• 时间戳反转:一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp
追加到key的末尾,例如[key][reverse_timestamp]
,[key]
的最新值可以通过scan [key]获得[key]
的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。
比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计[userId反转] [Long.Max_Value - timestamp]
,在查询用户的所有操作记录数据的时候,直接指定反转后的userId
,startRow是[userId反转][000000000000]
,stopRow是[userId反转][Long.Max_Value - timestamp]
如果需要查询某段时间的操作记录,startRow是[user反转][Long.Max_Value - 起始时间]
,stopRow是[userId反转][Long.Max_Value - 结束时间]
• HBase建表预分区:创建HBase表时,就预先根据可能的RowKey划分出多个region而不是默认的一个,从而可以将后续的读写操作负载均衡到不同的region上,避免热点现象。