HBase

2023-10-17 15:38:48 浏览数 (2)

HBase的特点:

  1)大:一个表可以有数十亿行,上百万列;

  2)无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一张表中不同的行可以有截然不同的列;

  3)面向列:面向列(族)的存储和权限控制,列(族)独立检索;

  4)稀疏:空(null)列并不占用存储空间,表可以设计的非常稀疏;

  5)数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;

  6)数据类型单一:Hbase中的数据都是字符串,没有类型。

HBase中通过row和columns确定的为一个存贮单元称为cell。

Cell:由{row key, column(=<family> <label>), version}唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。

  Hbase是一个能适应联机业务的数据库系统。

  物理存储:hbase的持久化数据是将数据存储在HDFS上

  存储管理:一个表是划分为很多region的,这些region分布式地存放在很多regionserver上Region内部还可以划分为store,store内部有memstore和storefile

  版本管理:hbase中的数据更新本质上是不断追加新的版本,通过compact操作来做版本间的文件合并Region的split

  集群管理:ZooKeeper HMaster HRegionServer

1 HBase存储结构

HMaster

  1. 监控 RegionServer   2. 处理 RegionServer 故障转移   3. 处理元数据的变更   4. 处理 region 的分配或移除   5. 在空闲时间进行数据的负载均衡   6. 通过 Zookeeper 发布自己的位置给客户端

RegionServer

  1. 负责存储 HBase 的实际数据   2. 处理分配给它的 Region   3. 刷新缓存到 HDFS   4. 维护 HLog   5. 执行压缩   6. 负责处理 Region 分片

Write-Ahead logs(HLog)

  HBase 的修改记录, 当对 HBase 读写数据的时候, 数据不是直接写进磁盘, 它会在内存中保留一段时间(时间以及数据量阈值可以设定) 。 但把数据保存在内存中可能有更高的概率引起数据丢失, 为了解决这个问题, 数据会先写在一个叫做 Write-Ahead logfile 的文件中, 然后再写入内存中。 所以在系统出现故障的时候, 数据可以通过这个日志文件重建。

HFile

  这是在磁盘上保存原始数据的实际的物理文件, 是实际的存储文件。

Store File

  HFile 存储在 Store 中, 一个 Store 对应 HBase 表中的一个列族。

MemStore

  顾名思义, 就是内存存储, 位于内存中, 用来保存当前的数据操作, 所以当数据保存在 WAL 中之后, RegsionServer 会在内存中存储键值对。

Region

  Hbase 表的分片, HBase 表会根据 RowKey 值被切分成不同的 region 存储在RegionServer 中, 在一个 RegionServer 中可以有多个不同的 region。

2 Hbase写数据过程

  Client写入

 -> 先将数据存入HLog

 -> 然后存入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 高性能。

3 RowKey设计原则

总的原则:避免热点现象,提⾼读写性能;

长度原则: 100 字节以内, 8 的倍数最好, 可能的情况下越短越好。 因为 HFile是按照 keyvalue 存储的, 过长的 rowkey 会影响存储效率; 其次, 过长的 rowkey在 memstore 中较大, 影响缓冲效果, 降低检索效率。 最后, 操作系统大多为 64位, 8 的倍数, 充分利用操作系统的最佳性能。

散列原则: 高位散列, 低位时间字段。 避免热点问题。

唯一原则: 分利用这个排序的特点, 将经常读取的数据存储到一块, 将最近可能会被访问 的数据放到一块。

4 RowKey如何设计

  电信案例:查询某个人(手机号)某年某月某日的通话详情

  1. 预分区

  (1) 评估未来半年到一年的数据增长,不让其自动分区(10G)

  (2) 确定分区键

     00| 01| 02| …

     000| 001| …

  1. 设计RowKey

  (1) 确定分区号 (散列性)

    手机号%分区数  不够散列

     (手机号 年月日)%分区数   按照月份、年进行查询 不方便

    (手机号 年月)%分区数

  (2) 拼接字段 (唯一性、长度)

    XX_手机号_时间戳

    XX_手机号_年月日 时分秒

    XX_时间戳_手机号

    XX_年月日 时分秒_手机号

  (3) 校验

    手机号:13412341234   通话时间:2021-09-07

    XX_手机号_年月日 时分秒

    startRow:05_13412341234_2021-09-07     stopRow :05_13412341234_2021-09-08         05_13412341234_2021-09-07

    XX_年月日 时分秒_手机号

    startRow:05_2021-09-07 00:00:00_13412341234     stopRow :05_2021-09-08 00:00:00_13412341234

    手机号:13412341234  通话时间:2021-09  2021-11

    XX_手机号_年月日 时分秒

    startRow:05_13412341234_2021-09     stopRow :05_13412341234_2021-09         05_13412341234_2021-10

    startRow:03_13412341234_2021-10     stopRow :03_13412341234_2021-11

    startRow:04_13412341234_2021-11     stopRow :04_13412341234_2021-12

5 热点现象( 数据倾斜) 怎么产生的, 以及解决方法有哪些

5.1热点现象

  某个小的时段内, 对 HBase 的读写请求集中到极少数的 Region 上, 导致这些region 所在的 RegionServer 处理请求量骤增, 负载量明显偏大, 而其他的RgionServer 明显空闲。

5.2 热点现象出现的原因

  HBase 中的行是按照 rowkey 的字典顺序排序的, 这种设计优化了 scan 操作, 可以将相关的行以及会被一起读取的行存取在临近位置, 便于 scan。 然而糟糕的rowkey 设计是热点的源头

热点发生在大量的 client 直接访问集群的一个或极少数个节点( 访问可能是读,写或者其他操作) 。 大量访问会使热点 region 所在的单个机器超出自身承受能力, 引起性能下降甚至 region 不可用, 这也会影响同一个 RegionServer 上的其他 region, 由于主机无法服务其他 region 的请求。

5.3 热点现象解决办法

  为了避免写热点, 设计 rowkey 使得不同行在同一个 region, 但是在更多数据情况下, 数据应该被写入集群的多个 region, 而不是一个。 常见的方法有以下这些:

1. 加盐: 在 rowkey 的前面增加随机数, 使得它和之前的 rowkey 的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的 region 的数量一致。 加盐之后的 rowkey 就会根据随机生成的前缀分散到各个 region 上,以避免热点。

2. 哈希: 哈希可以使负载分散到整个集群, 但是读却是可以预测的。 使用确定的哈希可以让客户端重构完整的 rowkey, 可以使用 get 操作准确获取某一个行数据

3. 反转: 第三种防止热点的方法时反转固定长度或者数字格式的 rowkey。这样可以使得 rowkey 中经常改变的部分(最没有意义的部分) 放在前面。这样可以有效的随机 rowkey, 但是牺牲了 rowkey 的有序性。 反转 rowkey的例子以手机号为 rowkey, 可以将手机号反转后的字符串作为 rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题

4. 时间戳反转: 一个常见的数据处理问题是快速获取数据的最近版本, 使用反转的时间戳作为 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 - 结束时间]

5. HBase 建表预分区: 创建 HBase 表时, 就预先根据可能的 RowKey 划分出多个 region 而不是默认的一个, 从而可以将后续的读写操作负载均衡到不同的 region 上, 避免热点现象。

6 HBase 的列簇设计

  原则: 在合理范围内能尽量少的减少列簇就尽量减少列簇, 因为列簇是共享region 的, 每个列簇数据相差太大导致查询效率低下。

  最优: 将所有相关性很强的 key-value 都放在同一个列簇下, 这样既能做到查询效率最高, 也能尽可能的访问相同的磁盘文件。 以用户信息为例, 可以将必须的基本信息存放在一个列族, 而一些附加的额外信息可以放在另一列族。

7 HBase 中 compact 用途是什么, 什么时候触发, 分为哪两种, 有什么区别

  在 hbase 中每当有 memstore 数据 flush 到磁盘之后, 就形成一个 storefile,当 storeFile 的数量达到一定程度后, 就需要将 storefile 文件来进行compaction 操作。

Compact 的作用:

1. 合并文件 2. 清除过期, 多余版本的数据 3. 提高读写数据的效率

HBase 中实现了两种 compaction 的方式: minor and major

  Minor 操作只用来做部分文件的合并操作以及包括 minVersion=0 并且设置 ttl 的过 期版本清理, 不做任何删除数据、 多版本数据的清理工作。

  Major 操作是对 Region 下的 HStore 下的所有 StoreFile 执行合并操作, 最终的结果 是整理合并出一个文件。

8 HBase中scan和get的功能以及实现的异同?

1)按指定RowKey 获取唯一一条记录,get方法(org.apache.hadoop.hbase.client.Get)

  Get 的方法处理分两种 : 设置了ClosestRowBefore 和没有设置ClosestRowBefore的rowlock。主要是用来保证行的事务性,即每个get 是以一个row 来标记的。一个row中可以有很多family 和column。

2)按指定的条件获取一批记录,scan方法(org.apache.Hadoop.hbase.client.Scan)实现条件查询功能使用的就是scan 方式。

  (1)scan 可以通过setCaching 与setBatch 方法提高速度(以空间换时间);

setCache用于设置缓存,即设置一次RPC请求可以获取多行数据。对于缓存操作,如果行的数据量非常大,多行数据有可能超过客户端进程的内存容量,由此引入批量处理这一解决方案。

setBatch 用于设置批量处理,批量可以让用户选择每一次ResultScanner实例的next操作要取回多少列,例如,在扫描中设置setBatch(5),则一次next()返回的Result实例会包括5列。如果一行包括的列数超过了批量中设置的值,则可以将这一行分片,每次next操作返回一片,当一行的列数不能被批量中设置的值整除时,最后一次返回的Result实例会包含比较少的列,如,一行17列,batch设置为5,则一共返回4个Result实例,这4个实例中包括的列数分别为5、5、5、2。

组合使用扫描器缓存和批量大小,可以让用户方便地控制扫描一个范围内的行键所需要的RPC调用次数。Cache设置了服务器一次返回的行数,而Batch设置了服务器一次返回的列数。

  假如我们建立了一张有两个列族的表,添加了10行数据,每个行的每个列族下有10列,这意味着整个表一共有200列(或单元格,因为每个列只有一个版本),其中每行有20列。

  ① Batch参数决定了一行数据分为几个Result,它只针对一行数据,Batch再大,也只能将一行的数据放入一个Result中。所以当一行数据有10列,而Batch为100时,也只能将一行的所有列都放入一个Result,不会混合其他行;

  ② 缓存值决定一次RPC返回几个Result,根据Batch划分的Result个数除以缓存个数可以得到RPC消息个数(之前定义缓存值决定一次返回的行数,这是不准确的,准确来说是决定一次RPC返回的Result个数,由于在引入Batch之前,一行封装为一个Result,因此定义缓存值决定一次返回的行数,但引入Batch后,更准确的说法是缓存值决定了一次RPC返回的Result个数);

RPC请求次数 = (行数 * 每行列数) / Min(每行的列数,批量大小) / 扫描器缓存

  下图展示了缓存和批量两个参数如何联动,下图中有一个包含9行数据的表,每行都包含一些列。使用了一个缓存为6、批量大小为3的扫描器,需要三次RPC请求来传送数据:

  (2)scan 可以通过setStartRow 与setEndRow 来限定范围([start,end)start 是闭区间,end 是开区间)。范围越小,性能越高。

  (3)scan 可以通过setFilter 方法添加过滤器,这也是分页、多条件查询的基础。

9 每天百亿数据存入HBase,如何保证数据的存储正确和在规定的时间里全部录入完毕,不残留数据?

9.1 需求分析:

  1)百亿数据:证明数据量非常大;

  2)存入HBase:证明是跟HBase的写入数据有关;

  3)保证数据的正确:要设计正确的数据结构保证正确性;

  4)在规定时间内完成:对存入速度是有要求的。

9.2 解决思路:

  1)数据量百亿条,什么概念呢?假设一整天60x60x24 = 86400秒都在写入数据,那么每秒的写入条数高达100万条,HBase当然是支持不了每秒百万条数据的,所以这百亿条数据可能不是通过实时地写入,而是批量地导入。批量导入推荐使用BulkLoad方式(推荐阅读:Spark之读写HBase),性能是普通写入方式几倍以上

  2)存入HBase:普通写入是用JavaAPI put来实现,批量导入推荐使用BulkLoad;

  3)保证数据的正确:这里需要考虑RowKey的设计、预建分区和列族设计等问题

  4)在规定时间内完成也就是存入速度不能过慢,并且当然是越快越好,使用BulkLoad。

10 HBase优化方法

10.1 减少调整

  减少调整这个如何理解呢?HBase中有几个内容会动态调整,如region(分区)、HFile,所以通过一些方法来减少这些会带来I/O开销的调整。

· Region

  如果没有预建分区的话,那么随着region中条数的增加,region会进行分裂,这将增加I/O开销,所以解决方法就是根据RowKey设计来进行预建分区,减少region的动态分裂

Region如何预建分区?

  预分区的目的主要是在创建表的时候指定分区数,提前规划表有多个分区,以及每个分区的区间范围,这样在存储的时候rowkey按照分区的区间存储,可以避免region热点问题。

  方案1:shell 方法

    create ‘tb_splits’, {NAME => ‘cf’,VERSIONS=> 3},{SPLITS => [‘10’,‘20’,‘30’]}

  方案2: JAVA程序控制

    取样,先随机生成一定数量的rowkey,将取样数据按升序排序放到一个集合里;

    根据预分区的region个数,对整个集合平均分割,即是相关的splitKeys;

    HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][]splitkeys)可以指定预分区的splitKey,即是指定region间的rowkey临界值。

· HFile

  HFile是数据底层存储文件,在每个memstore进行刷新时会生成一个HFile,当HFile增加到一定程度时,会将属于一个region的HFile进行合并,这个步骤会带来开销但不可避免,但是合并后HFile大小如果大于设定的值,那么HFile会重新分裂。为了减少这样的无谓的I/O开销,建议估计项目数据量大小,给HFile设定一个合适的值,减少HFile重新分裂次数

10.2 减少启停

  数据库事务机制就是为了更好地实现批量写入,较少数据库的开启关闭带来的开销,那么HBase中也存在频繁开启关闭带来的问题。

关闭自动Compaction,在闲时进行手动Compaction

  因为HBase中存在Minor Compaction和Major Compaction,也就是对HFile进行合并,所谓合并就是I/O读写,大量的HFile进行肯定会带来I/O开销,甚至是I/O风暴,所以为了避免这种不受控制的意外发生,建议关闭自动Compaction,在闲时进行compaction。

批量数据写入时采用BulkLoad

  如果通过HBase-Shell或者JavaAPI的put来实现大量数据的写入,那么性能差是肯定并且还可能带来一些意想不到的问题,所以当需要写入大量离线数据时建议使用BulkLoad

10.3 减少数据量

  虽然我们是在进行大数据开发,但是如果可以通过某些方式在保证数据准确性同时减少数据量,何乐而不为呢?

开启过滤,提高查询速度

  开启BloomFilter,BloomFilter是列族级别的过滤,在生成一个StoreFile同时会生成一个MetaBlock,用于查询时过滤数据

使用压缩:一般推荐使用Snappy和LZO压缩

10.4 合理设计RowKey和ColumnFamily

  在一张HBase表格中RowKey和ColumnFamily的设计是非常重要,好的设计能够提高性能和保证数据的准确性

  RowKey设计:应该具备以下几个属性:

  散列性:散列性能够保证相同相似的rowkey聚合,相异的rowkey分散,有利于查询。

  简短性:rowkey作为key的一部分存储在HFile中,如果为了可读性将rowKey设计得过长,那么将会增加存储压力。

   唯一性:rowKey必须具备明显的区别性。

  业务性:举例来说:假如我的查询条件比较多,而且不是针对列的条件,那么rowKey的设计就应该支持多条件查询。如果我的查询要求是最近插入的数据优先,那么rowKey则可以采用叫上Long.Max-时间戳的方式,这样rowKey就是递减排列。

列族的设计:

  列族的设计需要看应用场景

  多列族设计的优劣:

  优势:HBase中数据时按列进行存储的,那么查询某一列族的某一列时就不需要全盘扫描,只需要扫描某一列族,减少了读I/O;其实多列族设计对减少的作用不是很明显,适用于读多写少的场景

  劣势:降低了写的I/O性能。原因如下:数据写到store以后是先缓存在memstore中,同一个region中存在多个列族则存在多个store,每个store都一个memstore,当其实memstore进行flush时,属于同一个region的store中的memstore都会进行flush,增加I/O开销。

11 HRegionServer宕机如何处理?

  1)ZooKeeper会监控HRegionServer的上下线情况,当ZK发现某个HRegionServer宕机之后会通知HMaster进行失效备援;

  2)该HRegionServer会停止对外提供服务,就是它所负责的region暂时停止对外提供服务;

  3)HMaster会将该HRegionServer所负责的region转移到其他HRegionServer上,并且会对HRegionServer上存在memstore中还未持久化到磁盘中的数据进行恢复;

  4) 这个恢复的工作是由WAL重播来完成,这个过程如下:

  · wal实际上就是一个文件,存在/hbase/WAL/对应RegionServer路径下。

  · 宕机发生时,读取该RegionServer所对应的路径下的wal文件,然后根据不同的region切分成不同的临时文件recover.edits。

   · 当region被分配到新的RegionServer中,RegionServer读取region时会进行是否存在recover.edits,如果有则进行恢复。

12 HBase读写流程?

读:

   ① HRegionServer保存着meta表以及表数据,要访问表数据,首先Client先去访问zookeeper,从zookeeper里面获取meta表所在的位置信息,即找到这个meta表在哪个HRegionServer上保存着。

   ② 接着Client通过刚才获取到的HRegionServer的IP来访问Meta表所在的HRegionServer,从而读取到Meta,进而获取到Meta表中存放的元数据。

   ③ Client通过元数据中存储的信息,访问对应的HRegionServer,然后扫描所在HRegionServer的Memstore和Storefile来查询数据。

   ④ 最后HRegionServer把查询到的数据响应给Client。

写:

   ① Client先访问zookeeper,找到Meta表,并获取Meta表元数据。

   ② 确定当前将要写入的数据所对应的HRegion和HRegionServer服务器。

   ③ Client向该HRegionServer服务器发起写入数据请求,然后HRegionServer收到请求并响应。

   ④ Client先把数据写入到HLog,以防止数据丢失。

   ⑤ 然后将数据写入到Memstore。

   ⑥ 如果HLog和Memstore均写入成功,则这条数据写入成功

   ⑦ 如果Memstore达到阈值,会把Memstore中的数据flush到Storefile中。

   ⑧ 当Storefile越来越多,会触发Compact合并操作,把过多的Storefile合并成一个大的Storefile。

   ⑨ 当Storefile越来越大,Region也会越来越大,达到阈值后,会触发Split操作,将Region一分为二。

13 Hbase中的memstore是用来做什么的?

  hbase为了保证随机读取的性能,所以hfile里面的rowkey是有序的。当客户端的请求在到达regionserver之后,为了保证写入rowkey的有序性,所以不能将数据立刻写入到hfile中,而是将每个变更操作保存在内存中,也就是memstore中。memstore能够很方便的支持操作的随机插入,并保证所有的操作在内存中是有序的。当memstore达到一定的量之后,会将memstore里面的数据flush到hfile中,这样能充分利用hadoop写入大文件的性能优势,提高写入性能。

  由于memstore是存放在内存中,如果regionserver因为某种原因死了,会导致内存中数据丢失。所有为了保证数据不丢失,hbase将更新操作在写入memstore之前会写入到一个write ahead log(WAL)中。WAL文件是追加、顺序写入的,WAL每个regionserver只有一个,同一个regionserver上所有region写入同一个的WAL文件。这样当某个regionserver失败时,可以通过WAL文件,将所有的操作顺序重新加载到memstore中。

14 HBase在进行模型设计时重点在什么地方?一张表中定义多少个Column Family最合适?为什么?

Column Family的个数具体看表的数据,一般来说划分标准是根据数据访问频度,如一张表里有些列访问相对频繁,而另一些列访问很少,这时可以把这张表划分成两个列族,分开存储,提高访问效率。

15 如何提高HBase客户端的读写性能?

  ① 开启bloomfilter过滤器,开启bloomfilter比没开启要快3、4倍

  ② Hbase对于内存有特别的需求,在硬件允许的情况下配足够多的内存给它

  ③ 通过修改hbase-env.sh中的

    export HBASE_HEAPSIZE=3000 #这里默认为1000m

  ④ 增大RPC数量

    通过修改hbase-site.xml中的hbase.regionserver.handler.count属性,可以适当的放大RPC数量,默认值为10有点小。

16 HBase集群安装注意事项?

  ① HBase需要HDFS的支持,因此安装HBase前确保Hadoop集群安装完成;   ② HBase需要ZooKeeper集群的支持,因此安装HBase前确保ZooKeeper集群安装完成;   ③ 注意HBase与Hadoop的版本兼容性;   ④ 注意hbase-env.sh配置文件和hbase-site.xml配置文件的正确配置;   ⑤ 注意regionservers配置文件的修改;   ⑥ 注意集群中的各个节点的时间必须同步,否则启动HBase集群将会报错;

17 如何解决HBase中region太小和region太大带来的冲突?

  Region过大会发生多次compaction,将数据读一遍并重写一遍到hdfs 上,占用io,region过小会造成多次split,region 会下线,影响访问服务,最佳的解决方法是调整hbase.hregion. max.filesize 为256m。

18 Phoenix二级索引

HBase提供了检索数据的功能,不过原有系统仅提供了通过rowkey检索数据的功能,过于单⼀,不灵活,⼀旦查询条件改变了往往涉及到要全表扫描过滤,极⼤浪费机器物理资源,又达不到实时的⼀个效果。 HBase⼆级索引功能解决了原有HBase系统中仅能够通过rowkey检索数据的问题,使得⽤户能够指定多种条件,在HBase表中进⾏数据的实时检索与统计。

HBase 的二级索引是由社区开发的,不是原生的 HBase 功能。它基于 HBase 的协处理器(Coprocessor)机制实现,使用单独的表存储索引数据。 在 HBase 中,每个表可以定义多个协处理器,它们可以在 HBase 的主节点或者 region server 上执行。二级索引的实现就是在协处理器中完成的。

具体来说,当创建二级索引时,HBase 会自动创建一个单独的表来存储索引数据,并使用协处理器将写入原表的数据同步到索引表中。查询数据时,HBase 会首先使用二级索引表定位符合条件的行键,然后使用行键查找原始表中的数据。

值得注意的是,HBase 的二级索引需要额外的存储空间,并且在写入和更新数据时需要维护索引表。因此,在设计表结构时需要仔细权衡索引对系统性能的影响和索引所提供的查询优化效果。

  Apache Phoenix 是建立在 HBase 之上的一个 SQL 数据库层,它提供了 SQL 接口和大部分传统 SQL 数据库的功能。Phoenix 提供了二级索引的功能,这里简要分析一下 Phoenix 二级索引的实现原理。

  Phoenix 的二级索引是基于 HBase 中的二级索引实现的。具体来说,Phoenix 会在 HBase 中为每个二级索引创建一个单独的表,该表包含索引列、原始表行键和其它需要索引的列。这个表的行键是索引列的值,而值则是一个或多个指向原始表的行键的引用。这些引用指向的行包含原始表的数据列及其值。通过这种方式,Phoenix 可以快速地定位原始表中包含特定数据的行。

当在 Phoenix 中创建二级索引时,Phoenix 会自动创建一个与原始表相关的二级索引表,并将索引数据同步到该表中。在查询数据时,Phoenix 会使用二级索引表定位原始表中符合条件的行,然后从原始表中读取相应的数据。

  值得注意的是,Phoenix 二级索引会带来一定的存储和维护成本。每个索引表都需要占用额外的存储空间,并且需要在写入和更新数据时维护这些索引表。因此,在创建二级索引时,需要仔细权衡索引对系统性能的影响和索引所提供的查询优化效果。

19 HDFS和HBase各⾃使⽤场景

   Hbase是基于HDFS来存储的。

HDFS:

  (1) ⼀次性写⼊,多次读取。

  (2) 保证数据的⼀致性。

  (3) 主要是可以部署在许多廉价机器中,通过多副本提⾼可靠性,提供了容错和恢复机制。

HBase:

  (1)瞬间写⼊量很⼤,数据库不好⽀撑或需要很⾼成本⽀撑的场景。

  (2)数据需要长久保存,且量会持久增长到⽐较⼤的场景

  (3)HBase不适⽤与有join,多级索引,表关系复杂的数据模型

  (4)⼤数据量 (100s TB级数据) 且有快速随机访问的需求。

    如:淘宝的交易历史记录。数据量巨⼤⽆容置疑,⾯向普通⽤户的请求必然要即时响应。

  (5)容量的优雅扩展

    ⼤数据的驱使,动态扩展系统容量。例如: webPage DB。

  (6)业务场景简单,不需要关系数据库中很多特性(例如交叉列、交叉表,事务,连接等等)

  (7)优化⽅⾯:合理设计rowkey。

20 hbase.hregion.max.filesize应该设置多少合适

  Hbase中数据⼀开始会写⼊memstore,当memstore满64MB以后,会flush到disk上⽽成为storefile。当storefile数量超过3时,会启动compaction过程将它们合并为⼀个storefile。这个过程中会删除⼀些timestamp过期的数据,⽐如update的数据。⽽当合并后的storefile⼤⼩⼤于hfile默认最⼤值时,会触发split动作,将它切分成两个region。

hbase.hregion.max.filesize 是 HBase 中控制一个 Region 中存储数据的最大大小的参数。默认情况下,该参数的值为 1GB,即一个 Region 中最多可以存储 1GB 的数据。

  在实际生产环境中,hbase.hregion.max.filesize 的设置需要考虑多个因素,例如硬件配置、负载、数据访问模式等。下面是一些常见的考虑因素:

硬件配置:如果集群的硬件配置较弱,那么设置较小的 hbase.hregion.max.filesize 可以减轻负载,避免过度压缩系统资源。

数据访问模式:如果数据访问模式为 OLTP(联机事务处理),则需要快速读取和写入小数据量的记录,因此可以考虑使用较小的 Region 大小,以提高读写性能。如果数据访问模式为 OLAP(联机分析处理),则需要快速读取大数据量的记录,因此可以考虑使用较大的 Region 大小,以提高扫描性能。

数据分布:如果数据分布不均匀,那么设置较小的 hbase.hregion.max.filesize 可以平衡数据分布,避免某些 Region 过度加载数据,导致热点问题。

存储格式:如果使用的是 HBase 的压缩格式(如 Snappy、Gzip 等),则需要考虑压缩比率。通常情况下,压缩后的数据文件会变得更小,因此可以设置较大的 hbase.hregion.max.filesize,以减少 Region 的数量和管理开销。

根据以上因素的综合考虑,可以尝试将 hbase.hregion.max.filesize 的值设置为 1-10GB 左右。 需要注意的是,Region 的大小设置过大会影响数据迁移、Region 分裂等操作的性能,同时还可能会导致内存不足、负载过高等问题。因此,应该根据具体的业务场景和实际测试结果,选择合适的 hbase.hregion.max.filesize 值。

21 autoflush=false的影响

  ⽆论是官⽅还是很多blog都提倡为了提⾼hbase的写⼊速度⽽在应⽤代码中设置autoflush=false,然后在在线应⽤中应该谨慎进⾏该设置,原因如下:

  (1)autoflush=false的原理是当客户端提交delete或put请求时,将该请求在客户端缓存,直到数据超过2M(hbase.client.write.buffer决定)或⽤户执⾏了hbase.flushcommits()时才向regionserver提交请求。因此即使htable.put()执⾏返回成功,也并⾮说明请求真的成功了。假如还没有达到该缓存⽽client崩溃,该部分数据将由于未发送到regionserver⽽丢失。这对于零容忍的在线服务是不可接受的。

  (2)autoflush=true虽然会让写⼊速度下降2-3倍,但是对于很多在线应⽤来说这都是必须打开的,也正是hbase为什么让它默认值为true的原因。当该值为true时,每次请求都会发往regionserver,⽽regionserver接收到请求后第⼀件事就是写hlog,因此对io的要求是⾮常⾼的,为了提⾼hbase的写⼊速度,应该尽可能⾼地提⾼io吞吐量,⽐如增加磁盘、使⽤raid卡、减少replication因⼦数等。

22 对于传统关系型数据库中的⼀张table,在业务转换到hbase上建模时,从性能的⾓度应该 如何设置family(列族)和qualifier(列)呢?

22.1 最极端的:

  ①每⼀列都设置成⼀个family

  ②⼀个表仅有⼀个family,所有列都是其中的⼀个qualifier,那么有什么区别呢?

22.1.1 从读的⽅⾯考虑:

  a. family越多,那么获取每⼀个cell数据的优势越明显,因为io和⽹络都减少了。如果只有⼀个family,那么每⼀次读都会读取当前rowkey的所有数据,⽹络和io上会有⼀些损失。

  b. 当然如果要获取的是固定的⼏列数据,那么把这⼏列写到⼀个family中⽐分别设置family要更好,因为只需⼀次请求就能拿回所有数据。

22.1.2 从写的⾓度考虑:

  a. 内存⽅⾯来说,对于⼀个Region,会为每⼀个表的每⼀个Family分配⼀个Store,⽽每⼀个Store,都会分配⼀个MemStore,所以更多的family会消耗更多的内存

  b. 从flush和compaction⽅⾯说,⽬前版本的hbase,在flush和compaction都是以region为单位的,也就是说当⼀个family达到flush条件时,该region的所有family所属的memstore都会flush⼀次,即使memstore中只有很少的数据也会触发flush⽽⽣成⼩⽂件。 这样就增加了compaction发⽣的机率,⽽compaction也是以region为单位的,这样就很容易发⽣compaction风暴从⽽降低系统的整体吞吐量。

  c. 从split⽅⾯考虑,由于hfile是以family为单位的,因此对于多个family来说,数据被分散到了更多的hfile中,减⼩了split发⽣的机率。这是把双刃剑。更少的split会导致该region的体积⽐较⼤,由于balance是以region的数⽬⽽不是⼤⼩为单位来进⾏的,因此可能会导致balance失效。⽽从好的⽅⾯来说,更少的split会让系统提供更加稳定的在线服务。⽽坏处我们可以通过在请求的低⾕时间进⾏⼈⼯的split和balance来避免掉。

22.2 因此对于写⽐较多的系统,如果是离线应用,我们尽量只⽤⼀个family好了,但如果是在线应⽤,那还是应该根据应⽤的情况合理地分配family

  原则: 在合理范围内能尽量少的减少列簇就尽量减少列簇, 因为列簇是共享region 的, 每个列簇数据相差太大导致查询效率低下。

  最优: 将所有相关性很强的 key-value 都放在同一个列簇下, 这样既能做到查询效率最高, 也能保持尽可能少的访问不同的磁盘文件。 以用户信息为例, 可以将必须的基本信息存放在一个列族, 而一些附加的额外信息可以放在另一列族。

23 Hbase⾏健列族的概念,物理模型,表的设计原则?

  1. ⾏健:是hbase表⾃带的,每个⾏健对应⼀条数据。

  2. 列族:是创建表时指定的,为列的集合,每个列族作为⼀个⽂件单独存储,存储的数据都是字节数组,其中的数据可以有很多,通过时间戳来区分。

  3. HBase物理模型:

  (1)Table中的所有⾏都按照row-key的字典序排列;

  (2)Table在⾏的⽅向上分割为多个Region;

  (3)Region按⼤⼩分割的,每个表开始只有⼀个region,随着数据增多, region不断增⼤,当增⼤到⼀个阈值的时候, region就会等分成两个新的region,之后会有越来越多的region;

  (4)Region是HBase中分布式存储和负载均衡的最⼩单元。 不同region分布到不同的regionserver上;

  (5)region虽然是分布式存储的最⼩单元,但并不是存储的最⼩单元

Region由⼀个或者多个store组成,每个store保存⼀个columns family;

每个store又由⼀个memstore和0⾄多个storeFile组成

Memstore存储在内存中, storefile存储在HDFS上;

  (6)每个column family存储在HDFS上的⼀个单独⽂件中;

  (7)Key和version number在每个column family中都保存⼀份;

  (8)空值不会被保存。

  4. rowkey的设计原则:各个列簇数据平衡,长度原则、相邻原则,创建表的时候设置表放⼊regionserver缓存中,避免⾃动增长和时间,使⽤字节数组代替string,最⼤长度64kb,最好16字节以内,按天分表,两个字节散列,四个字节存储时分毫秒。

  5. 列族的设计原则:尽可能少(按照列族进⾏存储,按照region进⾏读取,不必要的io操作),经常和不经常使⽤的两类数据放⼊不同列族中,列族名字尽可能短

24 HBase存储单元Cell?

  1. 单元{row key, column(=<family> <label>),version}

  2. 唯⼀性;数据是没有类型的,以字节码形式存储

  3. 表: (⾏key,列族 列名,版本(timestamp))->值

25 HBase的客户端Client?

  1. 整个HBase集群的访问⼊口;

  2. 使⽤HBase RPC机制与HMaster和HRegionServer进⾏通信;

  3. 与HMaster通信进⾏管理类操作;

  4. 与HRegionServer进⾏数据读写类操作;

  5. 包含访问HBase接口,并维护cache来加快对HBase的访问。

26 HBase和Hive的对⽐

HBase

Hive

类型

列式数据库

数据仓库

内部机制

数据库引擎

MapReduce

增删改查

都⽀持

只⽀持导⼊和查询

Schema

只需要预先定义列族,不需要具体到列列可以动态修改

需要预先定义表格

应⽤场景

实时

离线处理

特点

以K-V形式存储

类SQL

27 HBase与传统关系型数据库(如MySQL)的区别

  1. 数据类型:没有数据类型,都是字节数组(有⼀个⼯具类Bytes,将java对象序列化为字节数组)。

  2. 数据操作: HBase只有很简单的插⼊、查询、删除、清空等操作,表和表之间是分离的,没有复杂的表和表之间的关系,⽽传统数据库通常有各式各样的函数和连接操作。

  3. 存储模式: Hbase适合于⾮结构化数据存储,基于列存储⽽不是⾏。

  4. 数据维护: HBase的更新操作不应该叫更新,它实际上是插⼊了新的数据,⽽传统数据库是替换修改

  5. 时间版本: Hbase数据写⼊cell时,还会附带时间戳,默认为数据写⼊时RegionServer的时间,但是也可以指定⼀个不同的时间。数据可以有多个版本。

  6. 可伸缩性, Hbase这类分布式数据库就是为了这个⽬的⽽开发出来的,所以它能够轻松增加或减少硬件的数量,并且对错误的兼容性⽐较⾼。⽽传统数据库通常需要增加中间层才能实现类似的功能

28 什么时候适合使⽤HBase(应⽤场景)?

  1. 半结构化或⾮结构化数据: 对于数据结构字段不够确定或杂乱⽆章⾮常难按⼀个概念去进⾏抽取的数据适合⽤HBase,因为HBase⽀持动态添加列。

  2. 记录很稀疏 : RDBMS的⾏有多少列是固定的。为null的列浪费了存储空间。⽽如上⽂提到的, HBase为null的Column不会被存储,这样既节省了空间⼜提⾼了读性能。

  3. 多版本号数据 : 依据Row key和Column key定位到的Value能够有随意数量的版本号值,因此对于须要存储变动历史记录的数据,⽤HBase是很⽅便的。⽐⽅某个⽤户的Address变更,⽤户的Address变更记录也许也是具有研究意义的。

  4. 仅要求最终⼀致性: 对于数据存储事务的要求不像⾦融⾏业和财务系统这么⾼,只要保证最终⼀致性就⾏。(⽐如HBase elasticsearch时,可能出现数据不⼀致)

  5. ⾼可⽤和海量数据以及很⼤的瞬间写⼊量,WAL解决⾼可⽤,⽀持PB级数据, put性能⾼

  6. 索引插⼊⽐查询操作更频繁的情况。⽐如,对于历史记录表和⽇志⽂件。(HBase的写操作更加⾼效)

  7. 业务场景简单,不需要太多的关系型数据库特性,列⼊交叉列,交叉表,事务,连接等。

29 Client会缓存.META.的数据,该数据更新了怎么办?

  其实, Client的元数据缓存不更新,当.META.的数据发⽣更新。⽐如因为region重新均衡,某个Region的位置发⽣了变化, Client再次根据缓存去访问的时候,会出现错误,当出现异常达到最⼤重试次数后, client就会重新去.META.所在的RegionServer获取最新的Region信息,如果.META.所在的RegionServer也变了, Client就会重新去ZK上获取.META.所在的RegionServer的最新地址。

30 hbase 实时查询的原理

  MemStore 是 HBase 中的一个重要组件,用于缓存最近写入的数据,直到数据量达到一定阈值后才将数据刷入磁盘。MemStore 存储的数据是按照行键(row key)排序的,这使得 HBase 可以在查询时快速定位到需要的数据行,从而提高查询性能。实时查询过程中,HBase 会首先在 MemStore 中查找符合查询条件的数据行,如果找到了匹配的数据行,则直接返回查询结果;如果没有找到匹配的数据行,则需要进一步在磁盘上的存储文件(HFile)中查找数据。

查询缓存是 HBase 的另一个重要组件,用于缓存查询结果。在查询过程中,HBase 会首先尝试从查询缓存中查找符合条件的结果,如果能够找到,则直接返回查询结果。如果没有找到,则继续在 MemStore 和 HFile 中查找数据。 查询缓存的使用可以避免重复查询相同的数据,从而进一步提高查询性能。

HBase 实现秒级查询的原理主要涉及到两个方面:数据的存储和查询的优化。

首先是数据的存储。HBase 数据存储的基本单位是 Region,一个 Region 包含多个 Store,每个 Store 对应一个列族(Column Family)。为了实现秒级查询,需要对数据的存储进行优化,以保证数据能够快速被查询。具体来说,可以采取以下措施:

  ① 预分区:将数据按照某个维度进行预分区,使得数据能够被均匀地分布在不同的 Region 中。这样可以避免数据倾斜,提高查询性能。

  ② 适当增大 Region 大小:将 Region 大小适当增大,可以减少 Region 数量,从而减少 Region 之间的负载均衡开销。同时,较大的 Region 可以提高查询性能,因为查询时需要在不同的 Region 之间进行切换,较大的 Region 可以减少这种切换的次数。

  ③ 优化数据的布局:将相似的数据放在一起存储,可以避免不必要的磁盘寻址,提高查询性能。

  ④ 使用 MemStore 和查询缓存:如前面所述,MemStore 和查询缓存都可以提高查询性能,因此在实现秒级查询时需要充分利用这两个机制。

其次是查询的优化。为了实现秒级查询,需要对查询过程进行优化,以提高查询性能。具体来说,可以采取以下措施:

  ① 使用 Scan 操作:Scan 操作是 HBase 中常用的查询操作,可以支持对整个表或某个 Region 进行扫描。通过合理设置 Scan 操作的参数,如起始行键、结束行键、缓存大小等,可以减少扫描的次数,提高查询性能。

  ② 使用过滤器:过滤器是 HBase 中常用的查询优化技术,可以根据查询条件过滤出符合条件的数据。通过合理使用过滤器,可以减少不必要的数据传输和计算,提高查询性能。

  ③ 优化查询并发度:为了提高查询性能,可以采用多线程或者并发查询的方式。HBase 提供了多线程查询和批量查询的功能,可以通过这些功能提高查询并发度,进一步提高查询性能。

  ④ 使用索引:HBase 的二级索引可以加速查询,但需要根据实际情况权衡索引对性能和存储空间的影响。

  ⑤ 调整数据模型:HBase 中的数据模型需要根据实际需求进行调整。例如,可以将常用的数据放在一个单独的列族中,提高查询性能;同时,也需要注意避免表结构复杂和列族数量过多,影响查询性能。

  总的来说,HBase 实现秒级查询的原理是通过数据存储和查询优化两个方面的优化实现的。在数据存储方面,需要通过预分区、优化数据布局、增大 Region 大小等方式来提高查询性能;在查询优化方面,需要使用 Scan 操作、过滤器等技术来优化查询过程

31 HBase 宕机如何处理?

  宕机分为 HMaster 宕机和 HRegisoner 宕机。

  a. 如果是 HRegisoner 宕机, HMaster 会将其所管理的 region 重新分布到其他活动的 RegionServer 上,由于数据和⽇志都持久在 HDFS中,该操作不会导致数据丢失。所以数据的⼀致性和安全性是有保障的。

  b. 如果是 HMaster 宕机, HMaster 没有单点问题, HBase 中可以启动多个 HMaster,通过Zookeeper 的Master Election 机制保证总有⼀个 Master 运⾏。即 ZooKeeper 会保证总会有⼀个 HMaster 在对外提供服务。

0 人点赞