大数据开发之 Hbase 面试题
1. Hbase 是怎么写数据的?
Client 写入 -> 存入 MemStore,一直到 MemStore 满 -> Flush 成一个 StoreFile,直至增长到一定阈值 -> 触发 Compact 合并操作 -> 多个 StoreFile 合并成一个 StoreFile,同时进行版本合并和数据删除 -> 当 StoreFiles Compact 后,逐步形成越来越大的 StoreFile -> 单个 StoreFile 大小超过一定阈值后(默认 10G),触发 Split 操作,把当前 Region Split 成 2 个 Region,Region 会下线,新 Split 出的 2 个孩子 Region 会被 HMaster 分配到相应的 HRegionServer 上,使得原先 1 个 Region 的压力得以分流到 2 个 Region 上
由此过程可知,HBase 只是增加数据,没大数据培训有更新和删除操作,用户的更新和删除都是逻辑层面的,在物理层面,更新只是追加操作,删除只是标记操作。
用户写操作只需要进入到内存即可立即返回,从而保证 I/O 高性能。
2. HDFS 和 HBase 各自使用场景
首先一点需要明白:Hbase 是基于 HDFS 来存储的。
HDFS:
一次性写入,多次读取。
保证数据的一致性。
主要是可以部署在许多廉价机器中,通过多副本提高可靠性,提供了容错和恢复机制。
HBase:
瞬间写入量很大,数据库不好支撑或需要很高成本支撑的场景。
数据需要长久保存,且量会持久增长到比较大的场景。
HBase 不适用与有 join,多级索引,表关系复杂的数据模型。
大数据量(100s TB 级数据)且有快速随机访问的需求。如:淘宝的交易历史记录。数据量巨大无容置疑,面向普通用户的请求必然要即时响应。
业务场景简单,不需要关系数据库中很多特性(例如交叉列、交叉表,事务,连接等等)。
3. Hbase 的存储结构
Hbase 中的每张表都通过行键(rowkey)按照一定的范围被分割成多个子表(HRegion),默认一个 HRegion 超过 256M 就要被分割成两个,由 HRegionServer 管理,管理哪些 HRegion 由 Hmaster 分配。HRegion 存取一个子表时,会创建一个 HRegion 对象,然后对表的每个列族(Column Family)创建一个 store 实例, 每个 store 都会有 0 个或多个 StoreFile 与之对应,每个 StoreFile 都会对应一个 HFile,HFile 就是实际的存储文件,一个 HRegion 还拥有一个 MemStore 实例。
4. 热点现象(数据倾斜)怎么产生的,以及解决方法有哪些
热点现象:
某个小的时段内,对 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 上,避免热点现象。
5. HBase 的 rowkey 设计原则
长度原则:100 字节以内,8 的倍数最好,可能的情况下越短越好。因为 HFile 是按照 keyvalue 存储的,过长的 rowkey 会影响存储效率;其次,过长的 rowkey 在 memstore 中较大,影响缓冲效果,降低检索效率。最后,操作系统大多为 64 位,8 的倍数,充分利用操作系统的最佳性能。
散列原则:高位散列,低位时间字段。避免热点问题。
唯一原则:分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问 的数据放到一块。
6. HBase 的列簇设计
原则:在合理范围内能尽量少的减少列簇就尽量减少列簇,因为列簇是共享 region 的,每个列簇数据相差太大导致查询效率低下。
最优:将所有相关性很强的 key-value 都放在同一个列簇下,这样既能做到查询效率最高,也能保持尽可能少的访问不同的磁盘文件。以用户信息为例,可以将必须的基本信息存放在一个列族,而一些附加的额外信息可以放在另一列族。
7. HBase 中 compact 用途是什么,什么时候触发,分为哪两种,有什么区别
在 hbase 中每当有 memstore 数据 flush 到磁盘之后,就形成一个 storefile,当 storeFile 的数量达到一定程度后,就需要将 storefile 文件来进行 compaction 操作。
Compact 的作用:
合并文件
清除过期,多余版本的数据
提高读写数据的效率
HBase 中实现了两种 compaction 的方式:minor and major. 这两种 compaction 方式的区别是:
Minor 操作只用来做部分文件的合并操作以及包括 minVersion=0 并且设置 ttl 的过期版本清理,不做任何删除数据、多版本数据的清理工作。
Major 操作是对 Region 下的 HStore 下的所有 StoreFile 执行合并操作,最终的结果是整理合并出一个文件。
评论