HBASE

HBASE

概述和特点

建立在hdfs之上,提供高可靠性、高性能、列存储、可伸缩、实时读写nosql的数据库系统。

  • 一个表可以有数十亿行,上百万列;

  • 无模式

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

  • 面向列

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

  • 稀疏

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

  • 数据多版本

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

  • 数据类型单一

    Hbase中的数据都是字符串,没有类型。

架构

HBASE架构

HMaster

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

RegionServer

  • 负责存储HBase的实际数据

  • 处理分配给它的Region

  • 刷新缓存到HDFS

  • 维护HLog

  • 执行压缩

  • 负责处理Region分片

读请求过程

  • 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一分为二。

常用shell命令

#进入HBase客户端命令操作界面
bin/hbase shell

#查看帮助命令
help

#查看当前数据库中有哪些表
list



###创建一张表

#创建user表,包含info、data两个列族
create 'user', 'info', 'data'
#或者
create 'user', {NAME => 'info', VERSIONS => '3'},{NAME => 'data'}



###添加数据操作

#向user表中插入信息,row key为rk0001,列族info中添加name列标示符,值为zhangsan
put 'user', 'rk0001', 'info:name', 'zhangsan'

#向user表中插入信息,row key为rk0001,列族info中添加gender列标示符,值为female
put 'user', 'rk0001', 'info:gender', 'female'

#向user表中插入信息,row key为rk0001,列族info中添加age列标示符,值为20
put 'user', 'rk0001', 'info:age', 20

#向user表中插入信息,row key为rk0001,列族data中添加pic列标示符,值为picture
put 'user', 'rk0001', 'data:pic', 'picture'



###查询数据操作

#通过rowkey进行查询
#获取user表中row key为rk0001的所有信息
get 'user', 'rk0001'

#查看rowkey下面的某个列族的信息
#获取user表中row key为rk0001,info列族的所有信息
get 'user', 'rk0001', 'info'

#查看rowkey指定列族指定字段的值
#获取user表中row key为rk0001,info列族的name、age列标示符的信息
get 'user', 'rk0001', 'info:name', 'info:age'

#查询所有数据
#查询user表中的所有信息
scan 'user'

#列族查询
#查询user表中列族为info的信息
scan 'user', {COLUMNS => 'info'}



#统计
count 'user'

表数据模型

Row Key

与nosql数据库们一样,row key是用来检索记录的主键。访问hbase table中的行,只有三种方式:

  1. 通过单个row key访问

  2. 通过row key的range

  3. 全表扫描

Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组。

Hbase会对表中的数据按照rowkey排序(字典顺序)

列族Column Family

hbase表中的每个列,都归属与某个列族。列族是表的schema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如courses:history , courses:math 都属于 courses 这个列族。

列族越多,在取一行数据时所要参与IO、搜寻的文件就越多,所以,如果没有必要,不要设置太多的列族

列 Column

列族下面的具体列,属于某一个ColumnFamily,类似于我们mysql当中创建的具体的列

时间戳

HBase中通过row和columns确定的为一个存贮单元称为cell。每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。

Cell

由{row key, column( = +

cell中的数据是没有类型的,全部是字节码形式存贮。

预分区

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

每一个region维护着startRowkey与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。

作用

  • 增加数据读写效率
  • 负载均衡,防止数据倾斜
  • 方便集群容灾调度region
  • 优化Map数量

手动设定预分区

create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']

HBASE_手动设定预分区

16进制算法生成预分区

create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

16进制算法生成预分区

get和scan

get

按指定RowKey 获取唯一一条记录

org.apache.hadoop.hbase.client.Get

scan

按指定的条件获取一批记录

org.apache.Hadoop.hbase.client.Scan

  • scan 可以通过setCachingsetBatch 方法提高速度(以空间换时间);
  • scan 可以通过setStartRowsetEndRow 来限定范围([start,end)start 是闭区间,end 是开区间)。范围越小,性能越高。
  • scan 可以通过setFilter 方法添加过滤器,这也是分页、多条件查询的基础。

compact

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

作用:

① 合并文件
  ② 清除过期,多余版本的数据
  ③ 提高读写数据的效率

实现方式

  • Minor

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

  • Major

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

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中。

HRegionServer宕机

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

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

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

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

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

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

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

优化

减少调整

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

  • Region
    如果没有预建分区的话,那么随着region中条数的增加,region会进行分裂,这将增加I/O开销,所以解决方法就是根据你的RowKey设计来进行预建分区, 减少region的动态分裂。
  • HFile
    HFile是数据底层存储文件,在每个memstore进行刷新时会生成一个HFile,当HFile增加到一定程度时,会将属于一个region的HFile进行合并, 这个步骤会带来开销但不可避免,但是合并后HFile大小如果大于设定的值,那么HFile会重新分裂。为了减少这样的无谓的I/O开销,建议估计项目数据量大小, 给HFile设定一个合适的值。

减少启停

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

  • 关闭Compaction,在闲时进行手动Compaction。
    因为HBase中存在Minor Compaction和Major Compaction,也就是对HFile进行合并,所谓合并就是I/O读写,大量的HFile进行肯定会带来I/O开销, 甚至是I/O风暴,所以为了避免这种不受控制的意外发生,建议关闭自动Compaction,在闲时进行compaction。
  • 批量数据写入时采用BulkLoad。
    如果通过HBase-Shell或者JavaAPI的put来实现大量数据的写入,那么性能差是肯定并且还可能带来一些意想不到的问题,所以当需要写入大量离线数据时 建议使用BulkLoad。

减少数据量

  • 开启过滤,提高查询速度
    开启BloomFilter,BloomFilter是列族级别的过滤,在生成一个StoreFile同时会生成一个MetaBlock,用于查询时过滤数据
  • 使用压缩
    一般推荐使用Snappy和LZO压缩

合理设计表结构

RowKey

  • 散列性:散列性能够保证相同相似的rowkey聚合,相异的rowkey分散,有利于查询。
  • 简短性:Rowkey 是一个二进制码流,Rowkey 的长度被很多开发者建议说设计在 10~100 个字节,不过建议存为byte[]字节数组,而且越短越好,不要超过 16 个字节,一般设计成定长的
  • 唯一性:rowKey必须具备明显的区别性。
  • 业务性
    假如我的查询条件比较多,而且不是针对列的条件,那么rowKey的设计就应该支持多条件查询。
    如果我的查询要求是最近插入的数据优先,那么rowKey则可以采用叫上Long.Max-时间戳的方式,这样rowKey就是递减排列。

列族

  • 根据实际应用场景(多列族)

    适合读多写少,查某一列时,直接扫列族,减少了I/O

    不适合写,多个列族就是多个store,memstore进行flush时,增加I/O开销。

  • 根据数据访问频度

    如一张表里有些列访问相对频繁,而另一些列访问很少, 这时可以把这张表划分成两个列族,分开存储,提高访问效率。

region大小

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

热点问题

热点发生在大量的 client 直接访问集群的一个或极少数个节点(访问可能是读, 写或者其他操作)。大量访问会使热点 region 所在的单个机器超出自身承受能力,引起性能 下降甚至 region 不可用,这也会影响同一个 RegionServer 上的其他 region,由于主机无法服 务其他 region 的请求。 设计良好的数据访问模式以使集群被充分,均衡的利用。 为了避免写热点,设计 rowkey 使得不同行在同一个 region,但是在更多数据情况下,数据 应该被写入集群的多个 region,而不是一个。

加盐

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

哈希

哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是 可以预测的。使用确定的哈希可以让客户端重构完整的 rowkey,可以使用 get 操作准确获取 某一个行数据

反转

反转 rowkey 的例子以手机号为 rowkey,可以将手机号反转后的字符串作为 rowkey,这 样的就避免了以手机号那样比较固定开头导致热点问题。

130xxx 155xx 156xx 180x x 189xx

时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 rowkey 的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值