HBase入门

一、概览
HBase是一个分布式的,面向列的开源数据库。

它更像是分布式存储而不是分布式数据库,它缺少很多RDBMS系统的特性,比如列类型,辅助索引,触发器,和高级查询语言等。

那Hbase有什么特性呢?如下:

  1. 强读写一致,但是不是“最终一致性”的数据存储,这使得它非常适合高速的计算聚合。
  2. .自动分片,通过Region分散在集群中,当行数增长的时候,Region也会自动的切分和再分配自动的故障转移。
  3. Hadoop/HDFS集成,和HDFS开箱即用,不用太麻烦的衔接
  4. 丰富的“简洁,高效”API,Thrift/REST API,Java API
  5. 块缓存,布隆过滤器,可以高效的列查询优化
  6. 操作管理,Hbase提供了内置的web界面来操作,还可以监控JMX指标

什么时候用Hbase?

  1. 成熟的数据分析主题,查询模式已经确立并且不轻易改变。
  2. 传统型关系型数据已经无法承受负荷,写入量巨大,高速插入,大量读取。
  3. 适合海量数据的单条记录或小范围扫描(大范围扫描性能低下),,不支持JOIN等复杂关联。

Hbase与关系型数据库对比

属性HBaseRDBMS
数据类型只有字符串丰富的数据类型
数据操作增删改查,不支持join各种各样的函数与表连接
存储模式基于列式存储基于表结构和行式存储
数据保护更新后仍然保留旧版本替换
可伸缩性轻易增加节点需要中间层,牺牲性能
事务不支持支持

二、Hbase数据模型

该技术来源于的Google论文"Bigtable"

Big Table的想法

学生表的例子S(S#,sn,sd,sa)(学号,名称,系别,年龄)
在Big Table中,可以写成三个列的表,列分别为行键,属性字段(名字sn),value
比如
学号1, 名称,小明
学号1,系别,计算机系
学号1,年龄,20
这样来看,任何一个具有key的表,均可以写成三个列的表。

Hbase中概念:

  • 表(Table):是一种稀疏表(不存储数据为NULL的数据),表的索引是行关键字、列关键字、时间戳
  • 行关键字(Row Key):行的主键,唯一标识一行数据,也称为行键。(并不是唯一一条记录,因为有时间戳标示多条)
    表中的行根据行键进行字典排序,所有对表的访问都要通过表的行键(单个行键访问\行键范围访问(支持正则)\全表扫描(性能低下))。在创建表时,行键不用也不能预先定义,而对表数据进行操作时必须指定行键,行键在添加数据时首次被确定。
  • 列族(Column Family):行中的列被分为列族。同一个列族的所有成员具有相同的列族前缀。例如[course:math]和[course:art]都是列族[course]的成员。一个表的列族必须在创建表时预先定义。
  • 列关键字(Column Key):也称列键。 格式为:[Family:qualifier]。 family是列族名,用于表示列族前缀。qualifier是列修饰符,表示列族中的一个成员,列族成员可以在随后按需拓展。理解为列的唯一标识。但是列标识是可以改变的,因此每一行可能有不同的列标识。
  • 存储单元(Cell): 在HBase中,值是作为一个单元保存在系统中的。要定位一个单元,需要用[行键+列键+时间戳] 3个要素。
  • 时间戳(Timestamp):插入单元格时间,默认为单元格的版本号。

HBase 本质上只有插入操作,更新和删除都是使用插入方式完成的,这是由其底层 HDFS 的流式访问特性(一次写入、多次读取)决定的。
HBase在更新时总会插入一个带时间戳的新行,而删除时则插入一个带有删除标记的新行。标示这条记录已被删除。
Hbase会在生命周期内,会在一定的时间内的将一些小的文件进行合并成大的文件,然后将一些打上删除标记的记录抛弃掉。每次插入都有一个时间戳的标记,每次都是一个新的版本,Hbase会保留一定数据的版本(自行设置)。如果查询时提供时间戳则返回距离该时间最近的版本,否则返回离现在最近的版本。

时间戳
对应每次数据操作的时间,可由系统自动生成,也可以由用户显式的赋值。
Hbase支持两种数据版本回收方式:
1.每个数据单元,值存储指定个数的最新版本。
2.保存指定时间长度的版本(7天)

在传统数据库中,只能通过表的主键和唯一字段定位到某一条数据。
在这里插入图片描述
主键为name,主要字段有name、grade、math、art。
由于主键唯一标识了一行记录,所以我们很容易按姓名查询某位同学的成绩。

那么思考以下几个问题:
如果新增一门课程,在不修改表结构的情况下,能保存成绩么?
如果有同学参加补考,怎么保存两次成绩。
如果同学只参加一门考试,其他课程都没有成绩。我们能保存只有成绩的课程来节省存储空间么?

HBase的数据模型就能完美解决以上问题:
在这里插入图片描述
可以通过[行键+时间戳+列族]来访问具体的值(单元)。
对表操作必须指定行键和列键,每次操作都会增加一条数据并会自动生成时间戳。从上到下倒序排列,不必由用户管理。
每次只针对一个列键操作。且列修饰符可以为空。
eg. 在t4时刻,客户端添加[jason] 的[grade]为[2]。
类似操作为:先找到行键[jason],然后指定列键并赋值[grade:=2]。这里的列族修饰符可以为空。
前面提到过列族修饰符可以由任意字符组成。
现在基于HBase回答前面的问题:

  1. 如果jason新增英语科目成绩,那么指定行键[jason],列键[source:english],以及值(英语成绩)即可。
  2. 如果jason 参加数学补考,那么直接指定行键[jason],列键[source:math],以及值(数学补考成绩)即可。
  3. 前面说过HBase是稀疏的存储设计。其实概览图中空白部分是不会实际被存储的。

设计要点:

  1. 行健设计Hbase在存储数据的时候,有两个SortedMap,首先按照rowkey进行字典排序,然后再对Column进行字典排序。行健的设计非常重要。这种设计方式可以让有关系的行非常的近,通常行健的设计是网站的域名反转, 比如(org.apache.www,org.apache.mail,org.apache.jira),这样的话所有的Apache的域名就很接近。避免单调的递增行健,因为Hbase的行健是有序排列的,这样可能导致一段时间内大部分写入集中在某一个Region上进行操作,负载都在一台节点上。可以设计成:[metric_type][event_timestamp],不同的metric_type可以将压力分散到不同的region上行健短到可读即可,因为查询短键不比长键性能好多少,所以设计时要权衡长度。行健不能改变,唯一可以改变的方式是先删除后插入。
    其他的避免热点堆积的解决方案:Rowkey加一个前缀,使用一些随机字符;使用Hash散列(比如md5后取前四位),好处时能让一个给定的行有相同前缀,使得读操作能够推断
  2. 列簇设计
    列簇是一些列的集合,一个列簇的成员有相同的前缀,以冒号(:)作为分隔符。现在Hbase不能很好处理2~3个以上的列簇,所以尽可能让列簇少一些,如果表有多个列簇,列簇A有100万行数据,列簇B有10亿行,那么列簇A会分散到很多的Region导致扫描列簇A的时候效率底下。列簇名的长度要尽量小,一个为了节省空间,另外加快效率,比如d表示data,v表示value
  3. 列簇属性配置 HFile数据块,默认是64KB,数据库的大小影响数据块索引的大小。数据块大的话一次加载进内存的数据越多,
    扫描查询效果越好。但是数据块小的话,随机查询性能更好

    create ‘mytable’,{NAME => ‘cf1’, BLOCKSIZE => ‘65536’} 数据块缓存,数据块缓存默认是打开的,如果一些比较少访问的数据可以选择关闭缓存
    create ‘mytable’,{NAME => ‘cf1’, BLOCKCACHE => ‘FALSE’} 数据压缩,压缩会提高磁盘利用率,但是会增加CPU的负载,看情况进行控制
    create ‘mytable’,{NAME => ‘cf1’, COMPRESSION => ‘SNAPPY’} Hbase表设计是和需求相关的,但是遵守表设计的一些硬性指标对性能的提升还是很有帮助的,这里整理了一些设计时用到的要点。

应用场景一:浏览历史

利用关系型数据库,特别简单 使用order by 取出前五条。

关系型数据库的困难
1.简单的事情只要上了量就会变成无比复杂的事情。
2.order by 耗费很多性能
3.大量发生,但又无法分布式处理
4.顾客需要实现看到自己的足迹,因此不能使用缓存技巧。

Hbase迎接挑战
1.天生就是面向时间戳查询
2.基于行键的查询异常快速,特别是最近的数据被放在内存的memstore里,完全没有开销。
3.分布式化解复核。

模式设计
行键: userid
列族和列 : book:bookid
为了充分利用分布式,可以用reverse key,hash等技巧改造行键。

应用场景二、推荐系统(浏览本商品的顾客还看过什么书)
使用Hbase:表设计与查询实现
两个表,一个是u-t,另一个是t-u
U-t:行键位userid,列族和列为thread:threadid
T-u: 行键位threadid, 列族和列为user:userid
查询:先根据threadid,找出userid,然后将这些userid根据u-t表中将所有的threadid找出,在程序中实现去重和统计功能。

应用场景三、辅助索引
例子:学生表(学号,身份证号,姓名,系,年龄),有时在学号上查询,有时在身份证上查询
主表:行键位学号,列族为学生,下面的列式身份证,姓名,性别,系,年龄。
辅助(索引)表: 行键位身份证号,列族和列为学号。
此索引表大大不同于oracle的索引,需要在手工维护此表。

应用场景四、复合行键设计

指查询需要多个列的时候,如何设计?
简单就是将多个列连接起来,作为一个行键。
复合行键:
便于分布
便于多条件伸缩查询

三、Hbase架构
在这里插入图片描述
HBase采用Master/slaves 的主从服务器结构,由一个HMaste服务器和多个HRegion Server服务器组成,
所有的服务器都通过Zoo Keeper协调并处理运行期间遇到的错误。
HMaster负责管理所有的HRegion Server,各HRegion Server负责存储许多HRegion,
每一个HRegion是对HBase逻辑表的分块。
一台物理节点只能跑一个HRegionServer

  1. HRegion
    HBase使用表存储数据,表由行和列组成。但是当表超过设定值大小时,Hbase会自动将表划分不同的区域,每个区域被称为HRegion。
    HRegion是集群上分布式存储和负责均衡的最小单位,类似于HDFS中文件与文件块的概念。
    一个HRegion保存一个表中连续的数据,通过表名及主键来区分每一个HRegion。最开始一个表只有一个HRegion,
    随着HRegion增大,超出设定阈值,会分裂成两个大小基本相同的HRegion,称为HRegion分裂。
  2. HRegion Server HRegion
    Server负责响应客户端IO请求,向HDFS中读写数据,一台机器上只运行一个HRegion Server。 HRegion
    Server包含两个部分:HLog 和 HRegion。 HLog用于存储数据日志,实质是HDFS的Sequence
    File。到达HRegion的写操作首先追加到日志中, 才被加入到内存中的Mem Store。 HLog
    主要用于故障恢复,如果HRegion所在主机发生故障,那么所维护的HRegion会被重新分配至新的主机上, 新的HRegion
    Server 在加载HRegion时,可通过Hlog恢复数据。
  3. HMaster
    HMaster主要任务是告诉每个HRegion Server需要维护哪些HRegion。在Hbase中可以启动多个HMaster,通过ZooKeeper的Master选举机制来保证系统中总会有一个HMaster在运行。
    HMaster包括以下功能:
    1.管理用户对表的增改查操作
    2.管理HRegion的负责均衡,调整HRegion分布
    3.在HRegion分裂后,负责HRegion分配
    4.在HRegion Server停机后,负责失效HRegion Server上的HRegion迁移
  4. zookeeper Zookeeper 是存储HBase
    -ROOT-表和.META.表的位置,这是HBase中两张特殊的表。称为根数据表(-ROOT-)和元数据表(.META.)。 元数据表记录普通用户表的HRegion标识符信息,每个HRgion的标识符为:表名+开始主键+唯一ID。
    随着用户表的HRegion的分裂,.META.表的信息也会增加,并且还可能被分割为几个HRegion,
    此时可以用一个-ROOT-表来保存META的HRegion信息,而-ROOT-表是不能被分割的,也就是-ROOT-表只有一个HRegion。
    那么客户端(Client)在访问用户数据前需要先访问Zoo Keeper,然后访问-ROOT-表,接着访问.META.表,
    最后才能找到用户数据所在位置进行访问。

Hbase读数据
数据读流程:

  1. Client通过zookeeper定位的Hbase元数据表(hbase:meta)。(老版本还有 --ROOT–表)
  2. 从hbase:meta定位数据分布在哪些RegionServer上
  3. 从RegionServer,将内存(MemStore)和磁盘(StoreFile)中的数据合并后,返回Client

数据写流程:

  1. Client向Region Server发起写请求(Put、Delete)
  2. Region Server首先将数据写入WAL(HLog)中。顺序写,没有寻道的时间(600M/s)
  3. Region Server将数据写入内存(MemStore)
  4. Client写成功
  5. 内存中的数据定期写入磁盘(StoreFile),这个过程就是Flush

什么时候触发Flush:

  1. 内存(MemStore)中的数据达到阈值(默认64M),
    将内存中的数据写入StoreFile(HDFS)
  2. 删除内存和HLog中历史数据
  3. 在HLog中打标记点

数据Compact(合并)、Split:

  1. 当一个Region的Store中StoreFile文件数量达到一定阈值(默认4个),会触发Compact(合并)操作,
    将多个StoreFile合并成一个大的StoreFile。 Compact会对Cell的历史版本进行删除和数据合并。
  2. 当一个Region的数据达到256M,会进行split操作,即将一个region拆分成两个region,拆分完成后,老的region下线。
    新的两个region根据负载情况分配到对应的regionServer。

从一张表的存储理解Hbase架构
基于一张表:
1.一个表的Region对应HDFS的一个目录
2.Store和列族一一对应。 在HDFS上,一个列族对应一个HDFS目录,在Region目录下面。
3.一个Store对应多个StoreFile和一个MemStore
4.数据写先写日志,然后存入MemStore,返回Client写成功。
5.MemStore中数据大小达到阈值,数据将Flush到StoreFile。
6.当Store中的文件数量达到阈值,会执行Compact(合并)操作
7.当一个Region的StoreFile的大小达到阈值,这个表的Region会进行split。

示例:user1表存在两个data/info两个列族
/hbase/data/default/user1/7eb4ce87a61ef099528c0ee17738091e/data/f6b45be8822243038c3fd55e07a89e10
/hbase/data/default/user1/7eb4ce87a61ef099528c0ee17738091e/info/d31cc9a4f55646b3a706288403fa8405
store是保存在表的region目录下(一级目录)、列族下(二级目录)。一个Store包含多个StoreFile。

当对StoreFile进行合并和拆分时,数据时无法读写的。同时这两个操作费IO。
可以关掉合并和拆分,通过定时shell脚本进行合并和拆分。

Hbase列族数量不能太多
1.Flush会产生更多IO Flush的最小单元是region,也就是说一个region中的某个列族做Flush操作,其他的列族也会Flush。
对每个列族而言,每次Flush都会产生一个文件,频繁Flush必然会产生更多的StoreFile,StoreFile数量增多又会产生更多的Compact操作。
Flush和Compact都是很重的IO操作。

  1. Split操作可能会导致数据分布不均匀 Split的最小单元是region,如果这个region有两个列族A、B。
    列族A有100亿条记录,列族B有100条记录。 如果最终Split成20个region,那么列族B的100条记录会分布到20个region上,
    扫描列族B的性能低下。

4.HBase数据特点

  • 数据类型: 只有简单的字符串类型。(传统SQL有多种数据类型)
  • 数据操作:只有简单的插入、查询、删除、清空等操作,表和表之间是分离的,没有复杂的表和表之间的关系。(传统SQL有多种连接操作)
  • 存储模式:基于列的存储。每个列族由几个文件保存,不同列族的文件是分离的。(传统SQL是基于表格结构和行模式存储) 数据维护:
    只是简单的插入了新数据,它的旧版本会保留(传统SQL是替换修改操作)
  • 可伸缩: 分布式数据库。能够轻松的增加和减少硬件数量,并且对错误兼容性较高。(传统SQL需要中间层才能实现类似功能)

四、实例
–查询数据库状态
status

–查询数据库版本
version

–创建表
create ‘member’,‘member_id’,‘address’,‘info’
member是表名
member_id,address,info是列族
–查看表信息
list
–查看表结构
describe ‘member’
–删除列族
alter ‘member’,{NAME=> ‘member_id’,method=>‘delete’}
–离线表,然后再修改表
disable ‘member’

–使表在线
enable ‘member’

– 查询表是否存在
exists ‘member’
–判断表是否离线或者在线
is_enabled ‘member’

–插入记录
put ‘member’,‘scuteshuxue’,‘info:age’,‘24’
member是表名
scuteshuxue是行键
info是列族

–获取行键所有数据
get ‘member’,‘scuteshuxue’

–获取一个行键,一个列族的所有数据
get ‘member’,‘scutshuxue’,‘info’

–获取一个行键,一个列族中一个列的所有数据
get ‘member’,‘scutshuxue’,‘info:age’
–更新一条记录,跟插入一样
put ‘member’,‘scutshuxue’,‘info:age’,‘99’
–全表扫描
scan ‘member’
–删除指定行键的字段
delete ‘member’,‘temp’,‘info:age’

–删除整个行
deleteall ‘member’,‘xiaofeng’
– 查询表多少行
count ‘member’
–清空表
truncate ‘member’
–示例获取多版本数据
create ‘t1’,‘f1’
alter ‘t1’,{NAME=>‘f1’,VERSIONS=>3}
–插入数据
put ‘t1’,‘rowkey1’,‘f1:name’,‘chhliu’
put ‘t1’,‘rowkey1’,‘f1:name’,‘xyh123’
put ‘t1’,‘rowkey1’,‘f1:name’,‘chhliuxyh’
–获取数据,默认只展示最新一条
get ‘t1’,‘rowkey1’,‘f1:name’
–获取多版本数据
get ‘t1’,‘rowkey1’,{COLUMN=>‘f1:name’,VERSIONS=>3}
–过滤数据
–查看列的值为22:
scan ‘testorder’,FILTER=>“ValueFilter(=,‘binary:22’)”
–查看列的值包含xiao: scan ‘testorder’,FILTER=>“ValueFilter(=,‘substring:xiao’)”
–列名以age开头,且age为22或者25的记录:
scan ‘testorder’, FILTER=>“ColumnPrefixFilter(‘ag’) AND (ValueFilter(=,‘binary:22’) OR ValueFilter(=,‘binary:25’) )”

整理来源:
https://www.jianshu.com/p/354ad1d29f3a
https://www.jianshu.com/p/b23800d9b227

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值