大数据技术原理与应用(第四章 分布式数据库HBase)
目录
4.1HBase简介
HBase设计原因
HBase与传统关系型数据库的联系与区别
HBase访问接口
4.2HBase数据模型
数据坐标
HBase数据的概念视图
HBase数据的物理视图(以列族为单位进行存储)
行式存储结构与列式存储结构
面向行与面向列的存储优势与缺点
4.3HBase实现原理
HBase的功能组件
HBase三层结构实现Region的寻址和定位
4.4HBase运行机制
HBase系统架构
客户端
Zookeeper服务器
Master(主服务器)
Region服务器
用户读写数据过程
StoreFile的合并与分裂过程
HLog的工作原理
4.5HBase应用方案
HBase在实际应用中的性能优化方法
提升读写性能
节省存储空间
HBase怎么检测性能
Master-status
Ganglia
OpenTSDB
Ambari
HBase之上如何构建SQL引擎和HBase二级索引
Coprocessor
HBase+Redis
HBase+Solr
HBase常用Java API编程实践
4.1HBase简介
HBase是BigTable的官方开源实现最初为了解决大规模网页搜索相关问题
网页搜索:首先构建整个网页的信息索引,并开发一个专门用于抓取页面内容的网页抓取器;随后将收集到的页面存入BigTable存储系统中,并通过MapReduce算法进行分布式处理;接着在互联网上执行搜索引擎查询以获取相关信息,并响应并接收用户提交的搜索请求;最后从BigTable数据库中检索相关页面内容,并将检索到的结果返回给用户查看。
BigTable是一种基于谷歌分布式文件系统GFS的分布式存储系统(该系统专门用于存储完全非结构化的数据)。它通过Chubby实现协同管理服务。该系统不仅能够高效处理海量数据(支持PB级规模的数据量),而且通过集群技术实现了大规模分布式存储能力。
HBase是一种高度可靠的分布式数据库,在高效性和弹性扩展方面均表现出色,并特别适用于以列方向存储非结构化与半结构化 loosely structured data. 该系统基于 HDFS 文件存储架构,并通过 Zookeeper 实现协作管理功能. 同时, 借助 MapReduce 技术, HBase 可有效地处理海量数据.
HBase设计原因
利用HDFS与MapReduce技术基础的支持下,Hadoop系统能够有效实现大规模数据离线批量处理功能;但该系统无法应对大数据环境下的实时处理需求。面对数据以指数级速度快速增长的情况,在传统关系型数据库体系中往往难以充分释放其扩展性能潜力。
HBase与传统关系型数据库的联系与区别
在数据类型的管理上:传统关系型数据库采用的是经典的关系统计学模型,在其中包含了多种数据类型的配合存储方式(如整数、字符等);而HBase则将所有存储的数据设定为未经解码的基本字符串形式(即字节体),其数据类型的定义依仗应用开发者在读取时自行解释。
在关系型数据库中定义了大量具体的数据操作类型(如运算功能与关联操作等);HBase仅支持基本的数据插入、查询、删除与清空操作;各为独立的数据实体的数据库不支持数据间的关联操作。
存储模型:关系型数据库基于行模式存储;HBase基于列存储。
数据索引方面:关系型数据库专门针对不同列的数据关系建立高度复杂且全面的索引方案;HBase仅限于为行键设计简单的索引结构,并不具备基于列的关键字组合进行高级查询的能力。
数据维护方面:在关系型数据库的更新操作中,在一定时间内(即更新周期内),旧的数据会被新的数据立即取代;而HBase的操作机制则通过增加带有时间戳的新记录来实现这一目标;它会将原始记录保留在版本集中,并且只有当记录的时间超过预设的时间限制时才会删除。
可伸缩性方面(可拓展性):关系型数据库难以实施水平扩展,在实际应用中主要通过增加计算资源来实现纵向扩展(如增加CPU数量、升级单核至双核、升级双核至四核等),而小型机向大型机的升级也仅能完成基本规模的扩展;基于分布式集群的HBase能够通过分布式存储架构存储海量数据,并且相对容易实施水平扩展。
HBase访问接口
为了实现异构系统在线访问而设计的一个内置Java API;通过Thrift Gateway实现了异构系统在线访问;提供了RESTful服务接口(REST Gateway)。
不进行任何改动操作
4.2HBase数据模型
HBase采用稀疏矩阵作为其基础的数据存储结构,并具有多维有序结构;该系统利用行键、列家族、属性标记符以及时间戳等四个关键要素来定位存储的数据内容;每个数据值都是未做解码处理的原始字符串类型(Bytes数组形式);该系统支持灵活扩展其列家族结构;每个记录包含一个Rowkey字段和多个字段组合。

因此,在更新数据时保留旧版本的原因在于:基于分布式文件系统的HDFS存储机制,并且该系统仅支持在现有数据基础上进行扩展而不允许对已有数据进行直接的修改操作。
数据坐标
其定位特征:基于一行一列两维空间可唯一标识数据;
HBase对数据的定位采用四维空间模型,在这一架构下可以通过行、列族以及列限定符(即列)来唯一地标识一个单元格的位置(即具体存储数据的地方)。每个单元格中的数据则依据其所属的时间戳来区分不同的版本信息。
HBase数据的概念视图

HBase数据的物理视图(以列族为单位进行存储)

行式存储结构与列式存储结构

面向行与面向列的存储优势与缺点
对于传统的事务型操作,在每次执行一次数据插入操作时,在OLTP系统中会将这条记录的各项信息存储到数据库中,并生成一条完整的记录;为了从记录中获取某一特定信息特征(针对某一列)的数据进行分析,在处理过程中需要逐一查看每一行的数据,并提取所需的信息字段。
面向列:按照列组织数据;每个列中的数据通常具有相似的结构;能够实现较高的数据压缩效率;有助于提高分析型应用的性能。
4.3HBase实现原理
HBase的功能组件
库函数:一般用于链接每个客户端。
Master服务器充当集群管理者角色,在数据分区层面实施全面运维方案;构建并管理一个Region服务器集合(实时监控集群内各Region服务器的状态,并记录其运行情况与故障记录);根据系统需求动态分配资源到各个Region;确保系统资源在各节点之间均衡分布
该系统通过区域划分实现数据分布式存储与管理方案。在大数据量环境下,默认完整的表会被划分为若干个独立的小区域以提高存储效率并降低单点故障风险。每个区域 server 负责对应区域的数据维护与逻辑处理功能。客户端在访问数据资源时会直接与对应的 region 进行交互操作而不必依赖 master server 来获取地理位置信息

当一个大的Region被分割为多个新的小Region时,无需实际进行物理分离操作,而是通过调整数据引用路径的方式即可完成,这种情况下,数据依然保留在原始物理位置上,从而使得区域划分变得更加高效。(划分规则规定同一个Region不会分配到不同的Region服务器上)(单个Region服务器一般能够存储约10至1000个独立的Regions)
HBase三层结构实现Region的寻址和定位

第一层:Zookeeper文件,记录了-ROOT-表的位置信息;
第二层:-ROOT-表用于存储.META.表中关于Region的位置信息,并且作为元数据信息存储的位置参考;仅允许存在一个Region以防止数据冲突;通过-.ROOT.-表能够访问.META.-表中的具体数据信息
第三层:.META:.表格表示该系统中所有用户相关数据分布的信息,其中包含了关于这些分布的基本元数据,具体包括:在该.META:表格中设置了以下字段,第一列为_region_id_,用于标识对应的数据分区;第二列为_server_id_,用于标识对应的数据分区所在的服务器;并且每个.META:表格都可以包含多个这样的分区配置项
以提高访问速度为目标,在.META.表中存储所有Region于内存中;客户端将 cached position information stored in memory. 当 cached information becomes invalid时, 系统将重新经历一次三级寻址流程以确定新的 position information.
4.4HBase运行机制
HBase系统架构

客户端
包含访问HBase的接口
Zookeeper服务器
构建协同管理功能并担任管理员角色;该集群负责管理和维护整个HBase集群,在分布式系统中广泛应用,并提供配置支持、域名注册与维护以及支持分布式同步操作。
Master(主服务器)
该集群整体上进行Region相关的事务管理工作;涵盖表的各种增删改查操作;确保各区域服务器间的负载均衡配置;优化分裂与合并后的工作数据分布情况;在出现故障或失效时及时处理资源的重新分配问题。
Region服务器
负责用户数据的存储和管理,与客户端直接进行数据的交互。
Region服务器负责向HDFS文件系统执行读写操作。各个Region服务器共享一个单独的HLog日志记录。当单个Region处理数据时会采用列族切分策略。存储过程首先将数据 writes into MemStore缓存区,在MemStore存储空间耗尽时会触发WriteToStoreFile磁盘操作。位于底层的StoreFile对象通过使用HDFS实现其存储功能。

用户读写数据过程
用户将输入的数据会被分配到某个Region服务器上进行处理;在数据的 write 过程中,在确保 data 的安全性和恢复性的前提下,
首先会在日志文件 HLog 中进行记录,
接着启动缓存 MemStore 文件的 writes,
当 HLog 中所有的 record 已经成功地保存至磁盘之后,
才允许客户端进行 call 并返回 result。
当用户读取数据时,首先也会定位到存储该Region的数据服务器,并从缓存中获取对应的MemStore文件。如果缓存记录中未找到相关信息,则会转而访问磁盘上的StoreFile位置以获取所需数据。
当系统检测到缓存刷新过程时, 会触发将 MemStore 中的数据按照预定周期自动完成复制至磁盘上的 StoreFile 文件, 并清除内存中的数据以避免重复引用, 同时在 HLog 中记录最新的操作状态; 每一次刷新操作都会创建一个新的 StoreFile 文件实例; 在启动前, 每个 Region 服务器都会查看其共享 HLog 文件, 以确认最近一次执行缓存刷新操作后是否进行了后续的操作记录; 如果发现存在更新信息, 系统将会依次完成 MemStore 数据的临时存储、新 StoreFile 文件的生成以及旧日志文件的删除操作, 最终为用户提供服务.
StoreFile的合并与分裂过程

HLog的工作原理
基于HBase构建的数据集群用于管理存储资源作为典型的分布式计算架构底层采用了经济型低性能计算节点这样的配置必然会导致集群运行中出现服务中断因此系统设计者必须在保证可靠性的前提下制定合理的容错机制例如通过日志系统来辅助实现数据恢复功能具体而言(Zookeeper负责实时监控整个集群的状态)当检测到单个节点出现服务故障时会立即通知Master服务器后者则会利用各故障节点的日志记录来逐步重建丢失的数据块The replication strategy of HLog files ensures high throughput during data writes
4.5HBase应用方案
HBase在实际应用中的性能优化方法

提升读写性能
配置HColumnDescriptor.setInMemory选项为true,并将相关数据表迁移到Region服务器的内存缓存区;依据预设条件判断后决定该数据项是否保留在缓存中
节省存储空间
配置最大版本数量为1,并将HColumnDescriptor.setMaxVersions设置为1以保留最新的数据字段,并不再删除旧的版本数据。
定期对数据进行清理工作,在未达到最大版本数的前提下,并非所有情况都需要长时间存储的数据。
配置适当的TimeToLive参数后,
一旦超出预设的时间限制就会标记为已过期,
并由系统自动删除。
HBase怎么检测性能
Master-status
HBase集成了一套工具包, 通过Web界面进行查询HBase的运行状态, 在浏览器中可以直接访问该地址进行查看.
Ganglia
UC Berkeley推出了一个开源集群监视项目;该系统专为监控各种系统的运行效率而设计;并且能够兼容HBase进行性能监控。
OpenTSDB
可以通过大规模集群的数据采集系统自动完成关键的性能参数收集,并将这些数据经过处理后以直观可视化的形式展示给管理员
Ambari
是Hadoop架构上的一个产品,作用是创建管理监视整个Hadoop集群。
HBase之上如何构建SQL引擎和HBase二级索引
Hive,与HBase接口互相通信实现对HBase的访问
Phoenix serves as a SQL intermediary, built upon the Apache HBase platform. This product enables developers to execute SQL queries directly on the HBase platform.
原生HBase产品未内置列索引功能,默认仅提供单个行键作为基础索引机制:基于单个行键实现基础数据存储;设定一个行键的起始范围和终止范围以实现区间数据查询;在全表范围内进行扫描操作以完成完整数据检索。
Coprocessor
HBase通过支持Coprocessor特性来构建二级索引系统,并将其划分为两个关键组件:Endpoint和Observer。Endpoint类似于传统关系型数据库中的存储过程,在数据存入主表的同时也会执行特定操作;Observer则类似于触发机制,在记录插入前后完成必要的同步操作以更新相关索引信息。这种设计的优势在于实现了非侵入性架构,在不影响主数据库结构的前提下提升了查询效率;然而其缺点也显而易见:每次插入新数据都需要同时同步修改对应的Index表条目,这不仅会导致处理时间加倍,还可能对整个HBase集群的性能产生一定的压力。

HBase+Redis
该系统是一种基于键值对的数据存储解决方案,在设计上实现了快速处理并存储大量键值对数据的能力。通过Redis缓存层来组织和维护索引信息,并通过特定机制将这些索引信息同步到HBase的基础数据存储层。

HBase+Solr
Solr是由高效率的Lucene技术开发的高性价比全文搜索服务器。它通过建立元数据索引实现快速检索功能,并支持增量式爬取机制以确保数据完整性与实时性相结合。

HBase常用Java API编程实践
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
public class ExampleForHBase {
//声明一些静态变量
public static Configuration configuration;
public static Connection connection;
public static Admin admin;
public static void main(String[] args)throws IOException{
//构建便捷初始化对象
init();
//表和数据的相关方法
createTable("student",new String[]{"score"});
insertData("student","zhangsan","score","English","69");
insertData("student","zhangsan","score","Math","86");
insertData("student","zhangsan","score","Computer","77");
getData("student", "zhangsan", "score","English");
close();
}
public static void init(){
//生成配置对象并设置相关参数
configuration = HBaseConfiguration.create();
configuration.set("hbase.rootdir","hdfs://localhost:9000/hbase");
//连接对象
try{
connection = ConnectionFactory.createConnection(configuration);
admin = connection.getAdmin();
}catch (IOException e){
e.printStackTrace();
}
}
//关闭数据库连接
public static void close(){
try{
if(admin != null){
admin.close();
}
if(null != connection){
connection.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
//创建表和列族,其中对列族进行遍历加入表中
public static void createTable(String myTableName,String[] colFamily) throws IOException {
TableName tableName = TableName.valueOf(myTableName);
if(admin.tableExists(tableName)){
System.out.println("talbe is exists!");
}else {
TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(tableName);
for(String str:colFamily){
ColumnFamilyDescriptor family =
ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(str)).build();
tableDescriptor.setColumnFamily(family);
}
admin.createTable(tableDescriptor.build());
}
}
//插入数据包括表名称,行键,列族,列,值
public static void insertData(String tableName,String rowKey,String colFamily,String col,String val) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(rowKey.getBytes());
put.addColumn(colFamily.getBytes(),col.getBytes(), val.getBytes());
table.put(put);
table.close();
}
//获取数据的返回结果被封装在result中
public static void getData(String tableName,String rowKey,String colFamily, String col)throws IOException{
Table table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(rowKey.getBytes());
get.addColumn(colFamily.getBytes(),col.getBytes());
Result result = table.get(get);
System.out.println(new String(result.getValue(colFamily.getBytes(),col==null?null:col.getBytes())));
table.close();
}
}
