2
头图

一、HBase基础

HBase 是一种建立在 Hadoop 文件系统之上的分布式、可扩展、支持海量数据存储的 NoSQL 数据库。HBase 是 BigTable 的开源 Java 版本。是建立在 HDFS 之上,提供高可靠性、高性能、列存储、可伸缩、实时读写 NoSql 的数据库系统。

它介于 NoSql 和 RDBMS 之间,仅能通过主键(rowKey)和主键的 range 来检索数据,仅支持单行事务(可通过 Hive 支持来实现多表 join 等复杂操作)。主要用来存储结构化和半结构化的松散数据。HBase 查询数据功能很简单,不支持 join 等复杂操作,不支持复杂的事务(行级的事务)。

HBase 不限制存储的数据的种类,允许动态的、灵活的数据模型,不用 SQL 语言,也不强调数据之间的关系。HBase被设计成在一个服务器集群上运行,可以相应地横向扩展。利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群。

HBase 本身是一个数据模型,类似于谷歌的大表设计(BIgtable),可以提供快速随机访问海量结构化数据。它利用了 Hadoop 的文件系统(HDFS)提供的容错能力,提供对数据的随机实时读/写访问,是Hadoop文件系统的一部分。 人们可以直接或通过 HBase 存储 HDFS 数据,也可以使用 HBase 在 HDFS 读取消费/随机访问数据。 HBase 利用 Hadoop 的 MapReduce 来处理海量数据。协同服务方面 Google Bigtable 利用 Chubby 来支持 ,HBase 的 Zookeeper 与之对应。

HBase 中的表一般有这样的特点:

  • 大:一个表可以有上十亿行,上百万列。
  • 面向列:面向列(族)的存储和权限控制,列(族)独立检索。
  • 稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏。

二、 HDFS 、Hive 、HBase 三者对比

1、HDFS 与 HBase 对比

HDFS

  • 为分布式存储提供文件系统
  • 针对存储大尺寸的文件进行优化,不需要对 HDFS 上的文件进行随机读写
  • 直接使用文件
  • 数据模型不灵活
  • 使用文件系统和处理框架
  • 优化一次写入,多次读取的方式

HBase

  • 提供表状的面向列的数据存储
  • 针对表状数据的随机读写进行优化
  • 使用 key-value 操作数据
  • 提供灵活的数据模型
  • 使用表状存储,支持 MapReduce,依赖 HDFS
  • 优化了多次读,以及多次写

2、Hive 与 HBase 对比

Hive

(1) 数据仓库

Hive 的本质其实就相当于将 HDFS 中已经存储的文件在 Mysql 中做了一个双射关系,以方便使用 HQL 去管理查询。

(2) 用于数据分析、清洗

Hive 适用于离线的数据分析和清洗,延迟较高。

(3) 基于 HDFS、MapReduce

Hive 存储的数据依旧在 DataN ode 上,编写的 HQL 语句终将是转换为 MapReduce 代码执行。

HBase

(1) 数据库

是一种面向列族存储的非关系型数据库。

(2) 用于存储结构化和非结构化的数据

适用于单表非关系型数据的存储,不适合做关联查询,类似 JOIN 等操作。

(3) 基于 HDFS

数据持久化存储的体现形式是 HFile,存放于 DataNode 中,被 ResionServer 以 region 的形式进行管理。

(4) 延迟较低,接入在线业务使用

面对大量的企业数据,HBase 可以直线单表大量数据的存储,同时提供了高效的数据访问速度。

三、HBase在商业项目中的能力

每天:
(1) 消息量:发送和接收的消息数超过60亿
(2) 将近1000亿条数据的读写
(3) 高峰期每秒150万左右操作
(4) 整体读取数据占有约55%,写入占有45%
(5) 超过2PB的数据,涉及冗余共6PB数据
(6) 数据每月大概增长300千兆字节。

四、HBase系统架构

image.png

架构角色:

(1)Region Server

Region Server 为 Region 的管理者,其实现类为 HRegionServer,主要作用如下:

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

(2)Master

Master 是所有 Region Server 的管理者,其实现类为 HMaster,主要作用如下:

  • 负责存储 HBase 的实际数据
  • 处理分配给它的 Region
  • 刷新缓存到 HDFS
  • 维护 HLog
  • 执行压缩
  • 负责处理 Region 分片

(3)Zookeeper

HBase 通过 Zookeeper 来做 Master 的高可用、RegionServer 的监控、元数据的入口以及集群配置的维护等工作。

image.png

(4)HDFS

HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持。

(5)Client

包含访问HBase的接口,并维护cache来加快对HBase的访问。

HRegionServer 组件

1. Write-Ahead logs

HBase 的修改记录。由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。

2. HFile

这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。

3. StoreFile

HFile 存储在 Store 中,一个 Store 对应 HBase 表中的一个列族。保存实际数据的物理文件,StoreFile 以 HFile 的形式存储在 HDFS 上。每个 Store 会有一个或多个 StoreFile(HFile),数据在每个 StoreFile 中都是有序的。

4. MemStore

写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile。

5. Region

Hbase 表的分片,HBase 表会根据 rowKey 值被切分成不同的 region 存储在 RegionServer 中,在一个 RegionServer 中可以有多个不同的 Region。

五、HBase shell操作

1、基本操作

1.进入 HBase 客户端命令行
bin/hbase shell
2.查看帮助命令
hbase(main):001:0> help
3.查看当前数据库中有哪些表
hbase(main):002:0> list

2、表的操作

1.创建表
hbase(main):002:0> create 'student','info'
2.插入数据到表
hbase(main):003:0> put 'student','1001','info:sex','male'
hbase(main):004:0> put 'student','1001','info:age','18'
hbase(main):005:0> put 'student','1002','info:name','Janna'
hbase(main):006:0> put 'student','1002','info:sex','female'
hbase(main):007:0> put 'student','1002','info:age','20'
3.扫描查看表数据
hbase(main):008:0> scan 'student'
hbase(main):009:0> scan 'student',{STARTROW => '1001', STOPROW =>
'1001'}
hbase(main):010:0> scan 'student',{STARTROW => '1001'}
4.查看表结构
hbase(main):011:0> describe ‘student’
5.更新指定字段的数据
hbase(main):012:0> put 'student','1001','info:name','Nick'
hbase(main):013:0> put 'student','1001','info:age','100'
6.查看“指定行”或“指定列族:列”的数据
hbase(main):014:0> get 'student','1001'
hbase(main):015:0> get 'student','1001','info:name'
7.统计表数据行数
hbase(main):021:0> count 'student'
8.删除数据
删除某 rowkey 的全部数据:
hbase(main):016:0> deleteall 'student','1001'
删除某 rowkey 的某一列数据:
hbase(main):017:0> delete 'student','1002','info:sex'
9.清空表数据
hbase(main):018:0> truncate 'student'
提示:清空表的操作顺序为先 disable,然后再 truncate。
10.删除表
首先需要先让该表为 disable 状态:
hbase(main):019:0> disable 'student'
然后才能 drop 这个表:
hbase(main):020:0> drop 'student'
提示:如果直接 drop 表,会报错:ERROR: Table student is enabled. Disable it first.
11.变更表信息
将 info 列族中的数据存放 3 个版本:
hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3}
hbase(main):022:0> get
'student','1001',{COLUMN=>'info:name',VERSIONS=>3}

六、HBase使用场景

首先,HBase 是基于 HDFS 来存储的。

HDFS

  1. 一次性写入,多次读取。
  2. 保证数据的一致性。
  3. 主要是可以部署在许多廉价机器中,通过多副本提高可靠性,提供了容错和恢复机制。

HBase

  1. 瞬间写入量很大,数据库不好支撑或需要很高成本支撑的场景。
  2. 数据需要长久保存,且量会持久增长到比较大的场景。
  3. HBase 不适用与有 join,多级索引,表关系复杂的数据模型。
  4. 大数据量且有快速随机访问的需求。如:淘宝的交易历史记录。数据量巨大无容置疑,面向普通用户的请求必然要即时响应。
  5. 业务场景简单,不需要关系数据库中很多特性(例如交叉列、交叉表,事务,连接等等)。

七、HBase的表数据模型

HBase 逻辑表结构
image.png

HBase 物理存储结构
image.png

(1)Name Space

命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是hbase 和 default,hbase 中存放的是 HBase 内置的表,default 表是用户默认使用的命名空间。

(2)Region

类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。

(3)rowKey

HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要。与 nosql 数据库一样,rowKey 是用来检索记录的主键。访问 hbase table 中的行,只有三种方式:

  • 通过单个 rowKey 访问
  • 通过 rowKey 的 range
  • 全表扫描

rowKey 行键可以是任意字符串(最大长度是 64KB,实际应用中长度一般为10-100bytes),在 hbase 内部,rowKey 保存为字节数组。Hbase 会对表中的数据按照 rowkey 排序(字典顺序)存储时,数据按照 Row key 的字典序(byte order)排序存储。设计 key 时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)。

(4) Column Family

HBase 表中的每个列,都归属于某个列族。列族是表的 schema 的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history , courses:math 都属于courses 这个列族。访问控制、磁盘和内存的使用统计都是在列族层面进行的。 列族越多,在取一行数据时所要参与 IO、搜寻的文件就越多,所以,如果没有必要,不要设置太多的列族。

(5)Column

HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。

(6)Time Stamp

用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入 HBase 的时间。HBase 中通过 row 和 columns 确定的为一个存贮单元称为 cell。每个 cell 都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64 位整型。时间戳可以由 hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。

为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase 提供了两种数据版本回收方式:

  • 保存数据的最后 n 个版本
  • 保存最近一段时间内的版本(设置数据的生命周期 TTL)。

用户可以针对每个列族进行设置。

(7)Cell

由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。

八、HBase读写过程

读流程

image.png

(1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
(2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
(3)与目标 Region Server 进行通讯。
(4)分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
(5) 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到 Block Cache。
(6)将合并后的最终结果返回给客户端。

写流程

image.png

(1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
(2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
(3)与目标 Region Server 进行通讯;
(4)将数据顺序写入(追加)到 WAL;
(5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
(6)向客户端发送 ack;
(7)等达到 MemStore 的刷写时机后,将数据刷写到 HFile。

:HBase架构中Zookeeper内查找数据的实现(关键词:LSM树):
image.png

九、HRegionServer 宕机处理

0、概述

正因为机器的配置并不是太好加上网络硬盘等各方面的原因,机器宕机的概率就会相对比较大。RegionServer作为HBase集群中实际的执行节点,不可避免地也会出现宕机。

宕机并不十分可怕,因为不会丢数据。HBase集群中一台RegionServer宕机(实指RegionServer进程挂掉)并不会导致已经写入的数据丢失,和MySQL等数据库一样,HBase采用WAL机制保证这点:它会先写HLog,再写缓存,缓存写满后一起落盘。即使意外宕机导致很多缓存数据没有及时落盘,也可以通过HLog日志恢复出来。

可是没有数据丢失并不意味着宕机对业务方没有任何影响。众所周知,RegionServer宕机是由Zookeeper首先感知到的,而Zookeeper感知到RegionServer宕机事件是需要一定时间的。在这段时间内,所有的读写路由还会正常落到它上面,这些读写必然都会失败。

1、处理流程

(1)RegionServer发生宕机,RegionServer注册到Zookeeper 的 /hbase/rs节点下的临时节点就会离线,Zookeeper第一时间通知Master进行失效备援。

(2)Master首先会将这台RegionServer上所有Region移到其他RegionServer上,再将HLog分发给其他RegionServer进行回放。

(3)再修改路由,业务方的读写恢复正常。

2、实践

(1)排查RegionServer日志

(2)排查系统监控

十、预分区

0、定义

每一个 region 维护着 StartRow 与 EndRow,如果加入的数据符合某个 Region 维护的 RowKey 范围,则该数据交给这个 Region 维护。将新数据所要投放的分区提前规划,以提高 HBase 性能,避免产生热点问题。其中,规划的分区数与未来半年或一年的数据量和机器规模有关。

1、预分区的优点

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

2、预分区的几种方式

(1)手动设定预分区

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

(2)生成 16 进制序列预分区

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

(3)按照文件中设置的规则预分区

创建 splits.txt 文件内容如下:
aaaa
bbbb
cccc
dddd

然后执行:
create 'staff3','partition3',SPLITS_FILE => 'splits.txt'

(4)使用 JavaAPI 创建预分区

//自定义算法,产生一系列 hash 散列值存储在二维数组中
byte[][] splitKeys = 某个散列值函数
//创建 HbaseAdmin 实例
HBaseAdmin hAdmin = new HBaseAdmin(HbaseConfiguration.create());
//创建 HTableDescriptor 实例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通过 HTableDescriptor 实例和散列值二维数组创建带有预分区的 Hbase 表
hAdmin.createTable(tableDesc, splitKeys);

十一、HRegion的负载均衡

HBase使用RowKey将表水平切割成多个 Hregion,每个HRegion都纪录了它的StartKey和 EndKey,Client可以通过HMaster快速的定位每个 RowKey在哪个HRegion中,HRegion由HMaster分配到相应在HRegion Split后,两个新的HRegion 最初会和之前的父HRegion在相同的 HRegionServer上,出于负载均衡的考虑,HMaster可能会将其中的一个甚至两个重新分配的其他的 HRegionServer中,此时会引起有些HRegionServer处理的数 据在其他节点上 ,直到下一次Major Compaction将数据从远端的节点移动到本地节点。这就是Hregion的负载均衡。

十二、rowKey设计

HBase 是三维有序存储的,通过 rowkey(行键),column key(column family 和 qualifier)和 TimeStamp(时间戳)这个三个维度可以对 HBase 中的数据进行快速定位。

HBase 中 rowkey 可以唯一标识一行记录,在 HBase 查询的时候,有以下几种方式:

  • 通过 get 方式,指定 rowkey 获取唯一一条记录;
  • 通过 scan 方式,设置 startRow 和 stopRow 参数进行范围匹配;
  • 全表扫描,即直接扫描整张表中所有行记录。

HBase rowKey 设计原则

1、长度原则

rowkey 是一个二进制码流,可以是任意字符串,最大长度 64kb,实际应用中一般为 10-100bytes,以 byte[]形式保存,一般设计成定长。建议越短越好,不要超过 16 个字节,原因如下:

  • 数据的持久化文件 HFile 中是按照 KeyValue 存储的,如果 rowkey 过长,比如超过 100 字节,1000w 行数据,光 rowkey 就要占用:100 * 1000w=10 亿个字节,将近 1G 数据,这样会极大影响 HFile 的存储效率。
  • MemStore 将缓存部分数据到内存,如果 rowkey 字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。

2、散列原则

如果 rowkey 按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将 rowKey 的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个 RegionServer,以实现负载均衡的几率。
如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个 RegionServer 上,这样在数据检索的时候负载会集中在个别的 RegionServer 上,造成热点问题,会降低查询效率。

3、唯一原则

必须在设计上保证其唯一性,rowkey 是按照字典顺序排序存储的,因此,设计 rowkey 的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

其他一些设计建议:

  • 尽量减少行键和列族的大小。在 HBase 中,value 永远和它的 key 一起传输的。当具体的值在系统间传输时,它的 rowkey,列名,时间戳也会一起传输。如果你的 rowkey 和列名很大,这个时候它们将会占用大量的存储空间。
  • 列族尽可能越短越好,最好是一个字符。
  • 冗长的属性名虽然可读性好,但是更短的属性名存储在 HBase 中会更好。

十三、HBase 中热点/数据倾斜的产生原因及解决方法

1、什么是热点

HBase 中的行是按照 rowkey 的字典顺序排序的,这种设计优化了 scan 操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于 scan。然而糟糕的 rowkey 设计是热点的源头。热点发生在大量的 client 直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点 region 所在的单个机器超出自身承受能力,引起性能下降甚至 region 不可用,这也会影响同一个 RegionServer 上的其他 region,由于主机无法服务其他 region 的请求。设计良好的数据访问模式以使集群被充分,均衡的利用。为了避免写热点,设计 rowkey 使得不同行在同一个 region,但是在更多数据情况下,数据应该被写入集群的多个 region,而不是一个。

2、常见的避免热点的方法以及它们的优缺点

(1) 加盐

这里所说的加盐不是密码学中的加盐,而是在 rowkey 的前面增加随机数,具体就是给 rowkey 分配一个随机前缀以使得它和之前的 rowkey 的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的 region 的数量一致。加盐之
后的 rowkey 就会根据随机生成的前缀分散到各个 region 上,以避免热点。

(2) 哈希

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

(3) 反转

第三种防止热点的方法时反转固定长度或者数字格式的 rowkey。这样可以使得 rowkey 中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机 rowkey,但是牺牲了 rowkey 的有序性。反转 rowkey 的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题。

(4) 时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为 rowkey 的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾,例如 key , [key] 的最新值可以通过 scan [key]获得[key]的第一条记录,因为 HBase 中 rowkey 是有序的,第一条记录是最后录入的数据。

十四、HBase的协处理器

0、背景

HBase 作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执 行求和、计数、排序等操作。如果做一些简单的相加或者聚合计算的时候直接将计算过程放置在 server 端,能够减少通讯开销,从而获 得很好的性能提升。于是,HBase 在 0.92 之后引入了协处理器(coprocessors)实现一些新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。

1、两种协处理器:observer 和 endpoint

1.1 Observer

Observer 类似于传统数据库中的触发器,当发生某些事件的时候这类协处理器会被 Server 端调用。Observer Coprocessor 就是一些散布在 HBase Server 端代码中的 hook 钩子, 在固定的事件发生时被调用。比如:put 操作之前有钩子函数 prePut,该函数在 put 操作执 行前会被 Region Server 调用;在 put 操作之后则有 postPut 钩子函数。

以 HBase0.92 版本为例,它提供了三种观察者接口:

  • RegionObserver:提供客户端的数据操纵事件钩子:Get、Put、Delete、Scan 等。
  • WALObserver:提供 WAL 相关操作钩子。
  • MasterObserver:提供 DDL-类型的操作钩子。如创建、删除、修改数据表等。

下图是以 RegionObserver 为例子讲解 Observer 这种协处理器的原理:
image.png

(1)客户端发出 put 请求

(2)该请求被分派给合适的 RegionServer 和 region

(3)coprocessorHost 拦截该请求,然后在该表上登记的每个 RegionObserver 上调用 prePut()

(4)如果没有被 prePut()拦截,该请求继续送到 region,然后进行处理

(5)region 产生的结果再次被 CoprocessorHost 拦截,调用 postPut()

(6)假如没有 postPut()拦截该响应,最终结果被返回给客户端

1.2 Endpoint

Endpoint 协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处 理器执行一段 Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理,最常见 的用法就是进行聚集操作。如果没有协处理器,当用户需要找出一张表中的最大数据,即 max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的 操作。这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执行, 势必效率低下。利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase 将利用底层 cluster 的多个节点并发执行求最大值的操作。即在每个 Region 范围内执行求最 大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给客 户端。在客户端进一步将多个 Region 的最大值进一步处理而找到其中的最大值。这样整体 的执行效率就会提高很多。

EndPoint 的工作原理如图:
image.png

1.3 对比

Observer 类似于 RDBMS 中的触发器,主要在服务端工作
Endpoint 类似于 RDBMS 中的存储过程,主要在服务端工作

Observer 允许集群在正常的客户端操作过程中可以有不同的行为表现
Endpoint 允许扩展集群的能力,对客户端应用开放新的运算命令

Observer 可以实现权限管理、优先级设置、监控、ddl 控制、二级索引等功能
Endpoint 可以实现 min、max、avg、sum、distinct、group by 等功能

2、协处理器加载方式

协处理器的加载方式有两种:静态加载方式(Static Load)和动态加载方式(Dynamic Load)

静态加载的协处理器称之为 System Coprocessor
动态加载的协处理器称之为 Table Coprocessor

十五、二级索引

为什么需要HBse二级索引

由于 HBase 的查询比较弱,如果需要实现类似于

select name,salary,count(1),max(salary) from user group by name,salary order by salary

等这样的复杂性的统计需求,基本上不可能,或者说比较困难,所以我们在使用 HBase 的时候,一般都会借助二级索引的方案来进行实现。HBase 的一级索引就是 rowKey,我们只能通过 rowkey 进行检索。如果要对库里的非rowkey字段进行数据检索和查询,往往要通过 MapReduce/Spark 等分布式计算框架进行,硬件资源消耗和时间延迟都会比较高。

为了HBase的数据查询更高效、适应更多的场景, 诸如使用非rowkey字段检索也能做到秒级响应,或者支持各个字段进行模糊查询和多字段组合查询等, 因此需要在HBase上面构建二级索引, 以满足现实中更复杂多样的业务需求。

HBase 的二级索引方案:

1、基于Coprocessor方案

基于Coprocessor实现二级索引的大致思路:构建一份“索引”的映射关系,存储在另一张hbase表或者其他DB里面。业界比较知名的基于Coprocessor的开源方案:

  • 华为的hindex:基于0.94版本,当年刚出来的时候比较火,但是版本较旧,GitHub项目地址最近这几年就没更新过。
  • Apache Phoenix: 功能围绕着SQL on HBase,支持和兼容多个HBase版本, 二级索引只是其中一块功能。 二级索引的创建和管理直接有SQL语法支持,使用起来很简便, 该项目目前社区活跃度和版本更新迭代情况都比较好。

Apache Phoenix 在目前开源的方案中,是一个比较优的选择。主打SQL on HBase,基于 SQL 能完成 HBase 的 CRUD 操作,支持 JDBC 协议。 Apache Phoenix 在 Hadoop 生态里面位置:

image.png

Phoenix二级索引特点:

  • Covered Indexes(覆盖索引) :把关注的数据字段也附在索引表上,只需要通过索引表就能返回所要查询的数据(列), 所以索引的列必须包含所需查询的列(SELECT的列和WHRER的列)。
  • Functional indexes(函数索引): 索引不局限于列,支持任意的表达式来创建索引。
  • Global indexes(全局索引):适用于读多写少场景。通过维护全局索引表,所有的更新和写操作都会引起索引的更新,写入性能受到影响。 在读数据时,Phoenix SQL会基于索引字段,执行快速查询。
  • Local indexes(本地索引):适用于写多读少场景。 在数据写入时,索引数据和表数据都会存储在本地。在数据读取时, 由于无法预先确定region的位置,所以在读取数据时需要检查每个region(以找到索引数据),会带来一定性能(网络)开销。

基于Coprocessor的方案优缺点:

优点: 基于Coprocessor的方案,从开发设计的角度看, 把很多对二级索引管理的细节都封装在的Coprocessor具体实现类里面, 这些细节对外面读写的人是无感知的,简化了数据访问者的使用。

缺点: 但是Coprocessor的方案入侵性比较强, 增加了在Regionserver内部需要运行和维护二级索引关系表的代码逻辑等,对Regionserver的性能会有一定影响。

2、非Coprocessor方案

选择不基于Coprocessor开发,自行在外部构建和维护索引关系也是另外一种方式。

常见的是采用底层基于Apache Lucene的Elasticsearch(下面简称ES)或Apache Solr ,来构建强大的索引能力、搜索能力, 例如支持模糊查询、全文检索、组合查询、排序等。

十六、调优

1、通用优化

(1)NameNode 的元数据备份使用 SSD。

(2)定时备份 NameNode 上的元数据,每小时或者每天备份,如果数据极其重要,可以 5~10 分钟备份一次。备份可以通过定时任务复制元数据目录即可。

(3)为 NameNode 指定多个元数据目录,使用 dfs.name.dir 或者dfs.namenode.name.dir 指定。一个指定本地磁盘,一个指定网络磁盘。这样可以提供元数据的冗余和健壮性,以免发生故障。

(4)设置 dfs.namenode.name.dir.restore 为 true,允许尝试恢复之前失败的 dfs.namenode.name.dir 目录,在创建 checkpoint 时做此尝试,如果设置了多个磁盘,建议允许。

(5)NameNode 节点必须配置为 RAID1(镜像盘)结构。

(6)保持 NameNode 日志目录有足够的空间,这些日志有助于帮助你发现问题。

(7)因为 Hadoop 是 IO 密集型框架,所以尽量提升存储的速度和吞吐量(类似位宽)。

2、Linux 优化

(1)开启文件系统的预读缓存(set readahead)可以提高读取速度

$ sudo blockdev --setra 32768 /dev/sda

(2)关闭进程睡眠池

$ sudo sysctl -w vm.swappiness=0

3、HDFS 优化(hdfs-site.xml)

(1)保证 RPC 调用会有较多的线程数

属性:dfs.namenode.handler.count
解释:该属性是 NameNode 服务默认线程数,的默认值是 10,根据机器的可用内存可以调整为 50~100。

属性:dfs.datanode.handler.count
解释:该属性默认值为 10,是 DataNode 的处理线程数,如果 HDFS 客户端程序读写请求比较多,可以调高到 15~20,设置的值越大,内存消耗越多,不要调整过高,一般业务中,5~10 即可。

(2)副本数的调整

属性:dfs.replication
解释:如果数据量巨大,且不是非常之重要,可以调整为 2~3,如果数据非常之重要,可以调整为 3~5。

(3)文件块大小的调整

属性:dfs.blocksize
解释:块大小定义,该属性应该根据存储的大量的单个文件大小来设置,如果大量的单个文件都小于 100M,建议设置成 64M 块大小,对于大于 100M 或者达到 GB 的这种情况,建议设置成 256M,一般设置范围波动在 64M~256M 之间。

4、MapReduce 优化(mapred-site.xml)

(1)Job 任务服务线程数调整

mapreduce.jobtracker.handler.count
该属性是 Job 任务线程数,默认值是 10,根据机器的可用内存可以调整为 50~100。

(2)HTTP

属性:mapreduce.tasktracker.http.threads
解释:定义 HTTP 服务器工作线程数,默认值为 40,对于大集群可以调整到 80~100。

(3)文件排序合并优化

属性:mapreduce.task.io.sort.factor
解释:文件排序时同时合并的数据流的数量,这也定义了同时打开文件的个数,默认值为 10,如果调高该参数,可以明显减少磁盘 IO,即减少文件读取的次数。

(4)设置任务并发

属性:mapreduce.map.speculative
解释:该属性可以设置任务是否可以并发执行,如果任务多而小,该属性设置为 true 可以明显加快任务执行效率,但是对于延迟非常高的任务,建议改为 false,这就类似于迅雷下载。

(5)MR 输出数据的压缩

属性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress
解释:对于大集群而言,建议设置 Map-Reduce 的输出为压缩的数据,而对于小集群,则不需要。

(6)优化 Mapper 和 Reducer 的个数
属性:mapreduce.tasktracker.map.tasks.maximum、mapreduce.tasktracker.reduce.tasks.maximum
解释:以上两个属性分别为一个单独的 Job 任务可以同时运行的 Map 和 Reduce 的数量。设置上面两个参数时,需要考虑 CPU 核数、磁盘和内存容量。假设一个 8 核的 CPU,业务内容非常消耗 CPU,那么可以设置 map 数量为 4,如果该业务不是特别消耗 CPU 类型的,那么可以设置 map 数量为 40,reduce 数量为 20。这些参数的值修改完成之后,一定要观察是否有较长等待的任务,如果有的话,可以减少数量以加快任务执行,如果设置一个很大的值,会引起大量的上下文切换,以及内存与磁盘之间的数据交换,这里没有标准的配置数值,需要根据业务和硬件配置以及经验来做出选择。在同一时刻,不要同时运行太多的 MapReduce,这样会消耗过多的内存,任务会执行的非常缓慢,我们需要根据 CPU 核数,内存容量设置一个 MR 任务并发的最大值,使固定数据量的任务完全加载到内存中,避免频繁的内存和磁盘数据交换,从而降低磁盘 IO,提高性能。

大概估算公式:
map = 2 + 2/3cpu_core
reduce = 2 + 1/3cpu_core

5、HBase 优化

(1)优化 DataNode 允许的最大文件打开数

属性:dfs.datanode.max.transfer.threads
文件:hdfs-site.xml
解释:HBase 一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为 4096 或者更高。默认值:4096

(2)优化延迟高的数据操作的等待时间

属性:dfs.image.transfer.timeout
文件:hdfs-site.xml
解释:如果对于某一次数据操作来讲,延迟非常高,socket 需要等待更长的时间,建议把该值设置为更大的值(默认 60000 毫秒),以确保 socket 不会被 timeout 掉。

(3)优化数据的写入效率

属性:mapreduce.map.output.compress、mapreduce.map.output.compress.codec
文件:mapred-site.xml
解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为 true,第二个属性值修改为:
org.apache.hadoop.io.compress.GzipCodec

(4)优化 DataNode 存储

属性:dfs.datanode.failed.volumes.tolerated
文件:hdfs-site.xml
解释:默认为0,意思是当DataNode中有一个磁盘出现故障,则会认为该 DataNode shutdown 了。如果修改为 1,则一个磁盘出现故障时,数据会被复制到其他正常的 DataNode 上,当前的 DataNode 继续工作。

(5)设置 RPC 监听数量

属性:hbase.regionserver.handler.count
文件:hbase-site.xml
解释:默认值为 30,用于指定 RPC 监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。

(6)优化 HStore 文件大小

属性:hbase.hregion.max.filesize
文件:hbase-site.xml
解释:默认值 10737418240(10GB),如果需要运行 HBase 的 MR 任务,可以减小此值,因为一个 region 对应一个 map 任务,如果单个 region 过大,会导致 map 任务执行时间过长。该值的意思就是,如果 HFile 的大小达到这个数值,则这个 region 会被切分为两个 Hfile。

(7)优化 hbase 客户端缓存

属性:hbase.client.write.buffer
文件:hbase-site.xml
解释:用于指定 HBase 客户端缓存,增大该值可以减少 RPC 调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少 RPC 次数的目的。

(8)指定 scan.next 扫描 HBase 所获取的行数

属性:hbase.client.scanner.caching
文件:hbase-site.xml
解释:用于指定 scan.next 方法获取的默认行数,值越大,消耗内存越大。

6、内存优化

HBase 操作过程中需要大量的内存开销,毕竟 Table 是可以缓存在内存中的,一般会分配整个可用内存的 70% 给 HBase 的 Java 堆。但是不建议分配非常大的堆内存,因为 GC 过程持续太久会导致 RegionServer 处于长期不可用状态,一般
16~48G 内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。

7、JVM 优化(hbase-env.sh)

(1)并行 GC

参数:·-XX:+UseParallelGC·
解释:开启并行 GC。

(2)同时处理垃圾回收的线程数

参数:-XX:ParallelGCThreads=cpu_core – 1
解释:该属性设置了同时处理垃圾回收的线程数。

(3)禁用手动 GC

参数:-XX:DisableExplicitGC
解释:防止开发人员手动调用 GC。

8、Zookeeper 优化

优化 Zookeeper 会话超时时间

参数:zookeeper.session.timeout
文件:hbase-site.xml
解释:该值会直接关系到 master 发现服务器宕机的最大周期,默认值为 30 秒,如果该值过小,会在 HBase 在写入大量数据发生而 GC 时,导致 RegionServer 短暂的不可用,从而没有向 ZK 发送心跳包,最终导致认为从节点 shutdown。一般 20 台左右的集群需要配置 5 台 Zookeeper。


西柚
36 声望4 粉丝