6.1 谁发明了 HBase,为什么

在本次讲座中,我们将讨论 Hbase 这样一个非常棒的工具,它最近非常受欢迎:例如 Facebook 将其用作其消息系统的基础,这已经说明了很多。

讲座将讨论Big Table的概念及其自由实现、工作特点以及与经典关系数据库(如MySQL和Oracle)以及键值存储(如Redis、Aerospike和memcached)的区别。像往常一样,让我们​​从问题的历史开始。与许多其他大数据项目一样,Hbase 诞生于谷歌开发的概念。Hbase 背后的原理在Bigtable:结构化数据的分布式存储系统一文中进行了描述。

正如我们在之前的讲座中所讨论的,普通文件非常适合使用 MapReduce 范例的批处理数据。另一方面,存储在文件中的信息更新起来很不方便;文件也被剥夺了随机访问的可能性。为了快速方便的随机访问工作,有一类nosql系统如key-value存储,如Aerospike、Redis、Couchbase、Memcached。然而,批处理在这些系统中通常非常不方便。Hbase是一种将批处理的便利性与更新和随机访问的便利性结合起来的尝试。

6.2 数据模型

HBase 是一个分布式的、面向列的、多版本的键值数据库。

  • 数据被组织成由 Hbase 中称为 RowKey 的主键索引的表。
  • 对于每个 RowKey 键,可以存储一组无限的属性(或列)。
  • 列被组织成称为列族的列组。通常,具有相同用途和存储模式的列被组合成一个列族。
  • 对于每个属性,可以存储几个不同的版本。不同的版本有不同的时间戳。

记录以 RowKey 排序的顺序物理存储。在这种情况下,不同Column Family对应的数据是分开存储的,这样就可以在需要的时候只从需要的Column Family中读取数据。

当某个属性被删除时,并不会立即在物理上被删除,而只是被标记上一个特殊的墓碑标志。数据的物理删除将在稍后执行 Major Compaction 操作时发生。

属于同一列组且对应于同一键的属性在物理上存储为排序列表。每个键的任何属性都可以不存在或存在,如果不存在该属性,这不会导致存储空值的开销。

列表和列组名称固定,布局清晰。在列组级别,设置生存时间(TTL)和最大存储版本数等参数。如果特定版本的时间戳与当前时间之间的差异大于 TTL,则该条目被标记为删除。如果某个属性的版本数超过最大版本数,则该记录也被标记为删除。

Hbase数据模型可以记为key-value匹配:

<table, RowKey, Column Family, Column, timestamp> -> Value

6.3 支持的操作

hbase 中支持的操作列表非常简单。支持 4 种主要操作:

  • Put:向 hbase 添加一个新条目。该条目的时间戳可以手动设置,否则会自动设置为当前时间。
  • Get:获取特定 RowKey 的数据。您可以指定我们将从中获取数据的列族以及我们要读取的版本数。
  • 扫描:一条条读取记录。您可以指定我们开始读取的记录、要读取的记录、要读取的记录数、将从中执行读取的列族以及每条记录的最大版本数。
  • Delete : 将特定版本标记为删除。不会有物理删除,它会被推迟到下一次 Major Compaction(见下文)。

6.4 架构

HBase是一个分布式数据库,可以运行在几十台甚至上百台物理服务器上,即使其中一部分出现故障,也能保证不间断运行。因此,HBase的架构相对于经典的关系型数据库来说是相当复杂的。

HBase 使用两个主要进程来工作:

1. 区域服务器——服务于一个或多个区域。区域是与特定范围的连续 RowKeys 相对应的一系列记录。每个区域包含:

  • Persistent Storage是 HBase 中主要的数据存储。数据以特殊的 HFile 格式物理存储在 HDFS 上。HFile 中的数据按 RowKey 排序顺序存储。一对(区域、列族)对应至少一个 HFIle。
  • MemStore - 写入缓冲区。由于数据按排序顺序存储在 HFile d 中,因此每条记录更新 HFile 的成本非常高。相反,在写入时,数据会进入一个特殊的 MemStore 内存区域,并在那里累积一段时间。当 MemStore 被填充到某个临界值时,数据被写入一个新的 HFile。
  • BlockCache - 读取缓存。允许您在频繁读取的数据上显着节省时间。
  • 预写日志 (WAL)。由于数据写入到 memstore 中,因此存在因崩溃导致数据丢失的风险。为了防止这种情况发生,在实际执行操作之前的所有操作都属于一个特殊的日志文件。这允许您在发生任何故障后恢复数据。

2. Master Server ——HBase集群中的主服务器。Master 管理区域在 Region Server 之间的分布,维护区域注册,管理定期任务的启动,以及做其他有用的工作。

为了协调服务之间的操作,HBase 使用 Apache ZooKeeper,这是一种旨在管理配置和同步服务的特殊服务。

当region的数据量增加到一定大小时,Hbase开始split,将region一分为二的操作。为了避免region的不断划分,可以预先设置region的边界,增加region的最大值尺寸。

由于一个区域的数据可以存储在多个 HFile 中,Hbase 会定期将它们合并在一起以加快工作速度。这种操作在 Hbase 中称为压缩。压实有两种类型:

  • 轻微压实。自动启动,在后台运行。与其他 Hbase 操作相比具有较低的优先级。
  • 主要压实。它是手动启动的,或者是在某些触发器发生时启动的(例如,通过计时器)。它具有高优先级,可以显着降低集群速度。Major Compaction 最好在集群负载较小的时候进行。Major Compaction 还会物理删除以前标记为墓碑的数据。

6.5 使用 HBase 的方法

HBase 外壳

开始使用 Hbase 的最简单方法是使用 hbase shell 实用程序。它在任何 hbase 集群节点上安装 hbase 后立即可用。

Hbase shell 是一个 jruby 控制台,内置支持所有基本的 Hbase 操作。下面是创建一个包含两个列族的 users 表,对其进行一些操作,并在 hbase shell 中删除该表的示例:

create 'users', {NAME => 'user_profile', VERSIONS => 5}, {NAME => 'user_posts', VERSIONS => 1231231231} 
put 'users', 'id1', 'user_profile:name', 'alexander' 
put 'users', 'id1', 'user_profile:second_name', 'alexander' 
get 'users', 'id1' 
put 'users', 'id1', 'user_profile:second_name', 'kuznetsov' 
get 'users', 'id1' 
get 'users', 'id1', {COLUMN => 'user_profile:second_name', VERSIONS => 5} 
put 'users', 'id2', 'user_profile:name', 'vasiliy' 
put 'users', 'id2', 'user_profile:second_name', 'ivanov' 
scan 'users', {COLUMN => 'user_profile:second_name', VERSIONS => 5} 
delete 'users', 'id1', 'user_profile:second_name' 
get 'users', 'id1' 
disable 'users' 
drop 'users'

本机 API

和其他大部分hadoop相关的项目一样,hbase是用java实现的,所以原生的api是用Java实现的。本机 API 在官方网站上有很好的记录。这是使用从那里获取的 Hbase API 的示例:

import java.io.IOException;

import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

public class MyLittleHBaseClient {
  public static void main(String[] args) throws IOException {
	Configuration config = HBaseConfiguration.create();
	Connection connection = ConnectionFactory.createConnection(config);
	try {
  	Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable"));
  	try {
    	Put p = new Put(Bytes.toBytes("myLittleRow"));
    	p.add(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"),
    	Bytes.toBytes("Some Value"));
    	table.put(p);

    	Get g = new Get(Bytes.toBytes("myLittleRow"));
    	Result r = table.get(g);
    	byte [] value = r.getValue(Bytes.toBytes("myLittleFamily"),
      	Bytes.toBytes("someQualifier"));

    	String valueStr = Bytes.toString(value);
    	System.out.println("GET: " + valueStr);

    	Scan s = new Scan();
    	s.addColumn(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"));
    	ResultScanner scanner = table.getScanner(s);
    	try {
       	for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
         	System.out.println("Found row: " + rr);
       	}
     	} finally {
       	scanner.close();
     	}
   	} finally {
     	if (table != null) table.close();
   	}
 	} finally {
   	connection.close();
 	}
  }
}

Thrift、REST 和对其他编程语言的支持

为了使用其他编程语言,Hbase 提供了 Thrift API 和 Rest API。基于它们,为所有主要编程语言构建了客户端:python、PHP、Java Script 等。

6.6 使用 HBase 的一些特性

  1. Hbase 与 MapReduce 集成开箱即用,可以使用特殊的 TableInputFormat 和 TableOutputFormat 作为输入和输出。

  2. 选择正确的 RowKey 非常重要。RowKey 必须提供良好的跨区域均匀分布,否则存在所谓的“热点区域”的风险 - 比其他区域使用频率更高的区域,这会导致系统资源使用效率低下。

  3. 如果数据不是单个上传,而是立即大批量上传,Hbase 支持一种特殊的 BulkLoad 机制,可以让你上传数据的速度比使用单个 Puts 快得多。BulkLoad 本质上是一个两步操作:

    • 使用特殊的 MapReduce 作业在没有 puts 参与的情况下形成 HFile
    • 将这些文件直接插入到 Hbase 中
  4. Hbase 支持将其指标输出到 Ganglia 监控服务器。这在管理 Hbase 以深入了解 HBase 问题时非常有帮助。

行键

RowKey是用户ID,是一个GUUID,一个专门生成的字符串,全球唯一。GUUID 分布均匀,可以在服务器之间很好地分布数据。

列族

我们的存储使用两个列族:

  • 数据。这组列存储不再与广告目的相关的数据,例如用户访问过某些 URL 的事实。此 Column Family 的 TTL 设置为 2 个月,版本数限制为 2000。
  • 长数据。这组列存储的数据不会随着时间的推移而失去相关性,例如性别、出生日期和其他“永恒”的用户特征。

喇叭

每种类型的用户事实都存储在单独的列中。例如,Data:_v 列存储用户访问过的 URL,LongData:gender 列存储用户的性别。

这个事实的时间戳被存储为时间戳。例如,在 Data:_v 列中,时间戳是用户访问特定 URL 的时间。

这种用户数据存储结构非常符合我们的使用模式,可以让你快速更新用户数据,快速获取用户的所有必要信息,并使用MapReduce一次性快速处理所有用户的数据。

6.7 备选方案

HBase 的管理和使用非常复杂,因此在使用 HBase 之前,查看替代方案是有意义的:

  • 关系数据库。一个很好的选择,尤其是在数据适合一台机器的情况下。另外,首先,在主索引以外的索引事务很重要的情况下,您应该考虑关系数据库。

  • 键值存储。Redis 和 Aerospike 等存储更适合需要延迟且批处理不太重要的情况。

  • 文件及其使用 MapReduce 的处理。如果数据只是增加,很少更新/改变,那么最好不要使用HBase,而只是将数据存储在文件中。为了简化文件工作,您可以使用 Hive、Pig 和 Impala 等工具。

在以下情况下使用 HBase 是合理的:

  • 有很多数据,它们不适合放在一台计算机/服务器上
  • 数据经常更新和删除
  • 数据中有一个明确的“键”,可以方便地绑定其他所有内容
  • 需要批量处理
  • 需要通过特定密钥随机访问数据