Merge branch 'master' of github.com:heibaiying/BigData-Notes
This commit is contained in:
commit
6037f6d5f6
@ -1,4 +1,4 @@
|
||||
# 分布式文件存储系统——HDFS
|
||||
# Hadoop分布式文件系统——HDFS
|
||||
|
||||
<nav>
|
||||
<a href="#一介绍">一、介绍</a><br/>
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
## 一、介绍
|
||||
|
||||
**HDFS** (**Hadoop Distributed File System**)是一种分布式文件系统,具有**高容错**、**高吞吐量**等特性,可以部署在**低成本**的硬件上。
|
||||
**HDFS** (**Hadoop Distributed File System**)是Hadoop下的分布式文件系统,具有高容错、高吞吐量等特性,可以部署在低成本的硬件上。
|
||||
|
||||
|
||||
|
||||
@ -39,46 +39,40 @@
|
||||
|
||||
### 2.1 HDFS 架构
|
||||
|
||||
HDFS 具有主/从架构,由单个NameNode (NN)和 多个 DataNode (DN) 组成:
|
||||
HDFS 遵循主/从架构,由单个NameNode(NN)和多个DataNode(DN)组成:
|
||||
|
||||
- NameNode : 负责执行文件**系统命名空间**的操作,如打开,关闭和重命名文件、目录等,它还负责集群元数据的存储,记录着每个文件中各个块所在数据节点的信息。
|
||||
- DataNode:负责提供来自文件系统客户端的读写请求,执行块的创建,删除等操作。
|
||||
- **NameNode** : 负责执行有关`文件系统命名空间`的操作,例如打开,关闭、重命名文件和目录等。它同时还负责集群元数据的存储,记录着文件中各个数据块的位置信息。
|
||||
- **DataNode**:负责提供来自文件系统客户端的读写请求,执行块的创建,删除等操作。
|
||||
|
||||
|
||||
|
||||
### 2.2 文件系统命名空间
|
||||
|
||||
HDFS 系统命名空间的层次结构与现在大多数文件系统类似(如 windows), 可以进行目录、文件的创建、移动、删除和重命名等操作,支持用户配置和访问权限配置,但不支持硬链接和软连接。
|
||||
|
||||
NameNode 负责维护文件系统名称空间,记录对名称空间或其属性的任何更改。
|
||||
HDFS的`文件系统命名空间`的层次结构与大多数文件系统类似(如Linux), 支持目录和文件的创建、移动、删除和重命名等操作,支持配置用户和访问权限,但不支持硬链接和软连接。`NameNode`负责维护文件系统名称空间,记录对名称空间或其属性的任何更改。
|
||||
|
||||
|
||||
|
||||
### 2.3 数据复制
|
||||
|
||||
由于hadoop设计运行在廉价的机器上,这意味着硬件是不可靠的,为了保证高容错,数据复制孕育而生。
|
||||
|
||||
HDFS 它将每一个文件存储为一系列**块**,复制文件的块以实现容错,块大小和复制因子可根据文件进行配置(默认块大小是128M,默认复制因子是3)。
|
||||
由于Hadoop被设计运行在廉价的机器上,这意味着硬件是不可靠的,为了保证容错性,HDFS提供了数据复制机制。HDFS 将每一个文件存储为一系列**块**,每个块由多个副本来保证容错,块的大小和复制因子可以自行配置(默认情况下,块大小是128M,默认复制因子是3)。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hdfsdatanodes.png"/> </div>
|
||||
|
||||
### 2.4 数据复制的实现原理
|
||||
|
||||
大型HDFS实例在通常分布在多个机架上的计算机群集上运行。不同机架中两个节点之间的通信必须通过交换机。在大多数情况下,同一机架中的计算机之间的网络带宽大于不同机架中的计算机之间的网络带宽。
|
||||
大型的HDFS实例在通常分布在多个机架的多台服务器上,不同机架上的两台服务器之间通过交换机进行通讯。在大多数情况下,同一机架中的服务器间的网络带宽大于不同机架中的服务器之间的带宽。因此HDFS采用机架感知副本放置策略,对于常见情况,当复制因子为3时,HDFS的放置策略是:
|
||||
|
||||
HDFS采用机架感知副本放置策略,对于常见情况,当复制因子为3时,HDFS的放置策略是:
|
||||
|
||||
在编写器位于datanode上时,将一个副本放在本地计算机上,否则放在随机datanode上;在另一个(远程)机架上的节点上放置另一个副本,最后一个在同一个远程机架中的另一个节点上。此策略可以减少机架间写入流量,从而提高写入性能。
|
||||
在写入程序位于`datanode`上时,就优先将写入文件的一个副本放置在该`datanode`上,否则放在随机`datanode`上。之后在另一个远程机架上的任意一个节点上放置另一个副本,并在该机架上的另一个节点上放置最后一个副本。此策略可以减少机架间的写入流量,从而提高写入性能。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hdfs-机架.png"/> </div>
|
||||
|
||||
如果复制因子大于3,则随机确定第4个和以下副本的放置,同时保持每个机架的副本数量低于上限,上限值通常为`(复制系数 - 1)/机架数量 + 2`,但是不允许同一个dataNode具有同一块的多个副本。
|
||||
如果复制因子大于3,则随机确定第4个和之后副本的放置位置,同时保持每个机架的副本数量低于上限,上限值通常为`(复制系数 - 1)/机架数量 + 2`,需要注意的是不允许同一个`dataNode`上具有同一个块的多个副本。
|
||||
|
||||
|
||||
|
||||
### 2.5 副本的选择
|
||||
|
||||
为了最大限度地减少全局带宽消耗和读取延迟,HDFS尝试满足最接近读取器的副本的读取请求。如果在与读取器节点相同的机架上存在副本,则该副本首选满足读取请求。如果HDFS群集跨越多个数据中心,则本地数据中心的副本优先于任何远程副本。
|
||||
为了最大限度地减少带宽消耗和读取延迟,HDFS在执行读取请求时,优先读取距离读取器最近的副本。如果在与读取器节点相同的机架上存在副本,则优先选择该副本。如果HDFS群集跨越多个数据中心,则优先选择本地数据中心上的副本。
|
||||
|
||||
|
||||
|
||||
@ -86,21 +80,21 @@ HDFS采用机架感知副本放置策略,对于常见情况,当复制因子
|
||||
|
||||
#### 1. 心跳机制和重新复制
|
||||
|
||||
每个DataNode定期向NameNode发送心跳消息,如果超过指定时间没有收到心跳消息,则将DataNode标记为死亡。NameNode不会将任何新的IO请求转发给标记为死亡的DataNode,且标记为死亡的DataNode上的数据也不在可用。 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode会跟踪这些需要复制的块,并在必要的时候进行复制。
|
||||
每个DataNode定期向NameNode发送心跳消息,如果超过指定时间没有收到心跳消息,则将DataNode标记为死亡。NameNode不会将任何新的IO请求转发给标记为死亡的DataNode,也不会再使用这些DataNode上的数据。 由于数据不再可用,可能会导致某些块的复制因子小于其指定值,NameNode会跟踪这些块,并在必要的时候进行重新复制。
|
||||
|
||||
#### 2. 数据的完整性
|
||||
|
||||
由于存储设备中故障等原因,存储在DataNode获取的数据块可能会发生此损坏。
|
||||
由于存储设备故障等原因,存储在DataNode上的数据块也会发生损坏。为了避免读取到已经损坏的数据而导致错误,HDFS提供了数据完整性校验机制来保证数据的完整性,具体操作如下:
|
||||
|
||||
当客户端创建HDFS文件时,它会计算文件每个块的`校验和`,并将这些`校验和`存储在同一HDFS命名空间中的单独隐藏文件中。当客户端检索文件内容时,它会验证从每个DataNode接收的数据是否与存储在关联校验和文件中的`校验和`匹配。如果没有,则客户端可以选择从另一个DataNode中检索该块。
|
||||
当客户端创建HDFS文件时,它会计算文件的每个块的`校验和`,并将`校验和`存储在同一HDFS命名空间下的单独的隐藏文件中。当客户端检索文件内容时,它会验证从每个DataNode接收的数据是否与存储在关联校验和文件中的`校验和`匹配。如果匹配失败,则证明数据已经损坏,此时客户端会选择从其他DataNode获取该块的其他可用副本。
|
||||
|
||||
#### 3.元数据的磁盘故障
|
||||
|
||||
`FsImage`和`EditLog`是HDFS架构的中心数据。这些数据的失效会引起HDFS实例失效。因为这个原因,可以配置NameNode使其支持`FsImage`和`EditLog`多副本同步,使得`FsImage`或`EditLog`的任何改变会引起每一份`FsImage`和`EditLog`同步更新。
|
||||
`FsImage`和`EditLog`是HDFS的核心数据,这些数据的意外丢失可能会导致整个HDFS服务不可用。为了避免这个问题,可以配置NameNode使其支持`FsImage`和`EditLog`多副本同步,这样`FsImage`或`EditLog`的任何改变都会引起每个副本`FsImage`和`EditLog`的同步更新。
|
||||
|
||||
#### 4.支持快照
|
||||
|
||||
快照支持在特定时刻存储数据副本,以便可以将损坏的HDFS实例回滚到先前良好时间点。
|
||||
快照支持在特定时刻存储数据副本,在数据意外损坏时,可以通过回滚操作恢复到健康的数据状态。
|
||||
|
||||
|
||||
|
||||
@ -108,29 +102,29 @@ HDFS采用机架感知副本放置策略,对于常见情况,当复制因子
|
||||
|
||||
### 3.1 高容错
|
||||
|
||||
由于HDFS 采用数据的多副本方案、所以哪怕部分硬件的损坏,也不会导致数据的丢失。
|
||||
由于HDFS 采用数据的多副本方案,所以部分硬件的损坏不会导致全部数据的丢失。
|
||||
|
||||
### 3.2 高吞吐量
|
||||
|
||||
HDFS是被设计用于批量处理而非用户交互。设计的重点是高吞吐量访问而不是低延迟数据访问。
|
||||
HDFS设计的重点是支持高吞吐量的数据访问,而不是低延迟的数据访问。
|
||||
|
||||
### 3.3 大文件支持
|
||||
|
||||
在HDFS上运行的应用程序具有大型数据集。一个典型文档在HDFS是GB到TB级别的。因此,HDFS被设计为支持大文件。
|
||||
HDFS适合于大文件的存储,文档的大小应该是是GB到TB级别的。
|
||||
|
||||
### 3.3 简单一致性模型
|
||||
|
||||
HDFS更适合于一次写入多次读取(write-once-read-many)的访问模型。支持将内容附加到文件末尾,但无法在任意点更新。此假设简化了数据一致性问题,并实现了高吞吐量数据访问。
|
||||
HDFS更适合于一次写入多次读取(write-once-read-many)的访问模型。支持将内容追加到文件末尾,但不支持数据的随机访问,不能从文件任意位置新增数据。
|
||||
|
||||
### 3.4 跨平台移植性
|
||||
|
||||
HDFS的设计便于从一个平台移植到另一个平台。这有助于HDFS作为大数据首选存储方案。
|
||||
HDFS具有良好的跨平台移植性,这使得其他大数据计算框架都将其作为数据持久化存储的首选方案。
|
||||
|
||||
|
||||
|
||||
## 附:图解HDFS存储原理
|
||||
|
||||
说明:本小结图片引用自博客[翻译经典 HDFS 原理讲解漫画](https://blog.csdn.net/hudiefenmu/article/details/37655491)
|
||||
> 说明:以下图片引用自博客:[翻译经典 HDFS 原理讲解漫画](https://blog.csdn.net/hudiefenmu/article/details/37655491)
|
||||
|
||||
### 1. HDFS写数据原理
|
||||
|
||||
|
@ -3,20 +3,21 @@
|
||||
<nav>
|
||||
<a href="#一MapReduce-概述">一、MapReduce 概述</a><br/>
|
||||
<a href="#二MapReduce-编程模型简述">二、MapReduce 编程模型简述</a><br/>
|
||||
<a href="#三MapReduce-编程模型详述">三、MapReduce 编程模型详述</a><br/>
|
||||
<a href="#三combiner--partitioner">三、combiner & partitioner</a><br/>
|
||||
<a href="#四MapReduce-词频统计案例">四、MapReduce 词频统计案例</a><br/>
|
||||
<a href="#五词频统计案例进阶">五、词频统计案例进阶</a><br/>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、MapReduce 概述
|
||||
|
||||
Hadoop MapReduce是一个分布式计算框架,用于编写应用程序,以可靠,容错的方式在大型集群上并行处理大量数据(多为TB级别数据集)。
|
||||
Hadoop MapReduce是一个分布式计算框架,用于编写批处理应用程序。编写好的程序可以提交到Hadoop集群上用于并行处理大规模的数据集。
|
||||
|
||||
MapReduce 作业通常将输入数据集拆分为独立的块,这些块由**map任务**以完全并行的方式处理。框架对**map任务**的输出进行排序,然后输入到**reduce任务**。通常,作业的输入和输出都存储在文件系统中。该框架负责调度任务,监视任务并重新执行失败的任务。
|
||||
|
||||
MapReduce框架专门用于`<key,value>`对,也就是说,框架将作业的输入视为一组`<key,value>`对,并生成一组`<key,value>`对作为输出。输出和输出的`key`和`value`都必须实现[Writable](http://hadoop.apache.org/docs/stable/api/org/apache/hadoop/io/Writable.html) 接口。
|
||||
MapReduce作业通过将输入的数据集拆分为独立的块,这些块由`map`以并行的方式处理;框架对`map`的输出进行排序,然后输入到`reduce`中。MapReduce框架专门用于`<key,value>`键值对处理,也就是说,框架将作业的输入视为一组`<key,value>`对,并生成一组`<key,value>`对作为输出。输出和输出的`key`和`value`都必须实现[Writable](http://hadoop.apache.org/docs/stable/api/org/apache/hadoop/io/Writable.html) 接口。
|
||||
|
||||
```
|
||||
(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)
|
||||
@ -32,29 +33,28 @@ MapReduce框架专门用于`<key,value>`对,也就是说,框架将作业
|
||||
|
||||
1. **input** : 读取文本文件;
|
||||
|
||||
2. **splitting** : 将文件按照行进行拆分,此时得到的K1为行数,V1表示对应行的文本内容;
|
||||
2. **splitting** : 将文件按照行进行拆分,此时得到的`K1`行数,`V1`表示对应行的文本内容;
|
||||
|
||||
3. **mapping** : 并行将每一行按照空格进行拆分,拆分得到的List(K2,V2),其中K2代表每一个单词,由于是做词频统计,所以其V2为1,代表出现1次;
|
||||
4. **shuffling**:由于Mapping操作可能是在不同的机器上并行处理的,所以需要通过shuffling将相同的数据分到同一个节点上去合并,这样才能统计出最终的结果,此时得到K2为每一个单词,List(V2)为可迭代集合,V2就是Mapping中的V2;
|
||||
5. **Reducing** : 这里的案例是统计单词出现的总次数,所以Reducing迭代List(V2),并计算其和值,最终输出。
|
||||
3. **mapping** : 并行将每一行按照空格进行拆分,拆分得到的`List(K2,V2)`,其中`K2`代表每一个单词,由于是做词频统计,所以`V2`的值为1,代表出现1次;
|
||||
4. **shuffling**:由于`Mapping`操作可能是在不同的机器上并行处理的,所以需要通过`shuffling`将相同`key`值的数据分发到同一个节点上去合并,这样才能统计出最终的结果,此时得到`K2`为每一个单词,`List(V2)`为可迭代集合,`V2`就是Mapping中的V2;
|
||||
5. **Reducing** : 这里的案例是统计单词出现的总次数,所以`Reducing`对`List(V2)`进行归约求和操作,最终输出。
|
||||
|
||||
MapReduce 编程模型中`splitting` 和` shuffing`操作都是由框架实现的,实际上,主要需要我们实现的是`mapping`和`reducing`中的编程逻辑,这也就是为何该框架叫做MapReduce的原因。
|
||||
MapReduce编程模型中`splitting` 和`shuffing`操作都是由框架实现的,需要我们自己编程实现的只有`mapping`和`reducing`,这也就是框架MapReduce名字的来源。
|
||||
|
||||
|
||||
|
||||
## 三、MapReduce 编程模型详述
|
||||
## 三、combiner & partitioner
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/Detailed-Hadoop-MapReduce-Data-Flow-14.png"/> </div>
|
||||
|
||||
### 3.1 InputFormat & RecordReaders
|
||||
|
||||
InputFormat将输出文件拆分为多个InputSplit,并由RecordReaders将InputSplit转换为标准的<key,value>键值对,作为map的输出。这一步的意义在于只有先进行逻辑拆分并转为标准的格式后,才能为多个map提供输入,进行并行处理;
|
||||
`InputFormat`将输出文件拆分为多个`InputSplit`,并由`RecordReaders`将`InputSplit`转换为标准的<key,value>键值对,作为map的输出。这一步的意义在于只有先进行逻辑拆分并转为标准的键值对格式后,才能为多个`map`提供输入,进行并行处理。
|
||||
|
||||
`InputFormat` 为一个抽象类,其中定义了两个抽象方法,而实际的操作则由其实现类来进行。其源码如下:
|
||||
`InputFormat` 为一个抽象类,其中只定义了两个抽象方法,具体的操作则由其实现类来进行。其源码如下:
|
||||
|
||||
**getSplits**:将输入文件拆分为多个InputSplit;
|
||||
|
||||
**createRecordReader**: 定义RecordReader的创建方法;
|
||||
- **getSplits**:将输入文件拆分为多个InputSplit;
|
||||
- **createRecordReader**: 定义RecordReader的创建方法;
|
||||
|
||||
```java
|
||||
public abstract class InputFormat<K, V> {
|
||||
@ -74,11 +74,11 @@ public abstract class InputFormat<K, V> {
|
||||
|
||||
### 3.2 combiner
|
||||
|
||||
combiner是map运算后的可选操作,其实际上是一个本地化的reduce操作,它主要是在map计算出中间文件后做一个简单的合并重复key值的操作。
|
||||
`combiner`是`map`运算后的可选操作,它实际上是一个本地化的`reduce`操作,它主要是在`map`计算出中间文件后做一个简单的合并重复`key`值的操作。这里以词频统计为例:
|
||||
|
||||
例如我们对文件里的单词频率做统计,map计算时候如果碰到一个hadoop的单词就会记录为1,但是这篇文章里hadoop可能会出现n多次,那么map输出文件冗余就会很多,因此在reduce计算前对相同的key做一个合并操作,那么文件会变小。这样就提高了宽带的传输效率,因为hadoop计算的宽带资源往往是计算的瓶颈也是最为宝贵的资源。
|
||||
`map`在遇到一个hadoop的单词时就会记录为1,但是这篇文章里hadoop可能会出现n多次,那么`map`输出文件冗余就会很多,因此在`reduce`计算前对相同的key做一个合并操作,那么需要传输的数据量就会减少,传输效率就可以得到提升。
|
||||
|
||||
但并非所有场景都适合使用combiner,使用它的原则是combiner的输入不会影响到reduce计算的最终输入,例如:如果计算只是求总数,最大值,最小值可以使用combiner,但是做平均值计算使用combiner的话,最终的reduce计算结果就会出错。
|
||||
但并非所有场景都适合使用`combiner`,使用它的原则是`combiner`的输出不会影响到`reduce`计算的最终输入,例如:如果计算只是求总数,最大值,最小值时都可以使用`combiner`,但是做平均值计算则不能使用`combiner`。
|
||||
|
||||
不使用combiner的情况:
|
||||
|
||||
@ -94,17 +94,7 @@ combiner是map运算后的可选操作,其实际上是一个本地化的reduce
|
||||
|
||||
### 3.3 partitioner
|
||||
|
||||
partitioner可以理解成分类器,按照key的不同分别将map的输出分给不同的reduce,可以自定义实现。
|
||||
|
||||
|
||||
|
||||
### 3.4 sort & combiner
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/mapreduce-sort.png"/> </div>
|
||||
|
||||
经过partitioner处理后,每个key-value对都得到分配到的reduecer信息,然后把记录先写入内存(In-memory buffer)。当写入内存的数据越来越多时,当buffer达到一定阀值(默认80M),就开始执行spill(溢写)步骤,即分成小文件写入磁盘。在写之前,先对memory中每个partition进行排序(in-memory sort)。如果数据量大的话,这个步骤会产生很多个spilled文件,如果我们定义了combine,那么在排序之前还会进行combine,最后一个步骤就是merge,把 spill 步骤产生的所有spilled files,merge成一个大的已排序文件。merge是相同的partition之间进行。
|
||||
|
||||
Merge是怎样的?如“aaa”从某个map task读取过来时值是5,从另外一个map 读取值是8,因为它们有相同的key,所以得merge成group。什么是group。对于“aaa”就是像这样的:{“aaa”, [5, 8, 10, …]},数组中的值就是从不同溢写文件中读取出来的,然后再把这些值加起来。请注意,因为merge是将多个溢写文件合并到一个文件,所以可能也有相同的key存在,在这个过程中如果client设置过Combiner,也会使用Combiner来合并相同的key。
|
||||
`partitioner`可以理解成分类器,将`map`的输出按照key值的不同分别分给对应的`reducer`,支持自定义实现,在后文案例中会有演示。
|
||||
|
||||
|
||||
|
||||
@ -238,7 +228,7 @@ public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
|
||||
}
|
||||
```
|
||||
|
||||
+ **KEYIN** : mapping输入的key的数据类型,即每行的偏移量(每行第一个字符在文本中的位置),Long类型,对应Hadoop中的LongWritable类型;
|
||||
+ **KEYIN** : mapping输入的key的数据类型,即每行的偏移量(每行第一个字符在文本中的位置),Long类型,对应Hadoop中的LongWritable类型;
|
||||
+ **VALUEIN** : mappin输入的value的数据类型,即每行数据;String类型,对应Hadoop中Text类型;
|
||||
+ **KEYOUT** :mapping输出的key的数据类型,即每个单词;String类型,对应Hadoop中Text类型;
|
||||
+ **VALUEOUT**:mapping输出的value的数据类型,即每个单词出现的次数;这里用int类型,对应Hadoop中IntWritable类型;
|
||||
@ -267,7 +257,7 @@ public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritab
|
||||
}
|
||||
```
|
||||
|
||||
这里的key是每个单词,这里的values是一个可迭代的数据类型,因为shuffling输出的数据实际上是下图中所示的这样的,即`key,(1,1,1,1,1,1,1,.....)`。
|
||||
这里的key是每个单词,values是一个可迭代的数据类型,因为shuffling输出的数据实际上是下图中所示的这样的,即`key,(1,1,1,1,1,1,1,.....)`。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hadoop-code-reducer.png"/> </div>
|
||||
|
||||
@ -344,7 +334,7 @@ public class WordCountApp {
|
||||
}
|
||||
```
|
||||
|
||||
这里说明一下:`setMapOutputKeyClass`和`setOutputValueClass`控制reducer函数的输出类型。map函数的输出类型默认情况下和reducer函数式相同的,如果不同,则必须通过`setMapOutputKeyClass`和`setMapOutputValueClass`进行设置。
|
||||
这里说明一下:`setMapOutputKeyClass`和`setOutputValueClass`用于设置reducer函数的输出类型。map函数的输出类型默认情况下和reducer函数式相同的,如果不同,则必须通过`setMapOutputKeyClass`和`setMapOutputValueClass`进行设置。
|
||||
|
||||
### 4.5 提交到服务器运行
|
||||
|
||||
@ -384,7 +374,7 @@ hadoop fs -cat /wordcount/output/WordCountApp/part-r-00000
|
||||
|
||||
### 1. combiner的代码实现
|
||||
|
||||
combiner的代码实现比较简单,只要在组装作业时,添加下面一行代码即可
|
||||
combiner的代码实现比较简单,只要在组装作业时,添加下面一行代码即可:
|
||||
|
||||
```java
|
||||
// 设置Combiner
|
||||
@ -403,7 +393,7 @@ job.setCombinerClass(WordCountReducer.class);
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hadoop-combiner.png"/> </div>
|
||||
|
||||
这里我们只有一个输入文件并且小于128M,所以只有一个Map进行处理,可以看到经过combiner后,records由3519降低为6(样本中单词种类就只有6个),这一点从图中日志的`reduce input records`参数也可以看出来。在这个用例中combiner的效果就非常明显。
|
||||
这里我们只有一个输入文件并且小于128M,所以只有一个Map进行处理,可以看到经过combiner后,records由3519降低为6(样本中单词种类就只有6个),在这个用例中combiner就能极大地降低需要传输的数据量。
|
||||
|
||||
## 5.2 Partitioner
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Hbase Java API 的基本使用
|
||||
# HBase Java API 的基本使用
|
||||
|
||||
<nav>
|
||||
<a href="#一简述">一、简述</a><br/>
|
||||
@ -11,19 +11,19 @@
|
||||
|
||||
## 一、简述
|
||||
|
||||
截至到目前(2019年4月),HBase 主要有1.x 和 2.x 两个主要的版本,两个版本的Java API的接口和方法有所不同的,1.x 中某些方法在2.x中被标识为`@deprecated`过时,所以下面关于API的样例,我会分别给出1.x和2.x两个版本。完整的代码见本仓库:
|
||||
截至到目前(2019.04),HBase 有两个主要的版本,分别是1.x 和 2.x ,两个版本的Java API有所不同,1.x 中某些方法在2.x中被标识为`@deprecated`过时。所以下面关于API的样例,我会分别给出1.x和2.x两个版本。完整的代码见本仓库:
|
||||
|
||||
>+ [Java API 1.x Examples](https://github.com/heibaiying/BigData-Notes/tree/master/code/Hbase/hbase-java-api-1.x)
|
||||
>
|
||||
>+ [Java API 2.x Examples](https://github.com/heibaiying/BigData-Notes/tree/master/code/Hbase/hbase-java-api-2.x)
|
||||
|
||||
同时在实际使用中,客户端的版本必须与服务端保持一致,如果用2.x版本的客户端代码去连接1.x版本的服务端,是会抛出`NoSuchColumnFamilyException`等异常的。
|
||||
同时你使用的客户端的版本必须与服务端版本保持一致,如果用2.x版本的客户端代码去连接1.x版本的服务端,会抛出`NoSuchColumnFamilyException`等异常。
|
||||
|
||||
## 二、Java API 1.x 基本使用
|
||||
|
||||
#### 2.1 新建Maven工程,导入项目依赖
|
||||
|
||||
要使用Java API 操作HBase,仅需要引入`hbase-client`。这里我服务端的HBase版本为`hbase-1.2.0-cdh5.15.2`,对应的`Hbase client` 选取 1.2.0 版本
|
||||
要使用Java API 操作HBase,需要引入`hbase-client`。这里选取的`HBase Client`的版本为`1.2.0`。
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
@ -35,8 +35,6 @@
|
||||
|
||||
#### 2.2 API 基本使用
|
||||
|
||||
这里列举了常用的增删改查操作
|
||||
|
||||
```java
|
||||
public class HBaseUtils {
|
||||
|
||||
@ -294,7 +292,7 @@ public class HBaseUtils {
|
||||
|
||||
### 2.3 单元测试
|
||||
|
||||
以单元测试的方式对封装的API进行测试
|
||||
以单元测试的方式对上面封装的API进行测试。
|
||||
|
||||
```java
|
||||
public class HBaseUtilsTest {
|
||||
@ -398,7 +396,7 @@ public class HBaseUtilsTest {
|
||||
|
||||
#### 3.1 新建Maven工程,导入项目依赖
|
||||
|
||||
这里选取的`HBase Client`的版本为最新的`2.1.4`
|
||||
这里选取的`HBase Client`的版本为最新的`2.1.4`。
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
@ -410,13 +408,13 @@ public class HBaseUtilsTest {
|
||||
|
||||
#### 3.2 API 的基本使用
|
||||
|
||||
2.x 版本相比于1.x 废弃了一部分方法,关于废弃的方法在源码中都会指明新的替代方法,比如,在2.x中创建表时:`HTableDescriptor`和`HColumnDescriptor`等类都标识为废弃,且会在3.0.0版本移除,取而代之的是使用`TableDescriptorBuilder`和`ColumnFamilyDescriptorBuilder`来定义表和列族。在升级版本时,可以用源码中指明的新的替代方法来代替过期的方法。
|
||||
2.x 版本相比于1.x 废弃了一部分方法,关于废弃的方法在源码中都会指明新的替代方法,比如,在2.x中创建表时:`HTableDescriptor`和`HColumnDescriptor`等类都标识为废弃,取而代之的是使用`TableDescriptorBuilder`和`ColumnFamilyDescriptorBuilder`来定义表和列族。
|
||||
|
||||
<div align="center"> <img width="700px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/deprecated.png"/> </div>
|
||||
|
||||
|
||||
|
||||
以下为HBase 2.x 版本Java API使用的完整示例:
|
||||
以下为HBase 2.x 版本Java API的使用示例:
|
||||
|
||||
```java
|
||||
public class HBaseUtils {
|
||||
@ -678,9 +676,9 @@ public class HBaseUtils {
|
||||
|
||||
## 四、正确连接Hbase
|
||||
|
||||
在上面的代码中在类加载时候就初始化了Connection连接,并且之后的方法都是复用这个Connection,这时我们可能会考虑是否可以使用自定义连接池来获取更好的性能表现?实际上这是没有必要的。
|
||||
在上面的代码中,在类加载时就初始化了Connection连接,并且之后的方法都是复用这个Connection,这时我们可能会考虑是否可以使用自定义连接池来获取更好的性能表现?实际上这是没有必要的。
|
||||
|
||||
首先官方对于`Connection`做了如下表述:
|
||||
首先官方对于`Connection`的使用说明如下:
|
||||
|
||||
```properties
|
||||
Connection Pooling For applications which require high-end multithreaded
|
||||
@ -690,9 +688,7 @@ as shown in the following example:
|
||||
|
||||
对于高并发多线程访问的应用程序(例如,在单个JVM中存在的为多个线程服务的Web服务器或应用程序服务器),
|
||||
您只需要预先创建一个Connection。例子如下:
|
||||
```
|
||||
|
||||
```java
|
||||
// Create a connection to the cluster.
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
try (Connection connection = ConnectionFactory.createConnection(conf);
|
||||
@ -715,19 +711,19 @@ Connection是一个集群连接,封装了与多台服务器(Matser/Region Se
|
||||
|
||||
之所以封装这些连接,是因为HBase客户端需要连接三个不同的服务角色:
|
||||
|
||||
+ Zookeeper:主要用于获得meta-region位置,集群Id、master等信息。
|
||||
+ HBase Master:主要用于执行HBaseAdmin接口的一些操作,例如建表等。
|
||||
+ HBase RegionServer:用于读、写数据。
|
||||
+ **Zookeeper** :主要用于获取`meta`表的位置信息,Master的信息;
|
||||
+ **HBase Master** :主要用于执行HBaseAdmin接口的一些操作,例如建表等;
|
||||
+ **HBase RegionServer** :用于读、写数据。
|
||||
|
||||
<div align="center"> <img width="700px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-arc.png"/> </div>
|
||||
|
||||
Connection对象和实际的socket连接之间的对应关系如下图:
|
||||
Connection对象和实际的Socket连接之间的对应关系如下图:
|
||||
|
||||
<div align="center"> <img width="700px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-connection.png"/> </div>
|
||||
|
||||
> 上面两张图片引用自博客:[连接HBase的正确姿势](https://yq.aliyun.com/articles/581702?spm=a2c4e.11157919.spm-cont-list.1.146c27aeFxoMsN%20%E8%BF%9E%E6%8E%A5HBase%E7%9A%84%E6%AD%A3%E7%A1%AE%E5%A7%BF%E5%8A%BF)
|
||||
|
||||
在HBase客户端代码中,真正对应socket连接的是RpcConnection对象。HBase使用PoolMap这种数据结构来存储客户端到HBase服务器之间的连接。PoolMap封装了ConcurrentHashMap>的结构,key是ConnectionId(封装了服务器地址和用户ticket),value是一个RpcConnection对象的资源池。当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象。
|
||||
在HBase客户端代码中,真正对应Socket连接的是`RpcConnection`对象。HBase使用`PoolMap`这种数据结构来存储客户端到HBase服务器之间的连接。`PoolMap`的内部有一个`ConcurrentHashMap`实例,其key是`ConnectionId`(封装了服务器地址和用户ticket),value是一个`RpcConnection`对象的资源池。当HBase需要连接一个服务器时,首先会根据`ConnectionId`找到对应的连接池,然后从连接池中取出一个连接对象。
|
||||
|
||||
```java
|
||||
@InterfaceAudience.Private
|
||||
@ -744,7 +740,7 @@ public class PoolMap<K, V> implements Map<K, V> {
|
||||
.....
|
||||
```
|
||||
|
||||
HBase中提供了三种资源池的实现,分别是Reusable,RoundRobin和ThreadLocal。具体实现可以通hbase.client.ipc.pool.type配置项指定,默认为Reusable。连接池的大小也可以通过hbase.client.ipc.pool.size配置项指定,默认为1,即每个Server 1个连接。也可以通过修改配置实现:
|
||||
HBase中提供了三种资源池的实现,分别是`Reusable`,`RoundRobin`和`ThreadLocal`。具体实现可以通`hbase.client.ipc.pool.type`配置项指定,默认为`Reusable`。连接池的大小也可以通过`hbase.client.ipc.pool.size`配置项指定,默认为1,即每个Server 1个连接。也可以通过修改配置实现:
|
||||
|
||||
```java
|
||||
config.set("hbase.client.ipc.pool.type",...);
|
||||
@ -752,9 +748,9 @@ config.set("hbase.client.ipc.pool.size",...);
|
||||
connection = ConnectionFactory.createConnection(config);
|
||||
```
|
||||
|
||||
从以上的表述中,可以看出HBase中Connection类已经实现了对连接的管理功能,所以我们不需要自己在Connection之上再做额外的管理。
|
||||
由此可以看出HBase中Connection类已经实现了对连接的管理功能,所以我们不必在Connection上在做额外的管理。
|
||||
|
||||
另外,Connection是线程安全的,但Table和Admin却不是线程安全的,因此正确的做法是一个进程共用一个Connection对象,而在不同的线程中使用单独的Table和Admin对象。Table和Admin的获取`getTable()`和`getAdmin()`都是轻量级的操作,所以不必担心性能的消耗,同时使用完成后建议显示的调用`close()`方法关闭它们。
|
||||
另外,Connection是线程安全的,但Table和Admin却不是线程安全的,因此正确的做法是一个进程共用一个Connection对象,而在不同的线程中使用单独的Table和Admin对象。Table和Admin的获取操作`getTable()`和`getAdmin()`都是轻量级,所以不必担心性能的消耗,同时建议在使用完成后显示的调用`close()`方法来关闭它们。
|
||||
|
||||
|
||||
|
||||
|
@ -29,10 +29,10 @@
|
||||
|
||||
## 一、基本命令
|
||||
|
||||
首先打开Hbase Shell
|
||||
打开Hbase Shell:
|
||||
|
||||
```shell
|
||||
# habse shell
|
||||
# hbase shell
|
||||
```
|
||||
|
||||
#### 1.1 获取帮助
|
||||
@ -136,7 +136,7 @@ alter 'Student', {NAME => 'teacherInfo', METHOD => 'delete'}
|
||||
|
||||
#### 3.3 更改列族存储版本的限制
|
||||
|
||||
默认情况下,列族只存储一个版本的数据,如果需要存储多个版本的数据,则需要修改列族的属性。修改后可通过`desc`命令查看
|
||||
默认情况下,列族只存储一个版本的数据,如果需要存储多个版本的数据,则需要修改列族的属性。修改后可通过`desc`命令查看。
|
||||
|
||||
```shell
|
||||
alter 'Student',{NAME=>'baseInfo',VERSIONS=>3}
|
||||
@ -194,13 +194,13 @@ delete 'Student','rowkey3','baseInfo:name'
|
||||
|
||||
## 四、查询
|
||||
|
||||
hbase中访问数据有两种基本的方式
|
||||
hbase中访问数据有两种基本的方式:
|
||||
|
||||
+ 按指定rowkey获取唯一一条数据:get方法
|
||||
+ 按指定rowkey获取数据:get方法;
|
||||
|
||||
+ 按指定条件获取数据:scan方法
|
||||
+ 按指定条件获取数据:scan方法。
|
||||
|
||||
get访问指定key的数据,而scan可以设置begin和end来访问一个范围内所有的数据。get本质上就是begin和end相同的一种特殊的scan。
|
||||
`scan`可以设置begin和end参数来访问一个范围内所有的数据。get本质上就是begin和end相等的一种特殊的scan。
|
||||
|
||||
#### 4.1Get查询
|
||||
|
||||
@ -242,13 +242,13 @@ scan 'Student', {COLUMNS=> 'baseInfo:name',STARTROW => 'rowkey2',STOPROW => 'wro
|
||||
|
||||
#### 4.5 条件过滤
|
||||
|
||||
Filter可以设定一系列条件来进行过滤。如我们要限制某个列的值大于等于24:
|
||||
Filter可以设定一系列条件来进行过滤。如我们要查询值等于24的所有数据:
|
||||
|
||||
```shell
|
||||
scan 'Student', FILTER=>"ValueFilter(=,'binary:24')"
|
||||
```
|
||||
|
||||
值包含yale这个值:
|
||||
值包含yale的所有数据:
|
||||
|
||||
```shell
|
||||
scan 'Student', FILTER=>"ValueFilter(=,'substring:yale')"
|
||||
@ -260,14 +260,14 @@ scan 'Student', FILTER=>"ValueFilter(=,'substring:yale')"
|
||||
scan 'Student', FILTER=>"ColumnPrefixFilter('birth')"
|
||||
```
|
||||
|
||||
FILTER中支持多个过滤条件通过括号、AND和OR的条件组合:
|
||||
FILTER中支持多个过滤条件通过括号、AND和OR进行组合:
|
||||
|
||||
```shell
|
||||
# 列名中的前缀为birth且列值中包含1998的数据
|
||||
scan 'Student', FILTER=>"ColumnPrefixFilter('birth') AND ValueFilter ValueFilter(=,'substring:1998')"
|
||||
```
|
||||
|
||||
`PrefixFilter`是对Rowkey的前缀进行判断:
|
||||
`PrefixFilter`用于对Rowkey的前缀进行判断:
|
||||
|
||||
```shell
|
||||
scan 'Student', FILTER=>"PrefixFilter('wr')"
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
## 一、简述
|
||||
|
||||
在使用Hbase的时,如果当您扩展到数十亿行和数百万列时,在网络中移动大量数据将在网络层产生瓶颈,大量的数据也加重了客户端计算处理的负担。在这种情况下,协处理器(Coprocessors)应运而生。您可以将业务计算代码放入在RegionServer的协处理器中,将处理好的数据再返回给客户端,这可以极大的降低移动的数据量,以获得性能的提升。同时协处理器运也允许用户扩展实现Hbase目前所不具备的功能,如权限校验、二级索引、完整性约束等。
|
||||
在使用HBase时,如果你的数据量达到了数十亿行或数百万列,此时能否在查询中返回大量数据将受制于网络的带宽,即便网络状况允许,但是客户端的计算处理也未必能够满足要求。在这种情况下,协处理器(Coprocessors)应运而生。它允许你将业务计算代码放入在RegionServer的协处理器中,将处理好的数据再返回给客户端,这可以极大地降低需要传输的数据量,从而获得性能上的提升。同时协处理器也允许用户扩展实现HBase目前所不具备的功能,如权限校验、二级索引、完整性约束等。
|
||||
|
||||
|
||||
|
||||
@ -30,11 +30,11 @@
|
||||
|
||||
#### 1. 功能
|
||||
|
||||
Observer协处理器类似于关系型数据库中的触发器,当发生某些事件的时候这类协处理器会被 Server端调用。通常可以用来实现下面功能:
|
||||
Observer协处理器类似于关系型数据库中的触发器,当发生某些事件的时候这类协处理器会被Server端调用。通常可以用来实现下面功能:
|
||||
|
||||
+ 权限校验:在执行`Get`或`Put`操作之前,您可以使用`preGet`或`prePut`方法检查权限;
|
||||
+ 完整性约束: HBase不支持关系型数据库中的外键功能,可以通过触发器在插入或者删除数据的时候,对关联的数据进行检查;
|
||||
+ 二级索引: 可以使用协处理器来维护二级索引。
|
||||
+ **权限校验**:在执行`Get`或`Put`操作之前,您可以使用`preGet`或`prePut`方法检查权限;
|
||||
+ **完整性约束**: HBase不支持关系型数据库中的外键功能,可以通过触发器在插入或者删除数据的时候,对关联的数据进行检查;
|
||||
+ **二级索引**: 可以使用协处理器来维护二级索引。
|
||||
|
||||
</br>
|
||||
|
||||
@ -55,7 +55,7 @@ Observer协处理器类似于关系型数据库中的触发器,当发生某些
|
||||
|
||||
#### 3. 接口
|
||||
|
||||
以上四种类型的Observer协处理器均继承自`Coprocessor`接口,这四个接口中分别定义了所有可用的钩子方法,以便在对应方法前后执行特定的操作。通常情况下,我们并不会直接实现上面接口,而是继承其Base实现类,Base实现类简单空实现了接口中的方法,这样我们在实现自定义的协处理器时,就可以按需重写对应的方法。
|
||||
以上四种类型的Observer协处理器均继承自`Coprocessor`接口,这四个接口中分别定义了所有可用的钩子方法,以便在对应方法前后执行特定的操作。通常情况下,我们并不会直接实现上面接口,而是继承其Base实现类,Base实现类只是简单空实现了接口中的方法,这样我们在实现自定义的协处理器时,就不必实现所有方法,只需要重写必要方法即可。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-coprocessor.png"/> </div>
|
||||
|
||||
@ -72,9 +72,9 @@ Observer协处理器类似于关系型数据库中的触发器,当发生某些
|
||||
+ 客户端发出 put 请求
|
||||
+ 该请求被分派给合适的 RegionServer 和 region
|
||||
+ coprocessorHost 拦截该请求,然后在该表的每个 RegionObserver 上调用 prePut()
|
||||
+ 如果没有被 prePut()拦截,该请求继续送到 region,然后进行处理
|
||||
+ region 产生的结果再次被 CoprocessorHost 拦截,调用 postPut()
|
||||
+ 假如没有 postPut()拦截该响应,最终结果被返回给客户端
|
||||
+ 如果没有被`prePut()`拦截,该请求继续送到 region,然后进行处理
|
||||
+ region 产生的结果再次被 CoprocessorHost 拦截,调用`postPut()`
|
||||
+ 假如没有`postPut()`拦截该响应,最终结果被返回给客户端
|
||||
|
||||
如果大家了解Spring,可以将这种执行方式类比于其AOP的执行原理即可,官方文档当中也是这样类比的:
|
||||
|
||||
@ -88,7 +88,7 @@ Observer协处理器类似于关系型数据库中的触发器,当发生某些
|
||||
|
||||
Endpoint协处理器类似于关系型数据库中的存储过程。客户端可以调用Endpoint协处理器在服务端对数据进行处理,然后再返回。
|
||||
|
||||
以聚集操作为例,如果没有协处理器,当用户需要找出一张表中的最大数据,即 max聚合操作,就必须进行全表扫描,在客户端上遍历扫描结果,这必然会加重了客户端处理数据的压力。利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase将利用底层 cluster 的多个节点并发执行求最大值的操作。即在每个 Region 范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给客户端。在客户端进一步将多个Region的最大值进一步处理而找到其中的最大值,以提高执行效率。
|
||||
以聚集操作为例,如果没有协处理器,当用户需要找出一张表中的最大数据,即 max 聚合操作,就必须进行全表扫描,然后在客户端上遍历扫描结果,这必然会加重了客户端处理数据的压力。利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase将利用底层 cluster 的多个节点并发执行求最大值的操作。即在每个 Region 范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出来,仅仅将该 max 值返回给客户端。之后客户端只需要将每个 Region 的最大值进行比较而找到其中最大的值即可。
|
||||
|
||||
|
||||
|
||||
@ -96,8 +96,8 @@ Endpoint协处理器类似于关系型数据库中的存储过程。客户端可
|
||||
|
||||
要使用我们自己开发的协处理器,必须通过静态(使用HBase配置)或动态(使用HBase Shell或Java API)加载它。
|
||||
|
||||
+ 静态加载的协处理器称之为 System Coprocessor(系统级协处理器),作用范围是整个Hbase上的所有表,需要重启Hbase;
|
||||
+ 动态加载的协处理器称 之为 Table Coprocessor(表处理器),作用于指定的表,不需要重启Hbase。
|
||||
+ 静态加载的协处理器称之为 **System Coprocessor**(系统级协处理器),作用范围是整个HBase上的所有表,需要重启HBase服务;
|
||||
+ 动态加载的协处理器称之为 **Table Coprocessor**(表处理器),作用于指定的表,不需要重启HBase服务。
|
||||
|
||||
其加载和卸载方式分别介绍如下。
|
||||
|
||||
@ -109,7 +109,7 @@ Endpoint协处理器类似于关系型数据库中的存储过程。客户端可
|
||||
|
||||
静态加载分以下三步:
|
||||
|
||||
1. 在hbase-site.xml定义需要加载的协处理器
|
||||
1. 在`hbase-site.xml`定义需要加载的协处理器。
|
||||
|
||||
```xml
|
||||
<property>
|
||||
@ -118,27 +118,27 @@ Endpoint协处理器类似于关系型数据库中的存储过程。客户端可
|
||||
</property>
|
||||
```
|
||||
|
||||
\<name>标签的值必须是下面其中之一:
|
||||
` <name>`标签的值必须是下面其中之一:
|
||||
|
||||
+ RegionObservers 和 Endpoints协处理器:`hbase.coprocessor.region.classes`
|
||||
+ WALObservers协处理器: `hbase.coprocessor.wal.classes`
|
||||
+ MasterObservers协处理器:`hbase.coprocessor.master.classes`
|
||||
|
||||
\<value>必须是协处理器实现类的全限定类名。如果为加载指定了多个类,则类名必须以逗号分隔。
|
||||
`<value>`必须是协处理器实现类的全限定类名。如果为加载指定了多个类,则类名必须以逗号分隔。
|
||||
|
||||
2. 将jar(包含代码和所有依赖项)放入HBase安装目录中的`lib`目录下
|
||||
2. 将jar(包含代码和所有依赖项)放入HBase安装目录中的`lib`目录下;
|
||||
|
||||
3. 重启HBase
|
||||
3. 重启HBase。
|
||||
|
||||
</br>
|
||||
|
||||
### 4.2 静态卸载
|
||||
|
||||
1. 从hbase-site.xml中删除配置的协处理器的\<property>元素及其子元素
|
||||
1. 从hbase-site.xml中删除配置的协处理器的\<property>元素及其子元素;
|
||||
|
||||
2. 从类路径或HBase的lib目录中删除协处理器的JAR文件(可选)
|
||||
2. 从类路径或HBase的lib目录中删除协处理器的JAR文件(可选);
|
||||
|
||||
3. 重启HBase
|
||||
3. 重启HBase。
|
||||
|
||||
|
||||
|
||||
@ -169,17 +169,16 @@ user/<hadoop-user>/coprocessor.jar| org.myname.hbase.Coprocessor.RegionObserverE
|
||||
arg1=1,arg2=2'
|
||||
```
|
||||
|
||||
Coprocessor 包含由管道(|)字符分隔的四个参数,按顺序解释如下:
|
||||
`Coprocessor`包含由管道(|)字符分隔的四个参数,按顺序解释如下:
|
||||
|
||||
+ JAR包路径:通常为JAR包在HDFS上的路径。关于路径以下两点需要注意:
|
||||
|
||||
+ 允许使用通配符,例如:`hdfs://<namenode>:<port>/user/<hadoop-user>/*.jar` 来添加指定的JAR包;
|
||||
|
||||
+ 可以使指定目录,例如:`hdfs://<namenode>:<port>/user/<hadoop-user>/` ,这会添加目录中的所有JAR包,但不会搜索子目录中的JAR包。
|
||||
|
||||
+ 类名:协处理器的完整类名。
|
||||
+ 优先级:协处理器的优先级,遵循数字的自然序,即值越小优先级越高。可以为空,在这种情况下,将分配默认优先级值。
|
||||
+ 参数(可选):传递的协处理器的可选参数。
|
||||
+ **JAR包路径**:通常为JAR包在HDFS上的路径。关于路径以下两点需要注意:
|
||||
+ 允许使用通配符,例如:`hdfs://<namenode>:<port>/user/<hadoop-user>/*.jar` 来添加指定的JAR包;
|
||||
|
||||
+ 可以使指定目录,例如:`hdfs://<namenode>:<port>/user/<hadoop-user>/` ,这会添加目录中的所有JAR包,但不会搜索子目录中的JAR包。
|
||||
|
||||
+ **类名**:协处理器的完整类名。
|
||||
+ **优先级**:协处理器的优先级,遵循数字的自然序,即值越小优先级越高。可以为空,在这种情况下,将分配默认优先级值。
|
||||
+ **可选参数** :传递的协处理器的可选参数。
|
||||
|
||||
3. 启用表
|
||||
|
||||
@ -292,7 +291,7 @@ admin.enableTable(tableName);
|
||||
|
||||
## 六、协处理器案例
|
||||
|
||||
这里给出一个简单的案例,实现一个类似于Redis中`append` 命令的协处理器,当我们对已有列执行put操作时候,Hbase默认执行的是update操作,这里我们修改为执行append操作。
|
||||
这里给出一个简单的案例,实现一个类似于Redis中`append` 命令的协处理器,当我们对已有列执行put操作时候,HBase默认执行的是update操作,这里我们修改为执行append操作。
|
||||
|
||||
```shell
|
||||
# redis append 命令示例
|
||||
@ -306,14 +305,14 @@ redis> GET mykey
|
||||
"Hello World"
|
||||
```
|
||||
|
||||
#### 6.1 创建测试表
|
||||
### 6.1 创建测试表
|
||||
|
||||
```shell
|
||||
# 创建一张杂志表 有文章和图片两个列族
|
||||
hbase > create 'magazine','article','picture'
|
||||
```
|
||||
|
||||
#### 6.2 协处理器编程
|
||||
### 6.2 协处理器编程
|
||||
|
||||
> 完整代码可见本仓库:[hbase-observer-coprocessor](https://github.com/heibaiying/BigData-Notes/tree/master/code/Hbase\hbase-observer-coprocessor)
|
||||
|
||||
@ -368,15 +367,15 @@ public class AppendRegionObserver extends BaseRegionObserver {
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.3 打包项目
|
||||
### 6.3 打包项目
|
||||
|
||||
由于项目使用的是maven构建,直接执行以下命令进行打包,这里我打包后的文件名为`hbase-observer-coprocessor-1.0-SNAPSHOT.jar`
|
||||
使用maven命令进行打包,打包后的文件名为`hbase-observer-coprocessor-1.0-SNAPSHOT.jar`
|
||||
|
||||
```shell
|
||||
# mvn clean package
|
||||
```
|
||||
|
||||
#### 6.4 上传JAR包到HDFS
|
||||
### 6.4 上传JAR包到HDFS
|
||||
|
||||
```shell
|
||||
# 上传项目到HDFS上的hbase目录
|
||||
@ -387,7 +386,7 @@ hadoop fs -ls /hbase
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-cp-hdfs.png"/> </div>
|
||||
|
||||
#### 6.5 加载协处理器
|
||||
### 6.5 加载协处理器
|
||||
|
||||
1. 加载协处理器前需要先禁用表
|
||||
|
||||
@ -416,7 +415,7 @@ hbase > desc 'magazine'
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-cp-load.png"/> </div>
|
||||
|
||||
#### 6.6 测试加载结果
|
||||
### 6.6 测试加载结果
|
||||
|
||||
插入一组测试数据:
|
||||
|
||||
@ -444,7 +443,7 @@ hbase > get 'magazine','rowkey1','article:author'
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-cp-lisi.png"/> </div>
|
||||
|
||||
#### 6.7 卸载协处理器
|
||||
### 6.7 卸载协处理器
|
||||
1. 卸载协处理器前需要先禁用表
|
||||
|
||||
```shell
|
||||
@ -470,7 +469,7 @@ hbase > desc 'magazine'
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-co-unload.png"/> </div>
|
||||
|
||||
#### 6.8 测试卸载结果
|
||||
### 6.8 测试卸载结果
|
||||
|
||||
依次执行下面命令可以测试卸载是否成功
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
## 一、前言
|
||||
|
||||
本文主要介绍Hbase常用的三种简单的容灾备份方案,即CopyTable、Export/Import、Snapshot。分别介绍如下:
|
||||
本文主要介绍Hbase常用的三种简单的容灾备份方案,即**CopyTable**、**Export**/**Import**、**Snapshot**。分别介绍如下:
|
||||
|
||||
|
||||
|
||||
@ -27,11 +27,11 @@
|
||||
|
||||
### 2.1 简介
|
||||
|
||||
CopyTable 可以将现有表的数据复制到新表中,具有以下特点:
|
||||
**CopyTable**可以将现有表的数据复制到新表中,具有以下特点:
|
||||
|
||||
- 支持时间区间 、row区间,改变表名称, 改变列族名称,指定是否 Copy已经被删除的数据等功能;
|
||||
- 支持时间区间 、row区间 、改变表名称 、改变列族名称 、以及是否Copy已被删除的数据等功能;
|
||||
- 执行命令前,需先创建与原表结构相同的新表;
|
||||
- CopyTable工具采用scan查询, 写入新表时采用put和delete API, 全是基于hbase的client Api进行读写。
|
||||
- `CopyTable`的操作是基于HBase Client API进行的,即采用`scan`进行查询, 采用`put`进行写入。
|
||||
|
||||
### 2.2 命令格式
|
||||
|
||||
@ -44,24 +44,31 @@ Usage: CopyTable [general options] [--starttime=X] [--endtime=Y] [--new.name=NEW
|
||||
1. 同集群下CopyTable
|
||||
|
||||
```shell
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --new.name=tableCopy tableOrig
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --new.name=tableCopy tableOrig
|
||||
```
|
||||
|
||||
2. 不同集群下CopyTable
|
||||
|
||||
```shell
|
||||
# 两表名称相同的情况
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --peer.adr=dstClusterZK:2181:/hbase tableOrig
|
||||
|
||||
# 也可以指新的表名
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --peer.adr=dstClusterZK:2181:/hbase --new.name=tableCopy tableOrig
|
||||
# 两表名称相同的情况
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable \
|
||||
--peer.adr=dstClusterZK:2181:/hbase tableOrig
|
||||
|
||||
# 也可以指新的表名
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable \
|
||||
--peer.adr=dstClusterZK:2181:/hbase \
|
||||
--new.name=tableCopy tableOrig
|
||||
```
|
||||
|
||||
|
||||
3. 下面是一个官方给的比较完整的例子,指定开始和结束时间,集群地址,以及只复制指定的列族:
|
||||
|
||||
```shell
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=1265875194289 --endtime=1265878794289 --peer.adr=server1,server2,server3:2181:/hbase --families=myOldCf:myNewCf,cf2,cf3 TestTable
|
||||
hbase org.apache.hadoop.hbase.mapreduce.CopyTable \
|
||||
--starttime=1265875194289 \
|
||||
--endtime=1265878794289 \
|
||||
--peer.adr=server1,server2,server3:2181:/hbase \
|
||||
--families=myOldCf:myNewCf,cf2,cf3 TestTable
|
||||
```
|
||||
|
||||
### 2.4 更多参数
|
||||
@ -80,21 +87,21 @@ Usage: CopyTable [general options] [--starttime=X] [--endtime=Y] [--new.name=NEW
|
||||
|
||||
### 3.1 简介
|
||||
|
||||
- Export可导出数据到HDFS, 然后可通过Import导入数据, Export支持指定开始时间和结束时间, 因此可以做增量备份;
|
||||
- Export导出工具与CopyTable一样是依赖hbase的scan读取数据
|
||||
- `Export`支持导出数据到HDFS, `Import`支持从HDFS导入数据。`Export`还支持指定导出数据的开始时间和结束时间,因此可以用于增量备份。
|
||||
- `Export`导出与`CopyTable`一样,依赖HBase的`scan`操作
|
||||
|
||||
### 3.2 命令格式
|
||||
|
||||
```shell
|
||||
# Export
|
||||
bin/hbase org.apache.hadoop.hbase.mapreduce.Export <tablename> <outputdir> [<versions> [<starttime> [<endtime>]]]
|
||||
hbase org.apache.hadoop.hbase.mapreduce.Export <tablename> <outputdir> [<versions> [<starttime> [<endtime>]]]
|
||||
|
||||
# Inport
|
||||
bin/hbase org.apache.hadoop.hbase.mapreduce.Import <tablename> <inputdir>
|
||||
hbase org.apache.hadoop.hbase.mapreduce.Import <tablename> <inputdir>
|
||||
```
|
||||
|
||||
+ 对于导出,`outputdir`目录可以不用预先创建,程序会自动创建。导出完成后,导出的文件将由调用导出命令的用户拥有。
|
||||
+ 默认情况下,仅导出给定`Cell`的最新版本,而不管存储的版本数量。要导出多个版本,需要将\<versions>替换为所需的版本数。
|
||||
+ 导出的`outputdir`目录可以不用预先创建,程序会自动创建。导出完成后,导出文件的所有权将由执行导出命令的用户所拥有。
|
||||
+ 默认情况下,仅导出给定`Cell`的最新版本,而不管历史版本。要导出多个版本,需要将`<versions>`参数替换为所需的版本数。
|
||||
|
||||
### 3.3 常用命令
|
||||
|
||||
@ -116,15 +123,11 @@ hbase org.apache.hadoop.hbase.mapreduce.Import tableName hdfs路径/tableName.d
|
||||
|
||||
### 4.1 简介
|
||||
|
||||
HBase快照(Snapshot)允许您获取表的副本(包括内容和元数据),并且性能开销很小。因为快照存储的仅仅是表的元数据和HFiles的信息。
|
||||
|
||||
快照的“clone”操作会从该快照创建新表,快照的“restore”将表的内容还原到快照节点。“clone”和“restore”操作不需要复制任何数据,因为底层HFiles(包含HBase表数据的文件)不会被任何操作修改,修改的只是表的元数据信息。
|
||||
|
||||
其实在版本0.94.6之前,备份或克隆表的唯一方法是使用CopyTable / ExportTable,或者在禁用表后复制HDFS中的所有HFiles。这些方都会降低Region服务器性能(复制/导出表),且在禁用表,会导致表无法读取或写入,这在实际的生产环境中都是不允许的。
|
||||
HBase的快照(Snapshot)功能允许您获取表的副本(包括内容和元数据),并且性能开销很小。因为快照存储的仅仅是表的元数据和HFiles的信息。快照的`clone`操作会从该快照创建新表,快照的`restore`操作会将表的内容还原到快照节点。`clone`和`restore`操作不需要复制任何数据,因为底层HFiles(包含HBase表数据的文件)不会被修改,修改的只是表的元数据信息。
|
||||
|
||||
### 4.2 配置
|
||||
|
||||
Snapshot快照功能默认是没有开启的,如果要开启快照,则需要预先在`hbase-site.xml`文件中添加如下配置项:
|
||||
HBase快照功能默认没有开启,如果要开启快照,需要在`hbase-site.xml`文件中添加如下配置项:
|
||||
|
||||
```xml
|
||||
<property>
|
||||
@ -137,7 +140,7 @@ HBase快照(Snapshot)允许您获取表的副本(包括内容和元数据
|
||||
|
||||
### 4.3 常用命令
|
||||
|
||||
预先说明一下之前的CopyTable、Export/Import命令都是在系统环境下直接执行的,而快照的所有命令都需要在Hbase Shell交互式命令行中执行。
|
||||
快照的所有命令都需要在Hbase Shell交互式命令行中执行。
|
||||
|
||||
#### 1. Take a Snapshot
|
||||
|
||||
@ -183,7 +186,7 @@ hbase> disable '表名'
|
||||
hbase> restore_snapshot '快照名'
|
||||
```
|
||||
|
||||
这里还需要注意的:是如果Hbase配置了基于Replication的主从复制,由于Replication在日志级别工作,而快照在文件系统级别工作,因此在还原之后,会出现副本与主服务器处于不同的状态的情况。这时候可以先停止同步,所有服务器还原到一致的数据点后再重新建立同步。
|
||||
这里需要注意的是:是如果HBase配置了基于Replication的主从复制,由于Replication在日志级别工作,而快照在文件系统级别工作,因此在还原之后,会出现副本与主服务器处于不同的状态的情况。这时候可以先停止同步,所有服务器还原到一致的数据点后再重新建立同步。
|
||||
|
||||
|
||||
|
||||
|
@ -23,9 +23,9 @@
|
||||
|
||||
## 一、Phoenix简介
|
||||
|
||||
Phoenix是HBase的开源SQL层。使得您可以使用标准JDBC API而不是常规HBase客户端API来操作Hbases上的数据。
|
||||
`Phoenix`是HBase的开源SQL中间层,它允许你使用标准JDBC的方式来操作HBase上的数据。在`Phoenix`之前,如果你要访问HBase,只能调用它的Java API,但相比于使用一行SQL就能实现数据查询,HBase的API还是过于复杂。`Phoenix`的理念是`we put sql SQL back in NOSQL`,即你可以使用标准的SQL就能完成对HBase上数据的操作。同时这也意味着你可以通过集成`Spring Data JPA`或`Mybatis`等常用的持久层框架来操作HBase。
|
||||
|
||||
Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase scan,并编排并行执行以生成标准的JDBC结果集,同时Phoenix还拥有二级索引等Hbase不具备的特性,这使得Phoenix具有极好的性能表现。
|
||||
其次`Phoenix`的性能表现也非常优异,`Phoenix`查询引擎会将SQL查询转换为一个或多个HBase Scan,通过并行执行来生成标准的JDBC结果集。它通过直接使用HBase API以及协处理器和自定义过滤器,可以为小型数据查询提供毫秒级的性能,为千万行数据的查询提供秒级的性能。同时Phoenix还拥有二级索引等HBase不具备的特性,因为以上的优点,所以`Phoenix`成为了HBase最优秀的SQL中间层。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/Phoenix-hadoop.png"/> </div>
|
||||
|
||||
@ -43,9 +43,7 @@ Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询
|
||||
|
||||
### 2.1 下载并解压
|
||||
|
||||
官方下载地址: http://phoenix.apache.org/download.html
|
||||
|
||||
官方针对Apache版本和CDH版本的HBase均提供了安装包,按需下载即可。这里我们下载的版本为`4.14.0-cdh5.14.2`
|
||||
官方针对Apache版本和CDH版本的HBase均提供了安装包,按需下载即可。官方下载地址: http://phoenix.apache.org/download.html
|
||||
|
||||
```shell
|
||||
# 下载
|
||||
@ -56,9 +54,9 @@ tar tar apache-phoenix-4.14.0-cdh5.14.2-bin.tar.gz
|
||||
|
||||
### 2.2 拷贝Jar包
|
||||
|
||||
按照官方文档的说明,需要将phoenix server jar 添加到所有 Region Servers上 Hbase 安装目录的 lib目录下。
|
||||
按照官方文档的说明,需要将`phoenix server jar`添加到所有`Region Servers`的安装目录的`lib`目录下。
|
||||
|
||||
这里由于我搭建的是Hbase伪集群,所以只需要拷贝到当前机器的HBase的lib目录下。如果是真实集群,则使用scp命令分发到所有Region Servers机器上。
|
||||
这里由于我搭建的是HBase伪集群,所以只需要拷贝到当前机器的HBase的lib目录下。如果是真实集群,则使用scp命令分发到所有`Region Servers`机器上。
|
||||
|
||||
```shell
|
||||
cp /usr/app/apache-phoenix-4.14.0-cdh5.14.2-bin/phoenix-4.14.0-cdh5.14.2-server.jar /usr/app/hbase-1.2.0-cdh5.15.2/lib
|
||||
@ -75,10 +73,10 @@ start-hbase.sh
|
||||
|
||||
### 2.4 启动Phoenix
|
||||
|
||||
在Phoenix解压目录下的`bin`目录下执行如下命令,需要指定Zookeeper的地址:
|
||||
在Phoenix解压目录下的`bin`目录下执行如下命令,需要指定Zookeeper的地址:
|
||||
|
||||
+ 如果HBase采用Standalone模式或者伪集群模式搭建,则采用内置的 Zookeeper,默认端口为2181;
|
||||
+ 如果是HBase是集群模式并采用自己搭建的Zookeeper集群,则按照自己的实际情况指定端口
|
||||
+ 如果HBase采用Standalone模式或者伪集群模式搭建,则默认采用内置的 Zookeeper服务,端口为2181;
|
||||
+ 如果是HBase是集群模式并采用外置的Zookeeper集群,则按照自己的实际情况进行指定。
|
||||
|
||||
```shell
|
||||
# ./sqlline.py hadoop001:2181
|
||||
@ -106,7 +104,7 @@ CREATE TABLE IF NOT EXISTS us_population (
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/Phoenix-create-table.png"/> </div>
|
||||
|
||||
新建的表会按照特定的规则转换为Hbase上的表,关于表的信息,可以通过Hbase Web UI 进行查看:
|
||||
新建的表会按照特定的规则转换为HBase上的表,关于表的信息,可以通过Hbase Web UI 进行查看:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-web-ui-phoenix.png"/> </div>
|
||||
|
||||
@ -167,29 +165,27 @@ ORDER BY sum(population) DESC;
|
||||
|
||||
### 3.7 扩展
|
||||
|
||||
从上面的简单操作中我们可以看出,Phoenix 查询语句与我们正常使用的SQL是基本相同的,关于Phoenix 支持的语句、数据类型、函数、序列(和Oracle中序列类似)因为涵盖内容很广,可以参考其官方文档,官方上有详尽的配图说明的:
|
||||
从上面的操作中可以看出,Phoenix支持大多数标准的SQL语法。关于Phoenix支持的语法、数据类型、函数、序列等详细信息,因为涉及内容很多,可以参考其官方文档,官方文档上有详细的说明:
|
||||
|
||||
+ 语法(Grammar):https://phoenix.apache.org/language/index.html
|
||||
+ **语法(Grammar)** :https://phoenix.apache.org/language/index.html
|
||||
|
||||
+ 函数(Functions):http://phoenix.apache.org/language/functions.html
|
||||
+ **函数(Functions)** :http://phoenix.apache.org/language/functions.html
|
||||
|
||||
+ 数据类型(Datatypes):http://phoenix.apache.org/language/datatypes.html
|
||||
+ **数据类型(Datatypes)** :http://phoenix.apache.org/language/datatypes.html
|
||||
|
||||
+ 序列(Sequences):http://phoenix.apache.org/sequences.html
|
||||
+ **序列(Sequences)** :http://phoenix.apache.org/sequences.html
|
||||
|
||||
+ 联结查询(Joins):http://phoenix.apache.org/joins.html
|
||||
+ **联结查询(Joins)** :http://phoenix.apache.org/joins.html
|
||||
|
||||
|
||||
|
||||
## 四、Phoenix Java API
|
||||
|
||||
因为Phoenix遵循JDBC规范,并提供了对应的数据库驱动PhoenixDriver,这使采用Java对其进行操作的时候,就如同对其他关系型数据库(例如 MySQL)操作一样。
|
||||
|
||||
因为在实际的开发中我们通常都是采用第三方框架,比如mybatis,Hibernate,Spring Data 等,很少使用原生Java API操作关系型数据库,所以这里只给出一个简单的查询作为示例,并在下一篇文章中给出Spring boot + mybatis + Phoenix 的整合用例。
|
||||
因为Phoenix遵循JDBC规范,并提供了对应的数据库驱动`PhoenixDriver`,这使得采用Java语言对其进行操作的时候,就如同对其他关系型数据库一样,下面给出基本的使用示例。
|
||||
|
||||
### 4.1 引入Phoenix core JAR包
|
||||
|
||||
如果是maven项目,直接在maven中央仓库找到对应的版本,导入依赖即可
|
||||
如果是maven项目,直接在maven中央仓库找到对应的版本,导入依赖即可:
|
||||
|
||||
```xml
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-core -->
|
||||
@ -200,7 +196,7 @@ ORDER BY sum(population) DESC;
|
||||
</dependency>
|
||||
```
|
||||
|
||||
如果是普通项目,则可以从Phoenix 解压目录下找到对应的JAR包,然后手动引入
|
||||
如果是普通项目,则可以从Phoenix解压目录下找到对应的JAR包,然后手动引入:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/phoenix-core-jar.png"/> </div>
|
||||
|
||||
@ -247,6 +243,8 @@ public class PhoenixJavaApi {
|
||||
|
||||
|
||||
|
||||
实际的开发中我们通常都是采用第三方框架来操作数据库,如`mybatis`,`Hibernate`,`Spring Data`等。关于Phoenix与这些框架的整合步骤参见下一篇文章:[Spring/Spring Boot + Mybatis + Phoenix](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Spring+Mybtais+Phoenix整合.md)
|
||||
|
||||
# 参考资料
|
||||
|
||||
1. http://phoenix.apache.org/
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Hbase简介
|
||||
# HBase简介
|
||||
|
||||
<nav>
|
||||
<a href="#一Hadoop的局限">一、Hadoop的局限</a><br/>
|
||||
@ -13,15 +13,15 @@ HBase是一个构建在Hadoop文件系统之上的面向列的数据库管理系
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase.jpg"/> </div>
|
||||
|
||||
这里先介绍一下Hadoop的优点和存在的限制。Hadoop擅长存储任意的、半结构甚至非结构化的数据,主要通过HDFS来存储,使用MapReduce来处理。以下是三种数据类型的解释:
|
||||
要想明白为什么产生HBase,就需要先了解一下Hadoop存在的限制?Hadoop可以通过HDFS来存储结构化、半结构甚至非结构化的数据,它是传统数据库的补充,是海量数据存储的最佳方法,它针对大文件的存储,批量访问和流式访问都做了优化,同时也通过多副本解决了容灾问题。
|
||||
|
||||
- 结构化数据:即以关系型数据库表形式管理的数据;
|
||||
- 半结构化数据:非关系模型的,有基本固定结构模式的数据,例如日志文件、XML文档、JSON文档、Email等;
|
||||
- 非结构化数据:没有固定模式的数据,如WORD、PDF、PPT、EXL,各种格式的图片、视频等。
|
||||
但是Hadoop的缺陷在于它只能执行批处理,并且只能以顺序方式访问数据,这意味着即使是最简单的工作,也必须搜索整个数据集,无法实现对数据的随机访问。实现数据的随机访问是传统的关系型数据库所擅长的,但它们却不能用于海量数据的存储。在这种情况下,必须有一种新的方案来解决海量数据存储和随机访问的问题,HBase就是其中之一(HBase,Cassandra,couchDB,Dynamo和MongoDB都能存储海量数据并支持随机访问)。
|
||||
|
||||
Hadoop是传统数据库的补充,是海量数据存储的最佳方法,支持用户在恰当的时候存储和获取数据,并且针对大文件的存储,批量访问和流式访问做了优化,同时也通过多副本解决了容灾问题。但是Hadoop只能执行批处理,并且只能以顺序方式访问数据,这意味着即使是最简单的工作,也必须搜索整个数据集,无法实现对数据的随机访问。实现数据的随机访问是传统的RDBMS所擅长的,但其却不能存储海量的数据。在这种情况下,必须有一种新的方案来解决海量数据存储和随机访问的问题,HBase就是其中之一。
|
||||
|
||||
> 注:HBase,Cassandra,couchDB,Dynamo和MongoDB都能存储海量数据并支持随机访问。
|
||||
> 注:数据结构分类:
|
||||
>
|
||||
> - 结构化数据:即以关系型数据库表形式管理的数据;
|
||||
> - 半结构化数据:非关系模型的,有基本固定结构模式的数据,例如日志文件、XML文档、JSON文档、Email等;
|
||||
> - 非结构化数据:没有固定模式的数据,如WORD、PDF、PPT、EXL,各种格式的图片、视频等。
|
||||
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ Hadoop是传统数据库的补充,是海量数据存储的最佳方法,支
|
||||
|
||||
HBase是一个构建在Hadoop文件系统之上的面向列的数据库管理系统。
|
||||
|
||||
HBase是一种类似于Google’s big table的数据模型,它是Hadoop生态系统的一部分,它将数据存储在HDFS上,客户端可以通过HBase实现对HDFS上数据的随机访问。它具有以下特性:
|
||||
HBase是一种类似于`Google’s Big Table`的数据模型,它是Hadoop生态系统的一部分,它将数据存储在HDFS上,客户端可以通过HBase实现对HDFS上数据的随机访问。它具有以下特性:
|
||||
|
||||
+ 不支持复杂的事务,只支持行级事务,即单行数据的读写都是原子性的;
|
||||
+ 由于是采用HDFS作为底层存储,所以和HDFS一样,支持结构化、半结构化和非结构化的存储;
|
||||
@ -44,13 +44,13 @@ HBase是一种类似于Google’s big table的数据模型,它是Hadoop生态
|
||||
|
||||
## 三、HBase Table
|
||||
|
||||
HBase是一个面向列的数据库管理系统,这里更为确切的而说,HBase是一个面向列族的数据库管理系统。表 schema 仅定义列族,表具有多个列族,每个列族可以包含任意数量的列,列由多个单元格(cell )组成,单元格可以存储多个版本的数据,多个版本数据以时间戳进行区分。
|
||||
HBase是一个面向`列`的数据库管理系统,这里更为确切的而说,HBase是一个面向`列族`的数据库管理系统。表 schema 仅定义列族,表具有多个列族,每个列族可以包含任意数量的列,列由多个单元格(cell )组成,单元格可以存储多个版本的数据,多个版本数据以时间戳进行区分。
|
||||
|
||||
下图为Hbase中一张表的:
|
||||
下图为HBase中一张表的:
|
||||
|
||||
+ RowKey为行的唯一标识,所有行按照RowKey的字典序进行排序;
|
||||
+ 该表具有两个列族,分别是personal和office;
|
||||
+ 其中列族personal拥有name、city、phone三个列,office拥有tel、addres两个列。
|
||||
+ 其中列族personal拥有name、city、phone三个列,列族office拥有tel、addres两个列。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBase_table-iteblog.png"/> </div>
|
||||
|
||||
@ -60,23 +60,21 @@ Hbase的表具有以下特点:
|
||||
|
||||
- 容量大:一个表可以有数十亿行,上百万列;
|
||||
|
||||
- 面向列:数据是按照列存储,每一列都单独存放,数据即索引,在查询时可以只访问指定列的数据,有效系统的I/O性能;
|
||||
- 面向列:数据是按照列存储,每一列都单独存放,数据即索引,在查询时可以只访问指定列的数据,有效地降低了系统的I/O负担;
|
||||
|
||||
- 稀疏性:空 (null) 列并不占用存储空间,表可以设计的非常稀疏 ;
|
||||
|
||||
- 数据多版本:每个单元中的数据可以有多个版本,按照时间戳排序,新的数据在最上面;
|
||||
|
||||
- 存储类型:底层所有数据的存储都是字节数组(byte[])
|
||||
- 存储类型:所有数据的底层存储格式都是字节数组(byte[])。
|
||||
|
||||
|
||||
|
||||
## 四、Phoenix
|
||||
|
||||
Phoenix是HBase的开源SQL中间层。使得您可以使用标准JDBC API而不是常规HBase客户端API来操作HBase上的数据。
|
||||
`Phoenix`是HBase的开源SQL中间层,它允许你使用标准JDBC的方式来操作HBase上的数据。在`Phoenix`之前,如果你要访问HBase,只能调用它的Java API,但相比于使用一行SQL就能实现数据查询,HBase的API还是过于复杂。`Phoenix`的理念是`we put sql SQL back in NOSQL`,即你可以使用标准的SQL就能完成对HBase上数据的操作。同时这也意味着你可以通过集成`Spring Data JPA`或`Mybatis`等常用的持久层框架来操作HBase。
|
||||
|
||||
简单来说,如果你要直接使用HBase,你就只能通过它的Java API来进行调用,虽然官网介绍它的API是简单易用的,但相比于使用一行SQL就能实现数据的查询过滤,原生的API还是过于复杂,Phoenix 的理念是`we put sql SQL back in NOSQL`,即你可以使用标准的SQL就能完成对HBase中数据的操作。这也就意味着使用Phoenix你可以无缝集成Spring Data JPA或 Mybatis等常用的持久层框架来操作HBase。
|
||||
|
||||
再一个就是Phoenix的性能表现,Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase scan,并编排并行执行以生成标准的JDBC结果集,同时Phoenix还拥有二级索引等Hbase不具备的特性,这使得Phoenix具有接近原生HBase API的性能表现。
|
||||
其次`Phoenix`的性能表现也非常优异,`Phoenix`查询引擎会将SQL查询转换为一个或多个HBase Scan,通过并行执行来生成标准的JDBC结果集。它通过直接使用HBase API以及协处理器和自定义过滤器,可以为小型数据查询提供毫秒级的性能,为千万行数据的查询提供秒级的性能。同时Phoenix还拥有二级索引等HBase不具备的特性,因为以上的优点,所以`Phoenix`成为了HBase最优秀的SQL中间层。
|
||||
|
||||
|
||||
|
||||
|
@ -27,51 +27,49 @@
|
||||
|
||||
### 2.1 Row Key (行键)
|
||||
|
||||
Row Key是用来检索记录的主键。访问HBase Table中的行,只有三种方式:
|
||||
`Row Key`是用来检索记录的主键。想要访问HBase Table中的数据,只有以下三种方式:
|
||||
|
||||
+ 通过单个Row Key访问
|
||||
+ 通过指定的`Row Key`进行访问;
|
||||
|
||||
+ 通过Row Key的range
|
||||
+ 通过Row Key的range进行访问,即访问指定范围内的行;
|
||||
|
||||
+ 全表扫描
|
||||
+ 进行全表扫描。
|
||||
|
||||
Row Key (行键)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在HBase内部,Row Key保存为字节数组。存储时,数据按照Row Key的字典序(byte order)排序存储。
|
||||
`Row Key`可以是任意字符串,存储时数据按照`Row Key`的字典序进行排序。这里需要注意以下两点:
|
||||
|
||||
需要注意以下两点:
|
||||
+ 因为字典序对Int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。如果你使用整型的字符串作为行键,那么为了保持整型的自然序,行键必须用0作左填充。
|
||||
|
||||
+ 字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整型的自然序,行键必须用0作左填充。
|
||||
|
||||
+ 行的一次读写是原子操作 (不论一次读写多少列)。
|
||||
+ 行的一次读写操作时原子性的 (不论一次读写多少列)。
|
||||
|
||||
|
||||
|
||||
### 2.2 Column Family(列族)
|
||||
|
||||
HBase表中的每个列(Column),都归属与某个列族。列族是表的schema的一部分(列不是),必须在使用表之前定义。列名都以列族作为前缀。例如`courses:history`,`courses:math`都属于`courses `这个列族。
|
||||
HBase表中的每个列,都归属于某个列族。列族是表的Schema的一部分,所以列族需要在创建表时进行定义。列族的所有列都以列族名作为前缀,例如`courses:history`,`courses:math`都属于`courses`这个列族。
|
||||
|
||||
|
||||
|
||||
### 2.3 Column Qualifier (列限定符)
|
||||
|
||||
列限定符被添加到列族中,以提供给定数据的索引。给定列族`content`,列限定符可能是`content:html`,另一个可能是`content:pdf`。虽然列族在创建表时是固定的,但列限定符是可变的,并且行与行之间可能有很大差异。
|
||||
列限定符,你可以理解为是具体的列名,例如`courses:history`,`courses:math`都属于`courses`这个列族,它们的列限定符分别是`history`和`math`。需要注意的是列限定符不是表Schema的一部分,你可以在插入数据的过程中动态创建列。
|
||||
|
||||
|
||||
|
||||
### 2.4 Column(列)
|
||||
|
||||
HBase 中的列由列族和列限定符组成,它们由`:`(冒号)字符分隔。
|
||||
HBase中的列由列族和列限定符组成,它们由`:`(冒号)进行分隔,即一个完整的列名应该表述为`列族名 :列限定符`。
|
||||
|
||||
|
||||
|
||||
### 2.5 Cell
|
||||
|
||||
Cell是行,列族和列限定符的组合,并包含值和时间戳。
|
||||
`Cell`是行,列族和列限定符的组合,并包含值和时间戳。你可以等价理解为关系型数据库中由指定行和指定列确定的一个单元格,但不同的是HBase中的一个单元格是由多个版本的数据组成的,每个版本的数据用时间戳进行区分。
|
||||
|
||||
|
||||
|
||||
### 2.6 Timestamp(时间戳)
|
||||
|
||||
HBase 中通过`row`和`columns`确定的为一个存储单元称为Cell。每个Cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由HBase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个Cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
|
||||
HBase 中通过`row key`和`column`确定的为一个存储单元称为`Cell`。每个`Cell`都保存着同一份数据的多个版本。版本通过时间戳来索引,时间戳的类型是 64位整型,时间戳可以由HBase在数据写入时自动赋值,也可以由客户显式指定。每个`Cell`中,不同版本的数据按照时间戳倒序排列,即最新的数据排在最前面。
|
||||
|
||||
|
||||
|
||||
@ -79,80 +77,82 @@ HBase 中通过`row`和`columns`确定的为一个存储单元称为Cell。每
|
||||
|
||||
### 2.1 Regions
|
||||
|
||||
HBase Table中的所有行都按照Row Key的字典序排列。HBase Tables 通过行键的范围(row key range)被水平切分成多个Region, 一个Region包含了在start key 和 end key之间的所有行。
|
||||
HBase Table中的所有行按照`Row Key`的字典序排列。HBase Tables 通过行键的范围(row key range)被水平切分成多个`Region`, 一个`Region`包含了在start key 和 end key之间的所有行。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBaseArchitecture-Blog-Fig2.png"/> </div>
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBaseArchitecture-Blog-Fig2.png"/> </div>
|
||||
|
||||
每个表一开始只有一个Region,随着数据不断插入表,Region不断增大,当增大到一个阀值的时候,Region就会等分会两个新的Region。当Table中的行不断增多,就会有越来越多的Region。
|
||||
每个表一开始只有一个`Region`,随着数据不断增加,`Region`会不断增大,当增大到一个阀值的时候,`Region`就会等分为两个新的`Region`。当Table中的行不断增多,就会有越来越多的`Region`。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-region-splite.png"/> </div>
|
||||
|
||||
Region是HBase中**分布式存储和负载均衡的最小单元**。最小单元就表示不同的Region可以分布在不同的Region Server上。但一个Region是不会拆分到多个server上的。
|
||||
`Region`是HBase中**分布式存储和负载均衡的最小单元**。这意味着不同的`Region`可以分布在不同的`Region Server`上。但一个`Region`是不会拆分到多个Server上的。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-region-dis.png"/> </div>
|
||||
|
||||
### 2.2 Region Server
|
||||
|
||||
Region Server在HDFS DataNode上运行。
|
||||
`Region Server`运行在HDFS的DataNode上。它具有以下组件:
|
||||
|
||||
- **WAL(Write Ahead Log,预写日志)**:用于存储尚未进持久化存储的数据记录,以便在发生故障时进行恢复。
|
||||
- **BlockCache**:读缓存。它将频繁读取的数据存储在内存中,如果存储不足,它将按照`最近最少使用原则`清除多余的数据。
|
||||
- **MemStore**:写缓存。它存储尚未写入磁盘的新数据,并会在数据写入磁盘之前对其进行排序。每个Region上的每个列族都有一个MemStore。
|
||||
- **HFile** :将行数据按照Key\Values的形式存储在文件系统上。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-Region-Server.png"/> </div>
|
||||
|
||||
|
||||
|
||||
Region Server存取一个子表时,会创建一个Region对象,然后对表的每个列族创建一个`Store`实例,每个`Store`会有 0 个或多个`StoreFile`与之对应,每个`StoreFile`则对应一个`HFile`,HFile 就是实际存储在HDFS上的文件。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-hadoop.png"/> </div>
|
||||
|
||||
Region Server存取一个子表时,会创建一个 Region 对象,然后对表的每个列族 (Column Family) 创建一个 Store 实例,每个 Store 都会有 0 个或多个 StoreFile 与之对应,每个 StoreFile 都会对应一个 HFile,HFile 就是实际的存储文件。因此,一个 Region 有多少个列族就有多少个 Store。
|
||||
|
||||
Region Server还具有以下组件:
|
||||
|
||||
+ WAL:Write Ahead Log(预写日志)是分布式文件系统上的文件。 WAL用于存储尚未进持久化存储的新数据,以便在发生故障时进行恢复。
|
||||
+ BlockCache:是读缓存。它将频繁读取的数据存储在内存中。如果存储不足,它将按照`最近最少使用原则`清除多余的数据。
|
||||
+ MemStore:是写缓存。它存储尚未写入磁盘的新数据,并会在数据写入磁盘之前对其进行排序。每个Region上的每个列族都有一个MemStore。
|
||||
+ HFile将行数据按照KeyValues的形式存储在文件系统上。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-Region-Server.png"/> </div>
|
||||
|
||||
## 三、Hbase系统架构
|
||||
|
||||
### 3.1 系统架构
|
||||
|
||||
HBase系统遵循Master/Salve架构,由三种不同类型的组件组成。
|
||||
HBase系统遵循Master/Salve架构,由三种不同类型的组件组成:
|
||||
|
||||
**Zookeeper**
|
||||
|
||||
1. 保证任何时候,集群中只有一个Master
|
||||
1. 保证任何时候,集群中只有一个Master;
|
||||
|
||||
2. 存贮所有Region的寻址入口
|
||||
2. 存贮所有Region的寻址入口;
|
||||
|
||||
3. 实时监控Region Server的状态,将Region server的上线和下线信息实时通知给Master
|
||||
3. 实时监控Region Server的状态,将Region Server的上线和下线信息实时通知给Master;
|
||||
|
||||
4. 存储HBase的schema,包括有哪些Table,每个Table有哪些Column Family
|
||||
4. 存储HBase的Schema,包括有哪些Table,每个Table有哪些Column Family等信息。
|
||||
|
||||
**Master**
|
||||
|
||||
1. 为Region server分配Region
|
||||
1. 为Region Server分配Region ;
|
||||
|
||||
2. 负责Region server的负载均衡
|
||||
2. 负责Region Server的负载均衡 ;
|
||||
|
||||
3. 发现失效的Region server并重新分配其上的Region
|
||||
3. 发现失效的Region Server并重新分配其上的Region;
|
||||
|
||||
4. GFS上的垃圾文件回收
|
||||
4. GFS上的垃圾文件回收;
|
||||
|
||||
5. 处理schema更新请求
|
||||
5. 处理Schema的更新请求。
|
||||
|
||||
**Region Server**
|
||||
|
||||
1. Region server维护Master分配给它的Region ,处理发到Region上的IO请求
|
||||
1. Region Server负责维护Master分配给它的Region ,并处理发送到Region上的IO请求;
|
||||
|
||||
2. Region server负责切分在运行过程中变得过大的Region
|
||||
2. Region Server负责切分在运行过程中变得过大的Region。
|
||||
|
||||
<div align="center"> <img width="600px" src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBaseArchitecture-Blog-Fig1.png"/> </div>
|
||||
|
||||
### 3.2 组件间的协作
|
||||
|
||||
HBase使用ZooKeeper作为分布式协调服务来维护集群中的服务器状态。 Zookeeper维护可用服务列表,并提供服务故障通知。
|
||||
HBase使用ZooKeeper作为分布式协调服务来维护集群中的服务器状态。 Zookeeper负责维护可用服务列表,并提供服务故障通知等服务:
|
||||
|
||||
+ 每个Region Server都会在ZooKeeper上创建一个临时节点,HMaster通过Zookeeper的Watcher机制监控这些节点以发现可用的Region Server和故障的Region Server;
|
||||
+ 每个Region Server都会在ZooKeeper上创建一个临时节点,Master通过Zookeeper的Watcher机制对节点进行监控,从而可以发现新加入的Region Server或故障退出的Region Server;
|
||||
|
||||
+ Masters会竞争创建临时节点, Zookeeper确定第一个并使用它来确保只有一个主服务器处于活动状态。主Master向Zookeeper发送心跳,备用HMaster监听主HMaster故障的通知,在主HMaster发生故障的时候取而代之。
|
||||
+ 所有Masters会竞争性地在Zookeeper上创建同一个临时节点,由于Zookeeper只能有一个同名节点,所以必然只有一个Master能够创建成功,此时该Master就是主Master,主Master会定期向Zookeeper发送心跳。备用Masters则通过Watcher机制对主HMaster所在节点进行监听;
|
||||
|
||||
+ 如果Region Server或主HMaster未能发送心跳,则会话过期并删除相应的临时节点。这会触发定义在该节点上的Watcher事件,使得Region Server或备用Region Server得到通知。
|
||||
+ 如果主Master未能定时发送心跳,则其持有的Zookeeper会话会过期,相应的临时节点也会被删除,这会触发定义在该节点上的Watcher事件,使得备用的Master Servers得到通知。所有备用的Master Servers在接到通知后,会再次去竞争性地创建临时节点,完成主Master的选举。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBaseArchitecture-Blog-Fig5.png"/> </div>
|
||||
|
||||
@ -162,19 +162,19 @@ HBase系统遵循Master/Salve架构,由三种不同类型的组件组成。
|
||||
|
||||
### 4.1 写入数据的流程
|
||||
|
||||
1. client向Region server提交写请求
|
||||
1. Client向Region Server提交写请求;
|
||||
|
||||
2. Region server找到目标Region
|
||||
2. Region Server找到目标Region;
|
||||
|
||||
3. Region检查数据是否与schema一致
|
||||
3. Region检查数据是否与Schema一致;
|
||||
|
||||
4. 如果客户端没有指定版本,则获取当前系统时间作为数据版本
|
||||
4. 如果客户端没有指定版本,则获取当前系统时间作为数据版本;
|
||||
|
||||
5. 将更新写入WAL log
|
||||
5. 将更新写入WAL Log;
|
||||
|
||||
6. 将更新写入Memstore
|
||||
6. 将更新写入Memstore;
|
||||
|
||||
7. 判断Memstore存储是否已满,如果存储已满则需要flush为Store Hfile文件
|
||||
7. 判断Memstore存储是否已满,如果存储已满则需要flush为Store Hfile文件。
|
||||
|
||||
> 更为详细写入流程可以参考:[HBase - 数据写入流程解析](http://hbasefly.com/2016/03/23/hbase_writer/)
|
||||
|
||||
@ -182,17 +182,17 @@ HBase系统遵循Master/Salve架构,由三种不同类型的组件组成。
|
||||
|
||||
### 4.2 读取数据的流程
|
||||
|
||||
以下是客户端首次读写Hbase 的流程:
|
||||
以下是客户端首次读写HBase上数据的流程:
|
||||
|
||||
1. 客户端从Zookeeper获取 META 表所在的Region Server。
|
||||
1. 客户端从Zookeeper获取`META`表所在的Region Server;
|
||||
|
||||
2. 客户端访问 META 表所在的Region Server,查询META 表获取它想访问的行键(Row Key)所在的Region Server。客户端将缓存这些信息以及META表的位置。
|
||||
2. 客户端访问`META`表所在的Region Server,从`META`表中查询到访问行键所在的Region Server,之后客户端将缓存这些信息以及`META`表的位置;
|
||||
|
||||
3. 客户端端将从相应的Region Server获取行数据。
|
||||
3. 客户端从行键所在的Region Server上获取数据。
|
||||
|
||||
如果再次读取,客户端将使用缓存来获取META 的位置及之前的行键。这样时间久了,客户端不需要查询META表,除非Region移动所导致的缓存失效,这样的话,则将会重新查询更新缓存。
|
||||
如果再次读取,客户端将从缓存中获取行键所在的Region Server。这样客户端就不需要再次查询`META`表,除非Region移动导致缓存失效,这样的话,则将会重新查询并更新缓存。
|
||||
|
||||
注:META 表是HBase中一张特殊的表,它保存了HBase中所有数据表的Region位置信息,ZooKeeper存储着META 表的位置。
|
||||
注:`META`表是HBase中一张特殊的表,它保存了所有Region的位置信息,META表自己的位置信息则存储在ZooKeeper上。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/HBaseArchitecture-Blog-Fig7.png"/> </div>
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
## 一、HBase过滤器简介
|
||||
|
||||
Hbase提供了种类丰富的过滤器(filter)来提高数据处理的效率,用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效,即谓词下推(predicate push down)。这样可以保证过滤掉的数据不会被传送到客户端,减轻网络传输和客户端处理的压力。
|
||||
Hbase提供了种类丰富的过滤器(filter)来提高数据处理的效率,用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效,即谓词下推(predicate push down)。这样可以保证过滤掉的数据不会被传送到客户端,从而减轻网络传输和客户端处理的压力。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-fliter.png"/> </div>
|
||||
|
||||
@ -110,30 +110,27 @@ public enum CompareOperator {
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:在1.x 版本的HBase中比较运算符定义在`CompareFilter.CompareOp`枚举类中,但在2.0之后这个类就被标识为 @deprecated ,并会在3.0移除。
|
||||
> 注意:在 1.x 版本的HBase中,比较运算符定义在`CompareFilter.CompareOp`枚举类中,但在2.0之后这个类就被标识为 @deprecated ,并会在3.0移除。所以2.0之后版本的HBase需要使用 `CompareOperator`这个枚举类。
|
||||
>
|
||||
> 所以1.x 版本的比较运算符需要使用`CompareFilter.CompareOp`枚举类, 2.0 版本HBase 则需要使用 `CompareOperator`枚举类。
|
||||
|
||||
### 3.2 比较器
|
||||
|
||||
所有比较器均继承自`ByteArrayComparable`抽象类
|
||||
所有比较器均继承自`ByteArrayComparable`抽象类,常用的有以下几种:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-bytearraycomparable.png"/> </div>
|
||||
|
||||
常用的有以下几种:
|
||||
- **BinaryComparator** : 使用`Bytes.compareTo(byte [],byte [])`按字典序比较指定的字节数组。
|
||||
- **BinaryPrefixComparator** : 按字典序与指定的字节数组进行比较,但只比较到这个字节数组的长度。
|
||||
- **RegexStringComparator** : 使用给定的正则表达式与指定的字节数组进行比较。仅支持`EQUAL`和`NOT_EQUAL`操作。
|
||||
- **SubStringComparator** : 测试给定的子字符串是否出现在指定的字节数组中,比较不区分大小写。仅支持`EQUAL`和`NOT_EQUAL`操作。
|
||||
- **NullComparator** :判断给定的值是否为空。
|
||||
- **BitComparator** :按位进行比较。
|
||||
|
||||
- BinaryComparator : 使用`Bytes.compareTo(byte [],byte [])`按字典序比较指定的字节数组
|
||||
- BinaryPrefixComparator : 按字典序与指定的字节数组进行比较,但只比较到这个字节数组的长度。
|
||||
- RegexStringComparator : 使用给定的正则表达式与指定的字节数组进行比较。仅支持 EQUAL 和 NOT_EQUAL 操作
|
||||
- SubStringComparator : 测试给定的子字符串是否出现在指定的字节数组中,比较不区分大小写。仅支持 EQUAL 和NOT_EQUAL 操作
|
||||
- NullComparator :判断给定的值是否为空
|
||||
- BitComparator :按位进行比较
|
||||
|
||||
BinaryPrefixComparator 和 BinaryComparator的区别不是很好表述,这里举例说明一下:
|
||||
`BinaryPrefixComparator` 和 `BinaryComparator`的区别不是很好理解,这里举例说明一下:
|
||||
|
||||
在进行`EQUAL`的比较时,如果比较器传入的是`abcd`的字节数组,但是待比较数据是`abcdefgh`:
|
||||
|
||||
+ 如果使用的是`BinaryPrefixComparator `比较器,则比较以`abcd`字节数组的长度为准,即`efgh`不会参与比较,这时候认为`abcd`与`abcdefgh` 是满足`EQUAL`条件的;
|
||||
+ 如果使用的是`BinaryPrefixComparator`比较器,则比较以`abcd`字节数组的长度为准,即`efgh`不会参与比较,这时候认为`abcd`与`abcdefgh` 是满足`EQUAL`条件的;
|
||||
+ 如果使用的是`BinaryComparator`比较器,则认为其是不相等的。
|
||||
|
||||
### 3.3 比较过滤器种类
|
||||
@ -142,11 +139,11 @@ BinaryPrefixComparator 和 BinaryComparator的区别不是很好表述,这里
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-compareFilter.png"/> </div>
|
||||
|
||||
+ RowFilter :基于行键来过滤数据;
|
||||
+ FamilyFilterr :基于列族来过滤数据;
|
||||
+ QualifierFilterr :基于列限定符(列名)来过滤数据;
|
||||
+ ValueFilterr :基于单元格(cell) 的值来过滤数据;
|
||||
+ DependentColumnFilter :指定一个参考列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选 。
|
||||
+ **RowFilter** :基于行键来过滤数据;
|
||||
+ **FamilyFilterr** :基于列族来过滤数据;
|
||||
+ **QualifierFilterr** :基于列限定符(列名)来过滤数据;
|
||||
+ **ValueFilterr** :基于单元格(cell) 的值来过滤数据;
|
||||
+ **DependentColumnFilter** :指定一个参考列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选 。
|
||||
|
||||
前四种过滤器的使用方法相同,均只要传递比较运算符和运算器实例即可构建,然后通过`setFilter`方法传递给`scan`:
|
||||
|
||||
@ -156,11 +153,11 @@ BinaryPrefixComparator 和 BinaryComparator的区别不是很好表述,这里
|
||||
scan.setFilter(filter);
|
||||
```
|
||||
|
||||
DependentColumnFilter 的使用稍微复杂一点,这里单独做如下说明。
|
||||
`DependentColumnFilter`的使用稍微复杂一点,这里单独做下说明。
|
||||
|
||||
### 3.4 DependentColumnFilter
|
||||
|
||||
可以把DependentColumnFilter理解为**一个valueFilter和一个时间戳过滤器的组合**。DependentColumnFilter 有三个带参构造器,这里选择一个参数最全的进行说明:
|
||||
可以把`DependentColumnFilter`理解为**一个valueFilter和一个时间戳过滤器的组合**。`DependentColumnFilter`有三个带参构造器,这里选择一个参数最全的进行说明:
|
||||
|
||||
```java
|
||||
DependentColumnFilter(final byte [] family, final byte[] qualifier,
|
||||
@ -168,11 +165,11 @@ DependentColumnFilter(final byte [] family, final byte[] qualifier,
|
||||
final ByteArrayComparable valueComparator)
|
||||
```
|
||||
|
||||
+ family :列族
|
||||
+ qualifier :列限定符(列名)
|
||||
+ dropDependentColumn :决定参考列是否被包含在返回结果内,为true时表示参考列被返回,为false时表示被丢弃
|
||||
+ op :比较运算符
|
||||
+ valueComparator :比较器
|
||||
+ **family** :列族
|
||||
+ **qualifier** :列限定符(列名)
|
||||
+ **dropDependentColumn** :决定参考列是否被包含在返回结果内,为true时表示参考列被返回,为false时表示被丢弃
|
||||
+ **op** :比较运算符
|
||||
+ **valueComparator** :比较器
|
||||
|
||||
这里举例进行说明:
|
||||
|
||||
@ -189,7 +186,7 @@ DependentColumnFilter dependentColumnFilter = new DependentColumnFilter(
|
||||
|
||||
+ 其次再用参考数据集中所有数据的时间戳去检索其他列,获得时间戳相同的其他列的数据作为`结果数据集`,这一步等同于时间戳过滤器;
|
||||
|
||||
+ 最后如果`dropDependentColumn `为true,则返回`参考数据集`+`结果数据集`,若为false,则抛弃参考数据集,只返回结果数据集。
|
||||
+ 最后如果`dropDependentColumn`为true,则返回`参考数据集`+`结果数据集`,若为false,则抛弃参考数据集,只返回`结果数据集`。
|
||||
|
||||
|
||||
|
||||
@ -201,8 +198,8 @@ DependentColumnFilter dependentColumnFilter = new DependentColumnFilter(
|
||||
|
||||
基于某列(参考列)的值决定某行数据是否被过滤。其实例有以下方法:
|
||||
|
||||
+ setFilterIfMissing(boolean filterIfMissing) :默认值为false,即如果该行数据不包含参考列,其依然被包含在最后的结果中;设置为true时,则不包含;
|
||||
+ setLatestVersionOnly(boolean latestVersionOnly) :默认为true,即只检索参考列的最新版本数据;设置为false,则检索所有版本数据。
|
||||
+ **setFilterIfMissing(boolean filterIfMissing)** :默认值为false,即如果该行数据不包含参考列,其依然被包含在最后的结果中;设置为true时,则不包含;
|
||||
+ **setLatestVersionOnly(boolean latestVersionOnly)** :默认为true,即只检索参考列的最新版本数据;设置为false,则检索所有版本数据。
|
||||
|
||||
```shell
|
||||
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
|
||||
@ -311,7 +308,7 @@ scan.setFilter(timestampsFilter);
|
||||
|
||||
### 4.7 首次行键过滤器 (FirstKeyOnlyFilter)
|
||||
|
||||
FirstKeyOnlyFilter只扫描每行的第一列,扫描完第一列后就结束对当前行的扫描,并跳转到下一行。相比于全表扫描,其性能更好,通常用于行数统计的场景,因为如果某一行存在,则行中必然至少有一列。
|
||||
`FirstKeyOnlyFilter`只扫描每行的第一列,扫描完第一列后就结束对当前行的扫描,并跳转到下一行。相比于全表扫描,其性能更好,通常用于行数统计的场景,因为如果某一行存在,则行中必然至少有一列。
|
||||
|
||||
```java
|
||||
FirstKeyOnlyFilter firstKeyOnlyFilter = new FirstKeyOnlyFilter();
|
||||
@ -324,7 +321,7 @@ scan.set(firstKeyOnlyFilter);
|
||||
|
||||
### 5.1 SkipFilter过滤器
|
||||
|
||||
SkipFilter包装一个过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,则拓展过滤整行数据。下面是一个使用示例:
|
||||
`SkipFilter`包装一个过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,则拓展过滤整行数据。下面是一个使用示例:
|
||||
|
||||
```java
|
||||
// 定义ValueFilter过滤器
|
||||
@ -338,7 +335,7 @@ Filter filter2 = new SkipFilter(filter1);
|
||||
|
||||
### 5.2 WhileMatchFilter过滤器
|
||||
|
||||
WhileMatchFilter包装一个过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,WhileMatchFilter则结束本次扫描,返回已经扫描到的结果。下面是其使用示例:
|
||||
`WhileMatchFilter`包装一个过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,`WhileMatchFilter`则结束本次扫描,返回已经扫描到的结果。下面是其使用示例:
|
||||
|
||||
```java
|
||||
Filter filter1 = new RowFilter(CompareOperator.NOT_EQUAL,
|
||||
@ -390,7 +387,7 @@ rowKey3/student:name/1555035007037/Put/vlen=8/seqid=0
|
||||
|
||||
## 六、FilterList
|
||||
|
||||
以上都是讲解单个过滤器的作用,当需要多个过滤器共同作用于一次查询的时候,就需要使用FilterList。FilterList支持通过构造器或者`addFilter`方法传入多个过滤器。
|
||||
以上都是讲解单个过滤器的作用,当需要多个过滤器共同作用于一次查询的时候,就需要使用`FilterList`。`FilterList`支持通过构造器或者`addFilter`方法传入多个过滤器。
|
||||
|
||||
```java
|
||||
// 构造器传入
|
||||
@ -405,8 +402,8 @@ public FilterList(final Filter... filters)
|
||||
|
||||
多个过滤器组合的结果由`operator`参数定义 ,其可选参数定义在`Operator`枚举类中。只有`MUST_PASS_ALL`和`MUST_PASS_ONE`两个可选的值:
|
||||
|
||||
+ MUST_PASS_ALL :相当于AND,必须所有的过滤器都通过才认为通过;
|
||||
+ MUST_PASS_ONE :相当于OR,只有要一个过滤器通过则认为通过。
|
||||
+ **MUST_PASS_ALL** :相当于AND,必须所有的过滤器都通过才认为通过;
|
||||
+ **MUST_PASS_ONE** :相当于OR,只有要一个过滤器通过则认为通过。
|
||||
|
||||
```java
|
||||
@InterfaceAudience.Public
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Hbase基本环境搭建
|
||||
# HBase基本环境搭建
|
||||
|
||||
<nav>
|
||||
<a href="#一安装前置条件说明">一、安装前置条件说明</a><br/>
|
||||
@ -14,12 +14,12 @@ HBase 需要依赖JDK环境,同时HBase 2.0+ 以上版本不再支持JDK 1.7
|
||||
|
||||
> [Linux环境下JDK安装](https://github.com/heibaiying/BigData-Notes/blob/master/notes/installation/Linux下JDK安装.md)
|
||||
|
||||
### 1.2 standalone 模式和伪集群模式的区别
|
||||
### 1.2 Standalone模式和伪集群模式的区别
|
||||
|
||||
+ 在standalone 模式下,所有守护进程都运行在一个 jvm 进程/实例中;
|
||||
+ 在分布模式下,HBase仍然在单个主机上运行,但是每个 HBase 守护进程(HMaster,HRegionServer 和 ZooKeeper)作为一个单独的进程运行。
|
||||
+ 在`Standalone`模式下,所有守护进程都运行在一个`jvm`进程/实例中;
|
||||
+ 在伪分布模式下,HBase仍然在单个主机上运行,但是每个守护进程(HMaster,HRegionServer 和 ZooKeeper)则分别作为一个单独的进程运行。
|
||||
|
||||
**说明:两种模式任选其一进行部署即可,对于开发环境来说是没有太大区别的。**
|
||||
**说明:两种模式任选其一进行部署即可,对于开发测试来说区别不大。**
|
||||
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ HBase 需要依赖JDK环境,同时HBase 2.0+ 以上版本不再支持JDK 1.7
|
||||
|
||||
### 2.1 下载并解压
|
||||
|
||||
从[官方网站](https://hbase.apache.org/downloads.html)下载所需要版本的二进制安装包,并进行解压
|
||||
从[官方网站](https://hbase.apache.org/downloads.html)下载所需要版本的二进制安装包,并进行解压:
|
||||
|
||||
```shell
|
||||
# tar -zxvf hbase-2.1.4-bin.tar.gz
|
||||
@ -80,15 +80,15 @@ export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
</configuration>
|
||||
```
|
||||
|
||||
`hbase.rootdir`: 配置hbase数据的存储路径
|
||||
`hbase.rootdir`: 配置hbase数据的存储路径;
|
||||
|
||||
`hbase.zookeeper.property.dataDir`: 配置zookeeper数据的存储路径
|
||||
`hbase.zookeeper.property.dataDir`: 配置zookeeper数据的存储路径;
|
||||
|
||||
`hbase.unsafe.stream.capability.enforce`: 使用本地文件系统存储,不使用HDFS的情况下需要禁用此配置,设置为false。
|
||||
|
||||
### 2.4 启动HBase
|
||||
|
||||
由于已经将HBase的bin目录配置到环境变量,直接使用以下命令启动
|
||||
由于已经将HBase的bin目录配置到环境变量,直接使用以下命令启动:
|
||||
|
||||
```shell
|
||||
# start-hbase.sh
|
||||
@ -96,7 +96,7 @@ export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
|
||||
### 2.5 验证启动是否成功
|
||||
|
||||
验证方式一:使用jps 查看HMaster进程是否启动
|
||||
验证方式一 :使用`jps`命令查看HMaster进程是否启动。
|
||||
|
||||
```
|
||||
[root@hadoop001 hbase-2.1.4]# jps
|
||||
@ -104,7 +104,7 @@ export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
15500 HMaster
|
||||
```
|
||||
|
||||
验证方式二:访问HBaseWeb UI 页面,默认端口为`16010`
|
||||
验证方式二 :访问HBaseWeb UI 页面,默认端口为`16010` 。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-web-ui.png"/> </div>
|
||||
|
||||
@ -114,15 +114,13 @@ export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
|
||||
### 3.1 Hadoop单机伪集群安装
|
||||
|
||||
这里我们采用HDFS作为Hbase的存储方案,需要预先安装Hadoop。(如果你没有安装Hadoop,依然可以按照Standalone 模式,采用本地文件系统作为存储方案)
|
||||
这里我们采用HDFS作为HBase的存储方案,需要预先安装Hadoop。Hadoop的安装方式单独整理至:
|
||||
|
||||
> [Hadoop单机伪集群搭建](https://github.com/heibaiying/BigData-Notes/blob/master/notes/installation/Hadoop单机版本环境搭建.md)
|
||||
|
||||
### 3.2 Hbase版本选择
|
||||
|
||||
HBase的版本必须要与Hadoop的版本兼容,不然会发生各种Jar包冲突。
|
||||
|
||||
由于我们Hadoop采用的版本为`hadoop-2.6.0-cdh5.15.2`,所以这里保持CDH版本一致,我们选择的HBase版本为`hbase-1.2.0-cdh5.15.2` ,HBase 1.2 的安装需要依赖JDK 1.7+ 。所有软件版本如下:
|
||||
HBase的版本必须要与Hadoop的版本兼容,不然会出现各种Jar包冲突。这里我Hadoop安装的版本为`hadoop-2.6.0-cdh5.15.2`,为保持版本一致,选择的HBase版本为`hbase-1.2.0-cdh5.15.2` 。所有软件版本如下:
|
||||
|
||||
+ Hadoop 版本: hadoop-2.6.0-cdh5.15.2
|
||||
|
||||
@ -134,14 +132,12 @@ HBase的版本必须要与Hadoop的版本兼容,不然会发生各种Jar包冲
|
||||
|
||||
### 3.3 软件下载解压
|
||||
|
||||
下载地址:http://archive.cloudera.com/cdh5/cdh/5/ 下载后进行解压:
|
||||
下载后进行解压,下载地址:http://archive.cloudera.com/cdh5/cdh/5/
|
||||
|
||||
```shell
|
||||
# tar -zxvf hbase-1.2.0-cdh5.15.2.tar.gz
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.4 配置环境变量
|
||||
```shell
|
||||
# vim /etc/profile
|
||||
@ -172,7 +168,7 @@ export PATH=$HBASE_HOME/bin:$PATH
|
||||
export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
```
|
||||
|
||||
2.修改安装目录下的`conf/hbase-site.xml`,增加如下配置(hadoop001为我虚拟机的主机名):
|
||||
2.修改安装目录下的`conf/hbase-site.xml`,增加如下配置(hadoop001为主机名):
|
||||
|
||||
```xml
|
||||
<configuration>
|
||||
@ -194,7 +190,7 @@ export JAVA_HOME=/usr/java/jdk1.8.0_201
|
||||
</configuration>
|
||||
```
|
||||
|
||||
3.修改安装目录下的`conf/regionservers`,指定 region servers的地址,修改后其内容如下(hadoop001为我虚拟机的主机名):
|
||||
3.修改安装目录下的`conf/regionservers`,指定region servers的地址,修改后其内容如下:
|
||||
|
||||
```shell
|
||||
hadoop001
|
||||
@ -212,7 +208,7 @@ hadoop001
|
||||
|
||||
### 3.7 验证启动是否成功
|
||||
|
||||
验证方式一:jps查看进程,其中HMaster,HRegionServer,HQuorumPeer三个进程是HBase的进程(其中HQuorumPeer是Hbase内置的Zookeeper的进程),其余的为HDFS和YARN的进程。
|
||||
验证方式一 :使用`jps`命令查看进程。其中`HMaster`,`HRegionServer`是HBase的进程,`HQuorumPeer`是HBase内置的Zookeeper的进程,其余的为HDFS和YARN的进程。
|
||||
|
||||
```shell
|
||||
[root@hadoop001 conf]# jps
|
||||
@ -229,6 +225,6 @@ hadoop001
|
||||
21933 HMaster
|
||||
```
|
||||
|
||||
验证方式二:访问HBase Web UI 界面,需要注意的是1.2 版本的HBase的访问端口为`60010`
|
||||
验证方式二 :访问HBase Web UI 界面,需要注意的是1.2 版本的HBase的访问端口为`60010`
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/hbase-60010.png"/> </div>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 80 KiB |
Loading…
x
Reference in New Issue
Block a user