大数据技术原理与应用 - (4). 分布式数据库 HBase
【第二篇】 - 大数据存储与管理, 《大数据技术原理与应用, 林子雨》
本篇介绍大数据存储与管理相关技术的概念与原理,包括
- 第3章 - Hadoop 分布式文件系统 (HDFS)
- 第4章 - 分布式数据库 (HBase)
- 第5章 - NoSQL 数据库
HBase 是针对 Google BigTable 的开源实现,是一个高可靠、高性能、面向列、可伸缩的分布式数据块,主要用来存储非结构化和半结构化的松散数据。
本章介绍 HBase 与关系型数据库的区别、访问接口、数据模型、实现原理和运行机制。
HBase 简介
HBase 是针对 Google BigTable 的开源实现,是一个高可靠、高性能、面向列、可伸缩的分布式数据块,主要用来存储非结构化和半结构化的松散数据。
HBase 与传统数据库的区别
传统关系数据库 |
HBase |
|
---|---|---|
数据类型 | 关系模型,具有丰富的数据类型和存储方式。 | 采用了更加简单的数据模型,它把数据存储为未经解释的字符串。 |
数据操作 | 包含了丰富的操作,如增删改查等,还会涉及复杂的多表连接。 | 不存在表与表的关系,只有简单的插入、查询、删除、清空等操作。 |
存储模式 | 基于行模式存储,元组或行会被连续地存储在磁盘页中。在读取数据时,需要顺序扫描每个元组。 | 基于列存储的,每个列族都由几个文件保存,不同列族的文件是分离的。 |
数据索引 | 通常可以针对不同列构建复杂的多个索引 (主索引 + 多个二级索引),以提高数据访问性能。 | 只有一个索引——行键。 |
数据维护 | 更新操作会用最新的当前值去替换记录中原来的旧值,旧值被覆盖后就不会存在。 | HBase中执行更新操作时,并不会删除数据旧的版本,而是生成一个新的版本,旧有的版本仍然保留。 |
可伸缩性 | 很难实现横向扩展 (增加机器),纵向扩展 (升级内存、cpu 等) 的空间也比较有限。 | 为了实现灵活的水平扩展而开发的,能够轻易地通过在集群中增加或者减少硬件数量来实现性能的伸缩。 |
HBase 访问接口
类型 |
特点 |
场合 |
---|---|---|
Native Java API | 最常规和高效的访问方式 | 适合 Hadoop MapReduce 作业并行批处理 HBase 表数据 |
HBase Shell | HBase 的命令行工具,最简单的接口 | 适合 HBase 管理使用 |
Thrift Gateway | 利用 Thrift 序列化技术,支持 C++、PHP、Python 等多种语言 | 适合其他异构系统在线访问 HBase 表数据 |
REST Gateway | 解除了语言限制 | 支持 REST 风格的 Http API 访问 HBase |
Pig | 使用 Pig Latin 流式编程语言来处理 HBase 中的数据 | 适合做数据统计 |
Hive | 简单 | 当需要以类似 SQL 语言方式来访问 HBase 的时候 |
HBase 数据模型
- HBase 是一个稀疏、多维度、排序的映射表,这张表的索引是行键 (Row Key)、列族 (Column Family)、列限定符 (Column Qualifier) 和时间戳 (Timestamp) 来进行索引
- 每个值是一个未经解释的字符串,没有数据类型
- 用户在表中存储数据,每一行都有一个可排序的行键和任意多的列
- 表在水平方向由一个或者多个列族组成,一个列族中可以包含任意多个列,同一个列族里面的数据存储在一起
- 列族支持动态扩展,可以很轻松地添加一个列族或列,无需预先定义列的数量以及类型,所有列均以字符串形式存储,用户需要自行进行数据类型转换
- HBase中执行更新操作时,并不会删除数据旧的版本,而是生成一个新的版本,旧有的版本仍然保留(这是和HDFS只允许追加不允许修改的特性相关的)
数据模型的相关概念
概念 |
描述 |
---|---|
表 | HBase 采用表来组织数据,表由行和列组成,列划分为若干了列族。 |
行 | 表由若干行组成,行由行键 (Row Key) 来标识。访问方式:1) 单个行键访问;2) 行键区间访问;3) 全表扫描 |
列族 | 1. 表被份组成许多列族的集合,它是基本访问控制单元。列族需要在创建时定义好,数量不能太多,不能频繁修改,存储在一个列族下的所有数据都属于同一数据类型。 2. 表中的列都归属于某个列族下,数据存放在列族的某个列下。 |
列限定符 | 列族中的数据通过列限定符 (或列) 来定位。不用事先定义,无需再不同行间保持一致,没有数据类型。 |
单元格 | 单元格 (cell) 存储的数据没有数据类型 (未经解释的字符串) ,格中可以保存一个数据的多个版本,每个版本对应一个不同的时间戳。 |
时间戳 | 每次对一个单元格执行操作 (新建、修改、删除) 时,HBase 都会隐式地自动生成并存储一个时间戳,用于定位格中不同版本地数据。 |
数据坐标
HBase 用四维坐标定位一个单元格: [行键, 列族, 列限定符, 时间戳]。
如果把思维坐标当作整体,可以视为 “键”,单元格中的数据视为 “值” 的话,HBase可以看作一个键值数据库。
键 | 值 |
---|---|
[“201505003”, “Info”, “email”, “1174184619081”] | “xie@qq.com“ |
[“201505003”, “Info”, “email”, “1174184620720”] | “you@163.com“ |
视图概念
HBase 表的概念试图中,每行都包含相同的列族,但是行不需要在每个列族里存储数据。从这个角度看,HBase 表是一个稀疏的映射关系,即里面存在很多空的单元格。
物理视图
物理存储层面,HBase 采用了基于列的存储方式,不像传统关系型数据库那样采用基于行存储的方式,这是 HBase 与传统数据库的重要区别。因此概念视图中的数据在物理层面实际上被存成了两个小片段,也就是 HBase 表按照 contents
和 anchor
两个列族分别存放,属于同一列族的数据保存在一起,同时每个列族一存放的还有行键和时间戳。
面向列的存储
列式数据库采用 DSM (Decomposition Storage Model) 存储模型,DSM 会对关系进行垂直分解,并为每个属性分配一个子关系。因此,一个具有 $n$ 个属性的关系会被分解为 $n$ 个子关系,每个子关系单独存储,每个子关系只有当前相应的属性被请求时,才会被访问。
DSM 是以关系数据库中的属性或列为单位进行存储的,关系中多个元组的同一属性值会被存储在一起,而一个元组中的不同属性值通常会被存放在不同的磁盘页中。 HBase 是以列族为单位进行分解,而不是每个列都不单独存储。
行存储:数据按行存储在底层文件系统中。通常,每一行会被分配固定的空间。
- 优点:有利于增加、修改整行记录等操作;有理由数据的读取操作。
- 缺点:单列查询时,会读取一些不必要的数据。
列存储:数据以列为单位,存储在底层文件系统中。
- 优点:有利于面向单列数据的读取、统计等操作。
- 缺点:整行读取时,可能需要多次I/O操作。
适用场景
- 行式数据库:适合小批量得数据处理,如联机事务型数据处理。主要用于如银行个人信息数据库之类得场景,对数据不出错要求较高。
- 列式数据库:适合批量数据处理和即席查询 (Ad-Hoc Query)。主要用于数据挖掘、决策支持、地理信息系统等查询密集型系统中,因为每次查询都不必遍历所有数据库。
为什么列式存储适合数据分析
- 因为分析中常常是以一个一个属性 (也就是列) 进行分析的,而列式数存储可以直接取出需要分析的那一列数据。相反地,行式数据库中如果需要取出某一列 (例如性别、年龄) 进行分析,需要扫描整个数据包才能取出对应数据。
HBase 存储原理
HBase 功能组件
HBase的实现包括三个主要的功能组件:
- 库函数:链接到每个客户端
- 一个 Master 服务器
- 负责管理和维护 HBase 表的分区信息,如一个表被分成了哪些 Region 和被存放到哪台 Region 服务器。
- 负责维护 Region 服务器列表,如监测 Region 服务器,确保它们之间负载均衡;对故障 Region 服务器中存储的 Region 进行重新分配。
- 处理模式变化,如表和列族的创建。
- 多个 Region 服务器
- 负责存储和维护分配给自己的 Region。
- 处理来自客户端的读写请求。
- 客户端并不依赖 Master,而是通过 Zookeeper 来获得 Region 位置信息,大多数客户端甚至从来不和 Master 通信,这种设计方式使得 Master 负载很小。
表和 Region
- HBase 中存储了许多表。一个 HBase 表根据行键的字典序又被划分为许多 Region。
- Region 是 HBase 分布式存储的最基本单元
- 随着数据不断插入,Region 会持续增大,当 Region 中行数达到阈值
hbase.hregion.max.filesize
,就会自动分裂成两个新的 Region。- 快速分裂,一般在2-3S内完成,因为只是修改只想信息,实际数据还是存在旧的 Region。
- 直到”合并”过程把存储文件异步地写到独立的文件之后才会读取新文件。
- 每个 Region 的最佳大小取决于单台服务器的有效处理能力。
- 目前推荐 1~2GB 以上 (2013年后的硬件水平)
- 不同的 Region 可以分布在不同的 Region 服务器上
- 同一个 Region 不会被分拆到多个 Region 服务器
Region 定位
HBase 表中有多个 Region,它们会被分发到不同的 Region 服务器上。因此,HBase 设计了三层结构实现 Region 的寻址和定位。
- Zookeeper: 文件记录了 -ROOT- 表 的位置信息。
- -ROOT- 表: 记录. META. 表的 Region 的位置信息 (因为 .META. 表增大后会分裂成多个 Region)。
- -ROOT- 表不能被分割,只有唯一一个 Region。
- .META.表 (元数据表): 存储了 Region 和 Region 服务器的映射关系。
- 当 HBase 表很大时, .META.表也会被分裂成多个 Region。
为了加快访问速度,.META. 表的全部 Region 都会被保存在内存中。
这样的设计能存多少数据?
假设 .META. 表中每行 (每个映射条目) 在内存中大约占 1 KB,每个 Region 限制为 128 MB。
- 一个 -ROOT- 表只有一个 Region, i.e., 128 MB 大小:
- 128 MB 可以存 $128 MB/ 1KB = 2^{17}$ 行映射条目。
- 也就是 一个 -ROOT- 表可以寻址 $2^{17}$ 个 .META. 表的 Region 位置信息。
- 同理,一个 .META. 表的 Region 可以寻址用户数据表的 Region 个数是 $128 MB/ 1KB = 2^{17}$。
- 最终三层结构可以保存的 Region 数目是 $(128 MB/ 1 KB) \times (128 MB/ 1 KB) = 2^{34}$ 个 Region。
客户端访问数据时的“三级寻址”:
- 为了加速寻址,客户端会缓存位置信息,这样可以直接从客户端缓存中获取 Region 位置信息。
- 解决缓存失效问题: 惰性监测。当访问数据时,发现缓存中 Region 位置信息不存在,才会判断出缓存失效。然后再重新启动上面的“三级寻址”过程重新获取最新的 Region 信息。
- 寻址过程客户端只需要询问 Zookeeper 服务器,不需要连接 Master 服务器。
HBase 的运行机制
HBase 系统架构
HBase 系统架构包含下图组件。由于 HBase 通常采用 HDFS 作为底层数据存储,因此下图加入了 HDFS 和 Hadoop。
- 客户端 (Client)
- 包含访问 HBase 的接口,同时在缓存中维护着已经访问过的 Region 位置信息,用来加快后续数据访问过程。
- Zookeeper
- 实现集群的协同管理服务。Zookeeper 可以不是一台机器,可以是多台机器构成的集群。
- Zookeeper 可以帮助选举出一个 Master 作为集群的总管,并保证在任何时刻总有唯一一个 Master 在运行,避免了 Master 的“单点失效”问题。
- Master: 主服务器Master主要负责表和Region的管理工作
- 管理用户对表的增加、删除、修改、查询等操作。
- 实现不同 Region 服务器之间的负载均衡。
- 在 Region 分裂或合并后,负责重新调整 Region 的分布。
- 对发生故障失效的 Region 服务器上的 Region 进行迁移。
- Region 服务器
- 负责维护分配给自己的 Region,并响应用户的读写请求。
Region 服务器工作原理
各个组件:
- 每个 Region 服务器中的若干个 Region 公用一个
HLog
- 每个 Region 中由多个
Store
组成- 每个
Store
对应表中一个列族的存储 - 一个
Store
包含一个MemStore
和若干个StoreFile
- 每个
MemStore
是在内存中缓存,保存最近更新的数据StoreFile
是磁盘中的文件,这些文件都是 B树的结构,方便快速读取。StoreFile
在底层的实现方式 HDFS 文件系统的HFile
用户读写数据的过程
- 当用户写入数据时,会被分配到相应Region服务器去执行。
- 用户数据首先被写入到
MemStore
和HLog
中。 - 只有当操作写入
Hlog
之后,commit()
调用才会将其返回给客户端。
- 用户数据首先被写入到
- 当用户读取数据时,Region 服务器会首先访问
MemStore
缓存,如果找不到,再去磁盘上面的StoreFile
中寻找。
缓存的刷新
MemStore
缓存容量有限,系统会周期性地调用Region.flushcache()
把MemStore
缓存里地内容写到磁盘地StoreFile
文件中,并清空缓存,同时在HLog
文件中写入一个标记。- 每个 Region 服务器都有一个自己的
HLog
文件,每次启动都检查该文件,确认最近一次执行缓存刷新操作之后是否发生新的写入操作;如果发现更新,则先写入MemStore
,再刷写到StoreFile
,最后删除旧的Hlog
文件,开始为用户提供服务。
StoreFile 的合并与分裂
- 随着
StoreFile
数量增加,达到预先设定的阈值后,系统会触发合并操作,将多个StoreFile
合并成一个大的StoreFile
。- 每次刷写都生成一个新的
StoreFile
,数量太多,影响查找速度。 - 因此系统会调用
Store.compact()
把多个StoreFile
合并成一个大文件。 - 合并操作比较耗费资源,因此只有
StoreFile
文件数量达到一个阈值才启动合并操作。
- 每次刷写都生成一个新的
- 单个
StoreFile
过大时,又触发分裂操作,1个父 Region 被分裂成两个子 Region。
HLog 工作原理
分布式环境必须要考虑系统出错。HBase 采用 HLog
保证系统恢复。
- HBase 系统为每个 Region 服务器配置了一个 HLog 文件,它是一种预写式日志(Write Ahead Log)
- 用户更新数据必须首先写入日志后,才能写入
MemStore
缓存,并且,直到MemStore
缓存内容对应的日志已经写入磁盘,该缓存内容才能被刷写到磁盘 - Zookeeper 会实时监测每个 Region 服务器的状态,当某个 Region 服务器发生故障时,Zookeeper 会通知 Master
- Master 首先会处理该故障 Region 服务器上面遗留的
HLog
文件,这个遗留的HLog
文件中包含了来自多个 Region 对象的日志记录 - 系统会根据每条日志记录所属的 Region 对象对
HLog
数据进行拆分,分别放到相应 Region 对象的目录下,然后,再将失效的 Region 重新分配到可用的 Region 服务器中,并把与该 Region 对象相关的HLog
日志记录也发送给相应的 Region 服务器 - Region 服务器领取到分配给自己的 Region 对象以及与之相关的
HLog
日志记录以后,会重新做一遍日志记录中的各种操作,把日志记录中的数据写入到MemStore
缓存中,然后,刷新到磁盘的StoreFile
文件中,完成数据恢复
共用日志优点:提高对表的写操作性能;缺点:恢复时需要分拆日志
HBase实际应用中的性能优化方法
行键(Row Key)
行键是按照字典序存储,因此,设计行键时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。InMemory
创建表的时候,可以通过HColumnDescriptor.setInMemory(true)
将表放到 Region 服务器的缓存中,保证在读取的时候被 cache 命中。Max Version
创建表的时候,可以通过HColumnDescriptor.setMaxVersions(int maxVersions)
设置表中数据的最大版本,如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1)
。Time To Live
创建表的时候,可以通过HColumnDescriptor.setTimeToLive(int timeToLive)
设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)
。
- Title: 大数据技术原理与应用 - (4). 分布式数据库 HBase
- Author: Zhanhang (Matthew) ZENG
- Link: https://zengzhanhang.com/2020/05/21/intro2BigData4/
- Released Date: 2020-05-21
- Last update: 2020-12-29
- Statement: All articles in this blog, unless otherwise stated, are based on the CC BY-NC-SA 4.0 license.