09HBase

约 7 分钟 · 1942 字评论
请注意,本文编写于 784 天前,最后修改于 145 天前,其中某些信息可能已经过时。

hbase

hbase的表设计

hbase的基本架构和读写流程

基础架构

  1. hbase依赖于hdfs,和zookeeper
  2. Zookeeper为整个HBase集群提供协助的服务,比如存储region和namespace的元数据信息
  3. HMaster主要用于监控和操作集群的所有RegionServer,table的create,delete,alter
  4. RegionServer主要用于服务和管理分区(Regions),数据的get,put,delete,region的split[[09_Hbase#520be5|切分详情]],compact[[09_Hbase#3192ef|合并详情]] RegionServer组成
  5. 多个Region
  6. 每个regionserver对应着一个BlockCache,用作读HBbase时数据的读缓存 Region组成
  7. 多个memstore,一个列族对应一个memstore,用于对写缓存的数据排序,当达到hbase数据刷写时机[[09_Hbase#^e1761e|刷写详情]],从内存写出为Hfile并上传到hdfs上落盘
  8. 每个region都对应着一个Hlog
  9. HFile为memstore中刷写后的文件

HBase存储在Hdfs上的实际目录结构

java
/hbase /data /<Namespace> (Namespaces in the cluster) /<Table> (Tables in the cluster) /<Region> (Regions for the table) /<ColumnFamily> (ColumnFamilies for the Region for the table) /<StoreFile> (StoreFiles for the ColumnFamily for the Regions for the table)

读数据流程

[!example|noborder grid]+ 读数据


shell
## 命令行读数据的格式: 1. 单独行 get 表名,rowkey,{COLUMN => 列族:列名} 2. 多行 scan
  1. clien于zookeeper建立连接,并读取hbase节点下的meta-region-server节点信息,以获取meta表所在的regionserver
  2. 根据反馈的regionserver信息,结合请求的namespace,table,rowkey从meta表中获得待查表的
    具体分布(即table所在的regionserver下的那个region),并把待查表的元数据缓存在 client中meta cache中,以供下次读取
  3. 向region发起读数据请求,依次读取(图示中的标记顺序有误!!)MemStore,BloackCache,Hfile以上三个位置均可能存在
    要读数据,且读取顺序不变,依照从内存到磁盘的读取顺序,最大程度的提高读取效率
  4. 将所有查到的数据进行合并,因为同一个rowkey可能有多个版本,并把合并结果缓存到BlockCache中,
    以供下次重复读取
  5. 把合并后的数据发送给client

写数据流程

shell
## 命令行写数据的格式: put 表名,rowkey,列族:列名,value值 hbase> put 't1', 'r1', 'c1', 'value' hbase(main):018:0> put 't1','1000','cf1:name','KIKI'

  1. 向zookeeper请求,获取hbase:meta表所在的regionserver,并向meta表所在的regionserver请求meta表数据,并缓存在client的meta cache中
  2. 向目标表所在的Region的Wal文件中写入数据,之后再MemStore中排序,随后就给client响应数据写入
    成功
  3. 当达到数据刷写磁盘时机[[09_Hbase#^e1761e|刷写详情]],hbase自动写入hfile(storefile)

如何读取刚刚写入hbase的最新数据?
可以使用flush 表名方式手动刷写memstore中的新数据到hfile,但是没有必要,因为读数据时,第一个读取的位置就是memstore

region的切分

  1. region切分的原因
  • region使用需要占用regionserver的内存的,当过大,占用就越高,就会因为一个或者几个region而影响regionserver下的其他region的正常工作
  • region数量也不宜过多,否则会因为数据分布在过多的region中,降低了读写的能力 ^520be5 0.94: 固定按照 10G切
    0.94-2.0: min(10G, 128M * R^3 * 2) R
    ,这张表的 Region数量
    2.0: 第一次按照 256M切,后面都按照 10G切

storefile的compact

stroefile需要合并的原因

  1. 基于HDFS的小文件问题,过多将占用namenode过多的内存
  2. 每个memstore刷写都会产生一个storefile,况且HBase总每条记录存在版本的概念,相同rowkey的数据在value变更,或者删除都会产生版本,造成读取效率下降 ^3192ef

数据刷写

写数据时的刷写时机

^e1761e

  1. 当region内的某个memstore大小达到l128M,会进行该region内所有memstore的flush
  2. 当数据的写操作速率高于flush的速率,就会延迟触发memestore级别,此时若region内的所有ms数据达到
    了512M,就会停止接受写请求,等待该region内的所有memstore刷写完成
  3. 当某个regionserver所有region大小达到了hbase堆内存 * 0.4 * 0.95,就会触发hbase集群所有
    regionserver的刷写,当写速率高于flush的速率,当数据达到hbase堆内存 * 0.4,该regionserver就会停止接受写请求,直到所有regionserver大小低于hbase堆内存 * 0.4 * 0.95,取消阻塞

预分区

  1. 预分区原因
    减少region进行自动split时带来的性能开销
  2. 如何预分区
    建hbase表使用关键字splts时指定分区,可以时分区规划文件,后者直接指定
  3. 项目没有使用预分区

数据热点问题和数据倾斜问题

数据热点

当client频繁地访问一个或者几个region,可能超过了单台regionserver负载能力,导致该RS上的其他region也不可用的情况

  1. rowkey的设计原则
    • 唯一性原则 表级别的唯一性
    • 长度原则 rowkey的大小尽量小
    • 散列原则 足够散列地分布再region中,视业务情况选用 当需要进行大量的连续查询,则rowkey可以设计得集中连续地分布再region中,提高查询效率
      当需要进行大量的散列插叙,则可以设计得足够散列地分布在region中,实现负载均衡
  2. rowkey具体设计
    • rowkey取hash
      这样可以将数据较随机散列的分布在regionserver中
    • rowkey反转
      比如rowkey中出现连续时间戳开头,那么可能会出现数据热点问题,此时,可以对rowkey字符串进行一个反转
    • rowkey加盐
      区别于密码学中的加盐,HBase中加盐是指在rowkey前加上随机的字符串,使得打乱rowkey的集中分布
  3. 数仓实时项目中设计
    利用了phoenix进行hbase映射,建表使用phoenix的加盐表,具体原理为:
java
// 使用phoenix在建维度表时指定该表的预分区 StringBuilder bu = new StringBuilder(); bu.append("create table if not exists ") .append(tp.getSinkTable()+ "(") .append(tp.getSinkColumns().replaceAll("[^,]+","$0 varchar")) .append(",constraint pk primary key (" + (tp.getSinkPk() == null? "id": tp.getSinkPk())) .append("))") .append(tp.getSinkExtend() == null? "": tp.getSinkExtend()); System.out.println("建表sql为:" + bu); /** phoenix加盐表原理: ①需要保证加盐表需要预先分区,比如将DIM_SKU_INFO预分为4个区, ② hash(rowkey)% salt_bucket 的得到新rowkey的前缀,并拼接,如当使用phoenix向hbae插入数据时,每条rowkey前加入ox00 ~ 0x03的字节,保证数据均匀分布在regionserver中 create table DIM_SKU_INFO( id varchar,,price varchar ,sku_name varchar, constraint pk primary key (id) ) SALT_BUCKETS = 4 */

数据倾斜

分布再一个或者几个region上的数据比其他region超大,导致的数据倾斜
本质还是rowkey的不均匀分布造成的,需要重新设置rowkey

二级索引

往往在读hbase数据时,不会只按照rowkey的一级索引查询,此时,就会进行hbase的全表扫描,效率低 1.全局索引(默认建表索引,也是电商实时项目中的选择)

sql
-- 对studen表的address字段建立本地索引 create index global_index on table STUDENT(address);

全局索引会建立一张新的索引表,新表的rowkey为 原表的rowkey + address
此时表数量增加,对比本地索引写速率下降(更新数据时需要额外维护一张或者多张索引表),对比原表没有索引时的全表扫描,读速率提升,适合读多写少的场景 2.本地索引

sql
-- 对studen表的name字段建立本地索引 create local index local_index on table STUDENT(name);

本地索引会为原表的每一条数据加入一条数据 数据 rowkey为 原row_key + name
但此时数据量增加,对比原表读效率提升,对比全局索引写速率提升 3.包含索引

sql
-- 对studen表的address字段建立包含索引 create index include_index on table STUENDT(name) INCLUDE(address);

包含索引include的字段在索引表中为普通字段,在读取数据时在磁盘中,减少加载索引表的内存占用
对比默认的全局索引,索引表需要全部加载入内存中,内存占用较包含索引表占用高

flink写数据hbase,如何保证数据一致性?

hbase利用rowkey的唯一性,实现幂等写入

hbase的读请求优化点

  1. hfile存储使用类似LSM的结构存储,查询高效
  2. 读取Hfile时使用BoolFilter,高效定位数据位置

实时项目中使用hbase的原因

  1. hbase用来存储dim层维度数据,hbase读写效率高效
  2. 维度数据字段比较多,而hbase是一个拥有可以存储百万列的实时组件
  3. 项目常用来查询单条数据,对于hbase表的k-v型设计比较符合

HBase的二级索引问题

项目中使用了Phoenix On HBase的模式,可以利用Phoenix提供的二级索引功能,实现HBase的二级索引

HBase的水平扩展问题

版权声明

本文作者:hedeoer

本文链接:/post/__gh__47

许可说明:本博客所有文章除特别声明外,均采用 许可协议。转载请注明出处!

09HBase

0

匿名访客仅支持文本评论。

暂无评论