文章目录
概述
HBase是分布式、面向列族的开源数据库,HDFS为HBase提供可靠的底层数据存储服务,MapReduce为HBase提供高性能的计算能力,Zookeeper为HBase提供稳定服务和Failover机制,可以说,HBase是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。
特性
- 底层存储依赖于 HDFS
- 面向列
适用场景
- HBase 适用于海量数据存储和准实时查询。HBase能够应用在上百亿行*上百万列,实现百毫秒的查询;
- 查询简单(基于rowkey或者rowkey查询范围)、不涉及复杂关联的环境,如:交通(红绿灯信息采集)、海量订单流水数据(长久保存)、交易记录、数据库历史数据。
- 不需要 列类型,辅助索引,触发器,和高级查询语言的场景
不适用场景
- 数据量少时,例如几百万行甚至不到的数据量
- 需要有复杂查询的场景
HBase 和 HDFS
HDFS适用于存储大容量文件
的分布式文件系统
,不支持快速单独记录查找
,提供了高延迟批量处理,但是没有批处理的概念;提供的数据只能够顺序访问
HBase 是建立在HDFS之上的数据库,提供在较大的表快速查找,提供了数十亿并发低延迟访问单个行记录(随机存储)
,HBase内部使用哈希表来提供随机接入
,并且其存储索引
,可以对 HDFS 文件中的数据进行快速查找。
面向行和面向列
- 行式:
- 列式:
可以很简单的发现,无非就是把每列抽出来,然后关联上Id。这个就叫列式存储吗?与行式存储有什么区别呐? - 可以看到以前的一行记录有多个属性列, 有部分的列是空缺的,但是我们还是需要留下存储的空间,但是在列式存储下就没有了这个问题,有什么才存什么,类似于 KV 结构
HBase的数据模型
HBase里边也有表、行和列
的概念。(一行数据由一个行键和一个或多个相关的列以及它的值所组成
)
- 在HBase里边,定位一行数据会有一个唯一的值,这个叫做 行键(RowKey)。
所以 HBase 中列的相关概念:
- 在 HBase 里边,
先有列族,后有列
。HBase的列(Column)都得归属到列族(Column Family)中。在HBase中用列标识符(Column Qualifier)来标识每个列。
具体到上面的场景就是:
在这里可以看出,如果我的 UserInfo:age 为空,那么这一列就没有,如果我要在 OrderInfo 列族中增加一行,那么就很简单的增加一列数据就行了。
换句话说:一个列族下可以任意添加列,不受任何限制
Hbase也是:数据写到HBase的时候都会被记录一个时间戳,这个时间戳被我们当做一个版本。比如说,我们修改或者删除某一条的时候,本质上是往里边新增一条数据,记录的版本加一了而已。(任何事情都几乎殊途同归啊
)
HBase 的Key-Value
HBase本质上其实就是Key-Value的数据库,上一次我们学Key-Value数据库还是Redis呢。那在HBase里边,Key是什么?Value是什么?
- Key :就是 RowKey(行键)+ColumnFamily(列族)+Column Qualifier(列修饰符)+TimeStamp(时间戳–版本)
- value:就是对应的数据。
具体在工作中:我们的 rowKey 是 reverse(requestId)-sceneId,列族是 f,列名是对应的字段名。
HBase 的架构
- Client客户端,它提供了访问HBase的接口,并且维护了对应的cache来加速HBase的访问。
- Zookeeper存储HBase的元数据(meta表),无论是读还是写数据,都是去Zookeeper里边拿到meta元数据告诉给客户端去哪台机器读写数据
- HRegionServer它是处理客户端的读写请求,负责与HDFS底层交互,是真正干活的节点。
HRegionServer 内部结构
前面也提到了,HBase可以存储海量的数据,HBase是分布式的。所以我们可以断定:HBase一张表的数据会分到多台机器上的。那HBase是怎么切割一张表的数据的呢?用的就是RowKey来切分,其实就是表的横向切割。
由上图可以看出,HRegionServer 中会有多个 HRegion 。
RowKey 的设计
- RowKey 要保证不重复。
Hbase 中的查询方式
- 全局扫描
- 根据一个 RowKey 进行查询
- 根据 RowKey 过滤的范围查询
(1)根据一个RowKey进行查询
首先我们要知道的是RowKey是会按字典序排序的,我们HBase表会用RowKey来横向切分表。
所以无论是读和写我们都是用 RowKey 去定位到 HRegion,然后找到 HRegionServer。
需要注意会存在热点key问题。可以通过进行加盐或者哈希的处理,这样很大程度上可以缓解数据热点问题。像我们的requestId 就是:
RequestId 用 62(26+26+10)位编码,机器(2^ 32)+时间(2^ 64)+线程(2^ 64)+业务信息+随机种子(2^4)基本上 32位string就可以保留很多信息了,既可以节约存储,后面设计分布式存储的时候主key短也可以提高性能。可以缩短长度,但是排查问题就需要反编译出机器名,可以参考下
整体参考了: https://zhuanlan.zhihu.com/p/145551967
核心实现原理
HBase原理 | HBase核心原理与应用场景
LSM-Tree(日志结构合并树)
LSM-Tree 日志结构合并树(顺序写磁盘)
适用场景
- 写多读少
特征
先写 WAL 日志,再将数据写到写缓存 MemStore 中,等写缓存达到一定规模后或满足其他触发条件才会 flush 刷写到磁盘,这样就将磁盘随机写变成了顺序写,提高了写性能。每一次刷写磁盘都会生成新的 HFile 文件。
因此写数据就是:
先写WAL日志,再将数据写到写缓存MemStore中,等写缓存达到一定规模后或满足其他触发条件才会flush刷写到磁盘,这样就将磁盘随机写变成了顺序写,提高了写性能。每一次刷写磁盘都会生成新的HFile文件。
整体的读取路径就是 先读取 Co-tree,再依次往上升级读取。假设 Ck 中存储的是 a=5,C2 中存储的是 a = 100,那么查询的时候不会查询到 Ck,只会到 C2,随着后续 树的迭代,就会将旧的数据进行更新并删除的操作。