Hadoop-现代大数据处理-全-

Hadoop 现代大数据处理(全)

原文:Modern big data processing with Hadoop

协议:CC BY-NC-SA 4.0

零、前言

如今,复杂的数据结构需要复杂的数据转换解决方案及其语义表示,以使用户更容易访问信息。Apache Hadoop,以及大量其他大数据工具,使您能够相对轻松地构建此类解决方案。这本书列出了一些独特的想法和技术,使您能够在成为专家大数据架构师的道路上克服不同的数据处理和分析挑战。

这本书首先快速阐述了企业数据架构的原则,并展示了它们与 Apache Hadoop 生态系统的关系。您将全面了解 Hadoop 的数据生命周期管理,然后在 Hadoop 中建模结构化和非结构化数据。这本书还将向您展示如何通过利用 Apache Spark 等工具来设计实时流管道,以及如何使用 Elasticsearch 等工具来构建高效的企业搜索解决方案。您将在 Hadoop 上构建企业级分析解决方案,并学习如何使用 Tableau 和 Python 等工具可视化您的数据。

本书还涵盖了在本地和云上部署大数据解决方案的技术,以及管理和管理 Hadoop 集群的专家技术。

到本书结束时,您将拥有构建专家大数据系统所需的所有知识,这些系统可以利用全套现代大数据框架和工具来满足任何数据或洞察力需求。您将拥有成为真正的大数据专家所需的技能和知识。

这本书是给谁的

这本书是为那些希望在 Hadoop 行业快速发展职业生涯并成为专业大数据架构师的大数据专业人士而写的。希望在大数据和 Hadoop 领域建立职业生涯的项目经理和大型机专业人员也会发现这本书很有用。需要对 Hadoop 有所了解,才能充分利用这本书。

这本书涵盖了什么

第 1 章企业数据架构原则,展示了如何在 Hadoop 集群中存储和建模数据。

第 2 章Hadoop 生命周期管理,涵盖了数据生命周期的各个阶段,包括数据创建、共享、维护、归档、保留和删除的时间。它还进一步详细介绍了数据安全工具和模式。

第 3 章Hadoop 设计考虑,涵盖了关键的数据架构原则和实践。读者将了解现代数据架构师如何适应大数据架构师用例。

第 4 章数据移动技术,涵盖了在 Hadoop 集群之间传输数据的不同方法,以利用其真正的力量。

第 5 章Hadoop中的数据建模,展示了如何使用云基础设施构建企业应用。

第 6 章设计实时流数据管道,涵盖了设计实时数据分析的不同工具和技术。

第 7 章大规模数据处理框架,描述了企业数据的架构原则以及管理和保护该数据的重要性。

第八章搭建企业搜索平台,给出了使用 Elasticsearch 搭建搜索解决方案的详细架构设计。

第 9 章设计数据可视化解决方案,展示了如何使用 Apache Ambari 部署您的 Hadoop 集群。

第 10 章使用云开发应用,涵盖了可视化数据的不同方式以及选择正确的可视化方法所涉及的因素。

第 11 章生产 Hadoop 集群部署,涵盖了不同的数据处理解决方案,以从我们的数据中获取价值。

充分利用这本书

如果 Hadoop 的正确安装能像前面几章解释的那样完成,那就太好了。对 Hadoop 的详细甚至很少了解将是一个额外的优势。

下载示例代码文件

你可以从你在www.packtpub.com的账户下载这本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册将文件直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. 登录或注册www.packtpub.com
  2. 选择“支持”选项卡。
  3. 点击代码下载和勘误表。
  4. 在搜索框中输入图书的名称,并按照屏幕指示进行操作。

下载文件后,请确保使用最新版本的解压缩文件夹:

  • 视窗系统的 WinRAR/7-Zip
  • zipeg/izp/un ARX for MAC
  • 适用于 Linux 的 7-Zip/PeaZip

这本书的代码包也托管在 GitHub 上,网址为 https://GitHub . com/PacktPublishing/Modern-Big-Data-Processing-with-Hadoop。如果代码有更新,它将在现有的 GitHub 存储库中更新。

我们还有来自丰富的图书和视频目录的其他代码包,可在【https://github.com/PacktPublishing/】获得。看看他们!

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载:http://www . packtpub . com/sites/default/files/downloads/modernbigdata processing with Hadoop _ color images . pdf

使用的约定

本书通篇使用了许多文本约定。

CodeInText:表示文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和推特句柄。下面是一个例子:“将下载的WebStorm-10*.dmg磁盘镜像文件作为另一个磁盘挂载到系统中。”

代码块设置如下:

export HADOOP_CONF_DIR="${HADOOP_CONF_DIR:-$YARN_HOME/etc/hadoop}"
export HADOOP_COMMON_HOME="${HADOOP_COMMON_HOME:-$YARN_HOME}"
export HADOOP_HDFS_HOME="${HADOOP_HDFS_HOME:-$YARN_HOME}"  

当我们希望将您的注意力吸引到代码块的特定部分时,相关的行或项目以粗体显示:

$ hadoop fs -cat /tmp/output-7/part*
 NewDelhi, 440
 Kolkata, 390
 Bangalore, 270

任何命令行输入或输出都编写如下:

useradd hadoop
passwd hadoop1 

粗体:表示一个新的术语、一个重要的单词或者你在屏幕上看到的单词。例如,菜单或对话框中的单词像这样出现在文本中。下面是一个示例:“从管理面板中选择系统信息。”

Warnings or important notes appear like this. Tips and tricks appear like this.

取得联系

我们随时欢迎读者的反馈。

综合反馈:发邮件feedback@packtpub.com并在邮件主题中提及书名。如果您对本书的任何方面有疑问,请发电子邮件至questions@packtpub.com

勘误表:虽然我们已经尽了最大的努力来保证内容的准确性,但是错误还是会发生。如果你在这本书里发现了一个错误,如果你能向我们报告,我们将不胜感激。请访问www.packtpub.com/submit-errata,选择您的图书,点击勘误表提交链接,并输入详细信息。

盗版:如果您在互联网上遇到任何形式的我们作品的非法拷贝,如果您能提供我们的位置地址或网站名称,我们将不胜感激。请通过copyright@packtpub.com联系我们,并提供材料链接。

如果你有兴趣成为一名作者:如果有一个你有专长的话题,你有兴趣写或者投稿一本书,请访问authors.packtpub.com

复习

请留下评论。一旦你阅读并使用了这本书,为什么不在你购买它的网站上留下评论呢?然后,潜在的读者可以看到并使用您不带偏见的意见来做出购买决定,我们在 Packt 可以了解您对我们产品的看法,我们的作者可以看到您对他们的书的反馈。谢谢大家!

更多关于 Packt 的信息,请访问packtpub.com

一、企业数据架构原则

传统上,企业采用数据仓库来存储、处理和访问大量数据。这些仓库通常是大型关系数据库管理系统数据库,能够存储非常大规模的各种数据集。随着数据复杂性、数量和访问模式的增加,许多企业已经开始采用大数据作为模型来重新设计他们的数据组织,并围绕它定义必要的策略。

此图描述了企业中典型数据仓库的外观:

由于企业有许多不同的部门、组织和地理位置,每个部门都倾向于拥有自己的仓库,并对整个企业提出了各种挑战。例如:

  • 数据的多个来源和目的地
  • 数据复制和冗余
  • 数据访问监管问题
  • 整个企业的非标准数据定义。
  • 软件和硬件的可扩展性和可靠性问题
  • 数据移动和审计
  • 不同仓库之间的集成

由于多项技术进步,以比几十年前更低的成本构建超大规模系统变得非常容易,例如:

  • 每太字节成本
  • 每纳米计算能力
  • 千兆网络带宽

随着全球化,市场全球化,消费者也全球化。这增加了覆盖范围。这些进步也给企业带来了以下方面的挑战:

  • 人力资本管理
  • 仓库管理
  • 物流管理
  • 数据隐私和安全
  • 销售和计费管理
  • 了解需求和供应

为了掌握市场的需求,企业开始收集越来越多关于自己的指标;因此,在当前情况下,数据处理的维度有所增加。

在本章中,我们将学习:

  • 数据架构原则
  • 元数据的重要性
  • 数据治理
  • 数据安全
  • 数据即服务
  • Hadoop 的数据架构演进

数据架构原则

当前状态的数据可以定义为以下四个维度(四个 Vs)。

数据量是设计大数据系统所需的一个重要指标。这是决定企业为满足当前和未来存储需求而必须进行的投资的一个重要因素。

企业中不同类型的数据需要不同的存储、归档和处理能力。Petabyte 存储系统在当今行业非常普遍,这在几十年前几乎是不可能实现的。

速度

这是决定数据移动性的另一个数据维度。组织内部存在着属于以下类别的各种数据:

  • 流式数据:
    • 实时/接近实时的数据
  • 静态数据:
    • 不可变数据
    • 可变数据

这个维度对企业用来消费和处理数据的网络架构有一些影响。

多样化

这个维度谈论数据的形式和形状。我们可以进一步将其分为以下几类:

  • 流式数据:
    • 在线数据格式(例如,JSON、MPEG 和 Avro)
  • 静态数据:
    • 不可变数据(例如,媒体文件和客户发票)
    • 可变数据(例如,客户详细信息、产品库存和员工数据)
  • 应用数据:
    • 配置文件、机密、密码等

作为一个组织,采用非常少的技术来减少数据的多样性是非常重要的。拥有许多不同类型的数据对于企业来说,在管理和使用所有数据方面都是一个非常大的挑战。

诚实

这个维度讲的是数据的准确性。如果没有对企业内每个系统为保持数据安全、可用和可靠而提供的保证有一个坚实的理解,就很难理解从这些数据中生成的分析并进一步生成见解。

必要的审计应该到位,以确保流经系统的数据通过所有质量检查,并最终通过大数据系统。

让我们看看典型的大数据系统是什么样子的:

如您所见,许多不同类型的应用正在与大数据系统交互,以存储、处理和生成分析。

元数据的重要性

在我们试图理解元数据的重要性之前,让我们尝试理解什么是元数据。元数据简单来说就是数据关于数据的数据。这听起来很混乱,因为我们正在以递归的方式定义定义。

在典型的大数据系统中,我们有这三个垂直级别:

  • 将数据写入大数据系统的应用
  • 在大数据系统中组织数据
  • 使用大数据系统数据的应用

这带来了一些挑战,因为我们谈论的是存储在大数据系统中的数百万(甚至数十亿)数据文件/数据段。我们应该能够正确识别整个企业中这些数据文件的所有权和使用情况。

我们举一个电视广播公司拥有一个电视频道的例子;它制作电视节目,并通过有线电缆网络、卫星网络、互联网等向所有目标受众广播。如果我们仔细看,内容的来源只有一个。但它正在通过所有可能的媒介传播,最终到达用户的位置,以便在电视、手机、平板电脑等设备上观看。

由于观众在各种设备上访问该电视内容,因此在这些设备上运行的应用可以生成若干消息来指示各种用户动作和偏好,并将它们发送回应用服务器。这些数据非常庞大,存储在一个大数据系统中。

根据数据在大数据系统中的组织方式,外部应用或对等应用几乎不可能知道系统中存储的不同类型的数据。为了使这一过程更容易,我们需要描述和定义数据组织是如何在大数据系统中发生的。这将帮助我们更好地了解大数据系统中的数据组织和访问。

让我们进一步扩展这个例子,假设有另一个应用从大数据系统中读取数据,以了解在给定的电视剧中做广告的最佳时间。该应用应该更好地理解大数据系统中可用的所有其他数据。因此,如果没有定义良好的元数据系统,很难做到以下几点:

  • 了解存储、访问和处理的数据的多样性
  • 跨不同类型的数据集构建接口
  • 从安全角度正确地将数据标记为高度敏感或不敏感的数据
  • 连接大数据生态系统中给定系统集之间的点
  • 审核和排除因数据不一致而可能出现的问题

数据治理

拥有非常大的数据量不足以做出对业务成功有积极影响的非常好的决策。确保只收集、保存和维护高质量的数据是非常重要的。随着需要收集新类型的数据,数据收集过程也经历了演变。在这个过程中,我们可能会破坏一些从上一代数据中读取的接口。如果没有定义明确的流程和人员,处理数据对所有规模的组织来说都是一个巨大的挑战。

要在管理数据方面出类拔萃,我们应该考虑以下品质:

  • 良好的政策和流程
  • 有责任
  • 正式决策结构
  • 管理规则的实施

这些类型的质量的实现被称为数据治理。在高层次上,我们将数据治理定义为管理良好的数据。这个定义也帮助我们澄清了数据管理和数据治理不是一回事。管理数据与使用数据来做出良好的业务决策并最终管理组织有关。数据治理关注的是我们在管理数据时,在整个组织中使用规范行为的程度。

这是一个重要的区别。那么底线是什么呢?大多数组织都管理数据,但很少有人能很好地管理这些管理技术。

数据治理基础

让我们试着理解数据治理的基本原理:

  • 有责任
  • 标准化
  • 透明度

透明度确保组织内外的所有员工在与组织相关的数据进行交互时了解自己的角色。这将确保以下事项:

  • 建立信任
  • 避免惊喜

问责制确保有权访问数据的团队和员工描述他们可以对数据做什么和不能做什么。

标准化处理数据如何被正确地标记、描述和分类。一个例子是如何为组织内的员工生成电子邮件地址。一种方法是使用firstname-lastname@company.com,或者这些的任何其他组合。这将确保每个有权访问这些电子邮件地址的人都知道哪个是第一个,哪个是最后一个,而无需任何人亲自解释。

标准化提高了数据质量,并为多个数据维度带来了秩序。

数据安全

安全不是一个新概念。它从早期的 UNIX 分时操作系统设计开始就被采用了。在最近的过去,由于广泛的数据泄露给组织带来了大量收入损失,个人和组织在安全方面的安全意识有所提高。

安全,作为一个普遍的概念,可以应用于许多不同的事情。谈到数据安全,我们需要了解以下基本问题:

  • 存在哪些类型的数据?
  • 谁拥有数据?
  • 谁有权访问数据?
  • 数据什么时候退出系统?
  • 数据是否受到物理保护?

让我们看一看一个简单的大数据系统,并尝试更详细地理解这些问题。系统的规模让安全成为每个人的噩梦。因此,我们应该制定适当的政策,让所有人保持一致:

在本例中,我们有以下组件:

  • 跨全球多个地理区域运行的异构应用。
  • 应用生成大量各种各样的输入数据。
  • 所有数据都被吸收到一个大数据系统中。
  • ETL/ELT 应用使用来自大数据系统的数据,并将可使用的结果放入 RDBMS(这是可选的)。
  • 商业智能应用从该存储中读取数据,并进一步生成对数据的洞察。这些是领导团队决策的动力。

你可以想象流经这个系统的数据的规模和数量。我们还可以看到,参与整个生态系统的服务器、应用和员工数量非常多。如果我们没有适当的政策,要确保这样一个复杂的系统不是一件非常容易的事情。

此外,如果攻击者使用社会工程来获得对系统的访问,我们应该确保数据访问仅限于最低可能的级别。当糟糕的安全实施到位时,攻击者可以访问几乎所有的商业秘密,这可能会给企业带来严重损失。

仅举一个例子,一家初创公司正在构建下一代计算设备,以将其所有数据托管在云上,并且没有适当的安全策略。当攻击者危及云中服务器的安全时,他们可以很容易地发现这家初创公司正在构建什么,并窃取情报。一旦情报被窃取,我们可以想象黑客如何利用这一点为自己谋利。

了解了安全性的重要性后,让我们定义需要保护的内容。

应用安全

应用是基于产品的组织的第一线,因为消费者使用这些应用与应用提供的产品和服务进行交互。我们必须确保在编程这些应用接口时遵循适当的安全标准。

由于这些应用向后端系统生成数据,我们应该确保在防火墙方面只允许适当的访问机制。

此外,这些应用与许多其他后端系统交互,我们必须确保显示与用户相关的正确数据。这归结为在访问不同类型的组织资源时,不仅为用户,而且为应用实现适当的身份验证和授权。

如果没有适当的审计,应用很难分析数据访问模式。所有日志都应该收集在远离应用服务器的中央位置,并可以进一步纳入大数据系统。

输入数据

一旦应用生成了几个度量标准,它们就可以临时存储在本地,由周期性进程进一步使用,或者被进一步推送到像 Kafka 这样的流系统。

在这种情况下,我们应该仔细思考并设计数据存储在哪里,以及哪些用户可以访问这些数据。如果我们进一步将这些数据写入像 Kafka 或 MQ 这样的系统,我们必须确保进一步的身份验证、授权和访问控制到位。

在这里,我们可以利用操作系统提供的安全措施,如进程用户标识、进程组标识、文件系统用户标识、组标识,以及高级系统(如 SELinux)来进一步限制对输入数据的访问。

大数据安全

根据所选择的数据仓库解决方案,我们必须确保授权的应用和用户能够读写数据仓库。适当的安全策略和审计应该到位,以确保这种大规模的数据不容易被每个人访问。

为了实现所有这些访问策略,我们可以使用操作系统提供的机制,如文件访问控制和使用访问控制。由于我们谈论的是地理上分布的大数据系统,因此我们必须思考和设计集中式身份验证系统,以便在员工与这些大数据系统交互时为他们提供无缝体验。

RDBMS 安全性

许多关系数据库管理系统非常安全,可以为用户提供以下访问级别:

  • 数据库ˌ资料库
  • 桌子
  • 使用模式

他们也有内置的审计机制来告诉哪些用户已经访问了什么类型的数据以及何时访问。这些数据对于保持系统的安全至关重要,应该进行适当的监控,以监控这些系统的健康和安全。

商业智能安全

这些应用可以是为满足公司特定需求而内部构建的应用,也可以是能够激发业务团队所寻求的洞察力的外部应用。还应该通过实施单点登录、基于角色的访问控制和基于网络的访问控制来适当地保护这些应用。

由于这些应用提供的洞察力对组织的成功至关重要,因此应该采取适当的安全措施来保护它们。

到目前为止,我们已经看到了企业系统的不同部分,并了解了可以遵循什么来提高整体企业数据设计的安全性。让我们谈谈在数据设计中随处可以应用的一些常见的东西。

人身安全

这涉及物理设备访问、数据中心访问、服务器访问和网络访问。如果未经授权的人员获得了对企业拥有的设备的访问权限,他们就可以访问其中存在的所有数据。

正如我们在前面几节中看到的,当操作系统运行时,我们能够通过利用操作系统的安全特性来保护资源。当入侵者获得对设备(甚至退役的服务器)的物理访问权限时,他们可以将这些设备连接到他们控制的另一个操作系统,并访问我们服务器上的所有数据。

当我们停用服务器时,必须小心,因为有一些方法可以恢复写入这些设备的数据(即使在格式化之后)。因此,我们应该遵循行业标准的设备擦除技术来适当地清除企业拥有的所有数据。

为了防止这些,我们应该考虑加密数据。

数据加密

加密数据将确保即使授权人员能够访问设备,他们也无法恢复数据。由于数据和员工流动性的增加,这是目前遵循的标准做法。许多大企业对笔记本电脑和手机上的硬盘进行加密。

安全密钥管理

如果您使用过任何需要身份验证的应用,您将使用用户名和密码的组合来访问服务。通常这些秘密存储在源代码本身中。这给非编译程序带来了挑战,因为攻击者可以轻松访问用户名和密码来访问我们的资源。

许多企业开始采用集中式密钥管理,使用这种管理,应用可以查询这些服务来访问受身份验证保护的资源。所有这些访问模式都经过 KMS 的适当审核

员工还应该使用自己的凭据访问这些系统,以访问资源。这确保密钥受到保护,并且只有授权的应用才能访问。

数据即服务

数据即服务(【DaaS】)是近年来因采用云的增加而流行的一个概念。说到数据。如何将数据添加到服务模型中,这可能有些令人困惑?

DaaS 为服务用户提供了极大的灵活性,让他们不必担心运行服务的底层基础设施的规模、性能和维护。基础设施会自动为我们处理它,但是考虑到我们正在处理云模型,我们拥有云的所有优势,例如现收现付、容量规划等等。这将减轻数据管理的负担。

如果我们试着仔细理解这一点,我们将单独去掉数据管理部分。但是数据治理也应该在这里得到很好的定义,否则我们将失去服务模型的所有好处。

到目前为止,我们谈论的是云中的服务概念。这是否意味着我们不能在企业或更小的组织中使用它?答案是。因为这是一个通用的概念,它告诉我们以下事情。

当我们谈论一个服务模式时,我们应该记住以下几点,否则混乱会接踵而至:

  • 证明
  • 批准
  • 审计

这将保证只有定义明确的用户、IP 地址和服务才能访问作为服务公开的数据。

让我们以一个拥有以下数据的组织为例:

  • 雇员
  • 服务器和数据中心
  • 应用
  • 内部网文档网站

如您所见,所有这些都是独立的数据集。但是,作为一个整体,当我们希望组织成功时。有很多重叠,我们应该尝试在这里接受 DaaS 模型,以便所有这些权威的数据应用仍然可以管理数据。但是对于其他应用,它们是使用 REST API 作为简单服务公开的;因此,这增加了组织内部的协作并促进了创新。

让我们进一步举例说明这是如何实现的:

  • 以数据库形式管理所有员工数据的团队可以提供简单的数据服务。所有其他应用都可以使用此数据集,而无需担心存储此员工数据的底层基础架构:
    • 这将释放数据服务的消费者,使消费者:
      • 无需担心底层基础设施
      • 无需担心用于与这些数据服务器通信的协议
      • 可以只关注 REST 模型来设计应用
    • 这方面的典型例子有:
      • 将员工数据存储在数据库中,如LDAPMicrosoft Active目录
  • 为整个组织管理基础架构的团队可以设计自己的系统,以避开组织的整个硬件库存,并可以提供简单的数据服务。组织的其他成员可以使用它来构建他们感兴趣的应用:
    • 这将使企业更加敏捷
    • 它确保了关于组织整个硬件的数据只有一个真实来源
    • 它提高了对数据的信任,并增强了对基于这些数据构建的应用的信心
  • 组织中的每个团队都可能使用不同的技术来构建应用并将其部署到服务器上。接下来,他们还需要构建一个数据存储,以跟踪服务器上部署的软件的活动版本。拥有这样的数据源可以在以下方面帮助组织:
    • 使用这些数据构建的服务可以持续监控和查看软件部署更频繁发生的位置
    • 这些服务还可以找出哪些应用易受攻击,并在生产中积极部署,以便通过升级操作系统或软件采取进一步措施来修复漏洞
    • 了解整个软件部署生命周期中的挑战
    • 为整个组织提供了一个以标准方式做事的单一平台,从而提升了主人翁意识
  • 文档对于一个组织来说是非常重要的事情之一。使用 DaaS 模型,组织和团队可以专注于与他们公司相关的文档,并且只为这些文档付费,而不是运行他们自己的基础架构。在这里,谷歌文档和微软在线办公等服务非常受欢迎,因为它们让我们可以灵活地按需付费,最重要的是,不用担心构建这些服务所需的技术。
    • 拥有这样的数据服务模型将有助于我们实现以下目标:
      • 只为使用的服务付费
      • 根据需要增加或减少存储规模
      • 如果服务在云上并连接到互联网,则可以从任何地方访问数据
      • 根据企业政策,通过虚拟专用网络连接时访问公司资源

在前面的示例中,我们看到了企业中使用的各种应用,以及数据作为模型如何以各种方式帮助企业带来协作、创新和信任。

但是,说到大数据,DaaS 能做什么?

就像所有其他数据一样,大数据也可以适合 DaaS 模型,并提供与我们之前看到的相同的灵活性:

  • 无需担心底层硬件和技术
  • 根据需要扩展基础架构
  • 只为企业拥有的数据付费
  • 运营和维护挑战被消除
  • 数据可以在地理位置上可用,以实现高可用性
  • 针对灾难恢复需求的集成备份和恢复

有了这几个优势,企业可以更加敏捷,构建能够利用这些数据作为服务的应用。

Hadoop 的演进数据架构

Hadoop 是一款有助于可扩展和分布式计算的软件。在 Hadoop 出现之前,业界使用了许多技术来满足他们的数据需求。让我们对这些存储机制进行分类:

  • 分级数据库
  • 网络数据库
  • 关系数据库

让我们了解一下这些数据架构是什么。

分层数据库体系结构

这种存储企业数据的模型是由 IBM 在 60 年代早期发明的,并被用于他们的应用中。层次数据库的基本概念是数据以根树的形式组织。根节点是树的开始,然后所有的子节点只链接到它的一个父节点。这是一种非常独特的存储和检索东西的方式。

如果你有一些计算机科学的背景,树是存储数据的独特方式之一,因此它彼此之间有一些关系(像父子关系)。

此图说明了数据在典型的 HDBMS 中是如何组织的:

正如我们所看到的,根节点是组织本身,与组织相关的所有数据都遵循一个描述若干关系的树结构。这些关系可以这样理解:

  • 员工拥有笔记本电脑手机工作站iMac
  • 员工隶属于组织
  • 许多供应商提供不同的要求:
    • 电脑厂商供应 iMac工作站
  • 印度和美国都有餐饮;两个供应商,最佳餐饮服务商湾区餐饮服务商提供这些服务

即使我们在这个巨大的数据存储中表达了多种类型的关系,我们也可以看到数据被复制,并且查询不同类型需求的数据成为一个挑战。

让我们来看一个简单的问题,比如:哪个供应商提供了员工-391 拥有的 iMac?

为了做到这一点,我们需要遍历树并从两个不同的子树中找到信息。

网络数据库体系结构

网络数据库管理系统也源于计算机科学:图论,其中有大量不同类型的节点和关系将它们连接在一起。该结构中没有特定的根节点。它是在 70 年代早期发明的:

我们可以看到,在这个结构中,有几个核心数据集,还有其他数据集与核心数据集链接。

我们可以这样理解它:

  • 定义了主医院
  • 它有许多附属医院
  • 附属医院在印度和美国
  • 印度医院使用病人的数据
  • 美国医院使用病人的数据
  • 病人商店与主医院相连
  • 员工属于医院,与其他组织有联系

在这种结构中,根据我们提出的设计,数据被表示为一个元素网络。

关系数据库体系结构

该系统于 80 年代初在 IBM 再次开发,被认为是迄今为止最著名的数据库系统之一。采用这种风格的软件的几个显著例子是甲骨文和 MySQL。

在这个模型中,数据以记录的形式存储,每个记录又有几个属性。所有记录集合都存储在一个表中。跨表的数据属性之间存在关系。相关表的集合存储在数据库中。

让我们看看这个关系数据库管理系统表的一个典型例子:

我们正在定义以下类型的表和关系

雇员

  • 该表由所有员工记录组成
  • 每条记录的定义如下:
    • 员工唯一标识符
    • 员工姓名
    • 员工出生日期
    • 员工地址
    • 员工电话
    • 员工移动电话

设备

  • 该表由员工拥有的所有设备组成
  • 每个所有权记录根据以下内容进行定义:
    • 设备所有权标识符
    • 设备模型
    • 设备制造商
    • 设备所有权日期
    • 设备唯一编号
    • 员工编号

部门

包含组织中所有部门的表格:

  • 唯一的部门标识
  • 唯一的部门名称

部门和员工映射表

这是一个特殊的表,仅包含使用其唯一标识符的部门和员工之间的关系:

  • 唯一的部门标识
  • 唯一的员工标识

Hadoop 数据架构

到目前为止,我们已经探索了企业使用的几种类型的数据架构。在本节中,我们将了解数据架构是如何在 Hadoop 中构建的。

简单介绍一下,Hadoop 有多个组件:

  • 数据
  • 数据管理
  • 对数据运行作业的平台

数据层

这是所有数据以文件形式存储的层。这些文件由 Hadoop 系统在内部拆分为多个部分,并在服务器之间复制以实现高可用性。

因为我们讨论的是以文件形式存储的数据,所以了解这些文件是如何组织的以实现更好的治理是非常重要的。

下图显示了如何在一个 Hadoop 存储层中组织数据。数据的内容可以是任何形式,因为 Hadoop 不会强制它们处于特定的结构中。因此,我们可以在该数据层中安全地存储蓝光电影、 CSV ( 逗号分隔值)文件、AVRO 编码文件等。

You might be wondering why we are not using the word HDFS (Hadoop Distributed File System) here. It's because Hadoop is designed to run on top of any distributed file system.

数据管理层

该层负责跟踪给定文件或路径的数据存储位置(根据服务器、偏移量等)。因为这只是一个记账层,所以非常重要的一点是,这一层的内容要得到高可靠性和持久性的保护。该层中数据的任何损坏都将导致整个数据文件永远丢失。

在 Hadoop 术语中,这也被称为名称节点

作业执行层

一旦我们解决了数据问题,接下来就是读写数据的程序了。当我们谈论单个服务器或笔记本电脑上的数据时,我们很清楚数据在哪里,因此我们可以编写程序,将数据读写到相应的位置。

以类似的方式,Hadoop 存储层使得应用非常容易给出文件路径,以便在计算过程中将数据读写到存储中。这对编程社区来说是一个非常大的胜利,因为他们不需要担心数据在分布式 Hadoop 集群中的物理存储位置的底层语义。

由于 Hadoop 在数据模型附近提升了计算,这提供了非常高的性能和吞吐量,运行的程序可以由 Hadoop 引擎在更接近整个集群中数据的位置进行调度和执行。数据的整个传输和软件执行的移动都由 Hadoop 负责。

因此,Hadoop 的最终用户将该系统视为具有强大计算能力和存储的简单系统。这种抽象赢得了每个人的需求,成为当今大数据计算的标准。

摘要

在本章中,我们看到了有多少组织采用数据仓库来存储、处理和访问他们拥有的大量数据。我们学习了数据架构原则、它们的治理和安全性。在下一章中,我们将了解一些数据预处理的概念。

二、Hadoop 生命周期管理

在本章中,我们将了解以下主题:

  • 数据争论
  • 数据屏蔽
  • 数据安全

数据争论

如果你有处理某种数据的经验,你会记得大多数时候数据需要被预处理,这样我们就可以进一步把它作为更大分析的一部分。这个过程叫做数据角力

让我们看看这个过程中的典型流程是什么样的:

  • 数据采集
  • 数据结构分析
  • 信息提取
  • 不需要的数据删除
  • 数据转换
  • 数据标准化

让我们试着详细了解这些。

数据采集

尽管不是数据争论的一部分,这个阶段处理从某处获取数据的过程。通常,所有数据都是在一个中心位置生成和存储的,或者在位于某些共享存储上的文件中可用。

了解这一步有助于我们构建接口或使用现有的库从获取的数据源位置提取数据。

数据结构分析

一旦获得数据,我们就必须了解数据的结构。请记住,我们获得的数据可以是以下任何形式:

  • 文本数据:
    • 结构数据
    • 非结构化数据
  • 二进制数据

这就是我们需要某些工具来帮助我们理解数据结构的地方。

一旦我们彻底理解了我们正在处理的数据,下一个任务就是理解我们需要从这个结构中提取的点点滴滴。有时,根据我们处理的数据的复杂性和大小,我们可能需要时间来真正找到并提取我们正在寻找的信息。

一旦我们知道我们在寻找什么,并且对数据的结构有了坚实的理解,我们就更容易想出简单的算法来从输入数据中提取所需的信息。

信息提取

在这个阶段,我们感兴趣的是从输入数据中提取必要的细节。在前一阶段,我们已经确定了我们感兴趣的必要部分。在这里,我们可以采用以下技术来提取信息:

  • 识别并定位文本所在的位置
    • 分析并提出信息提取的最佳方法:
    • 标记和提取信息
    • 转到偏移并提取信息
    • 基于正则表达式的信息抽取
    • 基于复杂算法的信息提取

根据数据的复杂性,我们可能必须采用一种或多种上述技术来从目标数据中提取信息。

不需要的数据删除

该阶段可以发生在信息提取步骤之前或之后。这取决于哪一个更容易(缩短文本或提取信息)。这是分析师可以做出的设计选择。

在这一阶段,我们将从信息或输入数据中删除不需要的数据,以便进一步提取数据,并可以轻松地用于我们的业务需求。

数据转换

这也是一个非常重要的阶段,在这个阶段,我们执行企业定义的标准来定义最终的数据输出。例如,一个组织可以建议所有国家/地区代码都采用 ISO 3166-1 alpha-2 格式。为了遵守这一标准,我们可能必须转换输入数据,其中可能包含具有全名的国家。因此,必须进行映射和转换。

可以对输入数据执行许多其他转换,以使组织中的任何人都可以按照组织的标准以定义良好的形式使用最终数据。

这一步也为拥有一个企业级标准来改进协作提供了一些重要性。

数据标准化

一旦信息提取完成,任何必要的清理完成,我们需要决定如何保存这个过程的结果。通常,我们可以对该数据使用简单的 CSV ( 逗号分隔值)格式。如果是处理复杂的输出格式,可以选择 XML ( 可扩展标记语言)或者 JSON ( javascript 对象标注)格式。

这些格式是非常标准的,我们今天所拥有的几乎所有技术都非常容易理解这些格式。但是为了一开始简单起见,最好从 CSV 格式开始。

数据屏蔽

处理客户数据的企业必须确保这些客户的 PII ( 个人身份信息)不会在整个数据管道中自由移动。该标准不仅适用于客户数据,还适用于根据 GDPR 法案、SOX 法案等标准被视为机密的任何其他类型的数据。为了确保我们保护客户、员工、承包商和供应商的隐私,我们需要采取必要的预防措施,以确保当数据通过几个管道时,数据用户只能看到匿名数据。我们的匿名化程度取决于公司遵守的标准以及国家标准。

因此,数据屏蔽可以称为在不丢失意义或上下文的情况下,用其他数据隐藏/转换部分原始数据的过程。

在本节中,我们将了解实现这一目标的各种技术:

  • 替代:
    • 静态
    • 动态:
      • 加密
      • 散列法
  • 隐藏
  • 清除
  • 截断
  • 差异
  • 洗牌

代替

替换是用计算数据替换部分数据的过程。数学上可以定义为:

其中 x 为源, y 为该功能的输出。

为了选择正确的替代机制,我们需要了解如何使用这些数据、目标受众以及数据流环境。让我们看看各种可用的替代机制。

静态

在这个方法中,我们有一个查找表;它由给定输入集的所有可能替换组成。该查找表可以这样可视化:

| 源文本(y) | 替代文本(y) |
| 史蒂夫·乔布斯 | AAPL-1 |
| 猫 | One hundred and twenty-three million four hundred and fifty-six thousand seven hundred and eighty-nine |
| 网球 | 板球 |

此表说明了如何构建查找表来用不同的文本替换源文本。当有预定义数量的替换可用时,这种方法可以很好地扩展。

这种基于查找表的替换的另一个例子是当我们遵循国家代码的命名标准时,例如,ISO-8661:

| 源文本(x) | 替代文本(y) |
| 埃及 | 邪恶的笑(Evil Grin 的缩写) |
| 印度 | 在…里 |
| 圣文森特和格林纳丁斯 | 越南 |
| 联合王国 | 千兆字节 |
| 美利坚合众国 | 美国 |

动态的

当存在大量可能性,并且我们希望使用一些算法来更改数据时,这些替代技术非常有用。这些方法可以分为两种。

加密

这是通过使用某种形式的秘密将给定文本更改为其他形式的过程。这些是数学定义的函数:

如您所见,这些函数接受一个输入和一个秘密,并生成可以使用相同的秘密和输出解密的数据:

如果我们仔细观察,它是在这里发挥重要作用的秘密。在密码学中,有两种基于这个秘密的算法。这些的使用取决于情况和秘密运输的挑战。

在不深入研究密码学的情况下,让我们试着了解一下这些方法是什么:

  • 对称密钥加密
  • 非对称密钥加密

两者之间的基本区别是,在第一种情况下,我们对加密和解密使用相同的秘密。但是在后者中,我们使用两种不同的密钥进行加密和解密。

让我们看几个对称密钥加密的例子:

| 算法 | 输入数据 | 输出数据 | 方法 |
| ROT13 | hello | uryyb | 加密 |
| | uryyb | hello | [通信]解密 |
| 是吗 | hello | yOYffF4rl8lxCQ4HS2fpMg== | 加密(秘密是hello) |
| | yOYffF4rl8lxCQ4HS2fpMg== | hello | 解密(秘密是hello) |
| RIJNDAEL-256 | hello | v8QbYPszQX/TFeYKbSfPL/
rNJDywBIQKtxzOzWhBm16/
VSNN4EtlgZi3/
iPqJZpCiXXzDu0sKmKSl6IxbBKhYw== | 加密(秘密是hello) |
| | v8QbYPszQX/TFeYKbSfPL/
rNJDywBIQKtxzOzWhBm16/
VSNN4EtlgZi3/
iPqJZpCiXXzDu0sKmKSl6IxbBKhYw== | hello | 加密(秘密是hello) |

如您所见,根据我们使用的加密算法,生成的数据在复杂性和长度上都有所不同。它还取决于用于加密的密钥。

加密带来了更多计算需求和存储空间的挑战。如果我们想在屏蔽过程中使用加密作为方法之一,我们需要相应地规划我们的系统。

散列法

这也是一种基于密码学的技术,原始数据被转换成不可逆的形式。让我们看看这个的数学形式:

这里,与加密的情况不同,我们不能使用输出来发现输入是什么。

让我们看几个例子来更好地理解这一点:

| 输入 | 输出 | 方法 |
| 10-point | 7d862a9dc7b743737e39dd0ea3522e9f | 讯息摘要 5 |
| 10th | 8d9407b7f819b7f25b9cfab0fe20d5b3 | 讯息摘要 5 |
| 10-point | c10154e1bdb6ea88e5c424ee63185d2c1541efe1bc3d4656a4c3c99122ba9256 | SHA256 |
| 10th | 5b6e8e1fcd052d6a73f3f0f99ced4bd54b5b22fd4f13892eaa3013ca65f4e2b5 | SHA256 |

我们可以看到,根据我们使用的加密算法,输出大小会有所不同。另一件需要注意的事情是,给定的散列函数产生相同的输出大小,而与输入大小无关。

隐藏

在这种方法中,数据被认为过于敏感,甚至不能透露给原始所有者。因此,为了保护数据的机密性,文本的某些部分用一个预定义的字符屏蔽,比如 X(或任何东西),这样只有对这些部分有完全了解的人才能提取必要的信息。

示例:信用卡信息被认为是高度机密,绝不应该透露给任何人。如果你有一些在亚马逊等网站上在线购买的经验,你会看到你的完整信用卡信息没有显示出来;仅显示最后四位数字。由于我是这种信用卡的真正所有者,我可以轻松识别它并继续交易。

同样,当分析师需要查看部分数据时,重要的是要屏蔽掉其中的重要部分,这样最终用户就不会了解全部情况,而是同时使用这些数据进行他们正在进行的任何分析。

让我们看几个例子来更好地理解这一点:

| 数据类型 | 输入 | 输出 | 网络 |
| 信用卡呢 | 4485 4769 3682 9843 | 4485 XXXX XXXX 9843 | 签证 |
| 信用卡呢 | 5402 1324 5087 3314 | 5402 XXXX XXXX 3314 | 万事达信用卡 |
| 信用卡呢 | 3772 951960 72673 | 3772 XXXXXX 72673 | 美国运通 |

在前面的示例中,这些数字遵循预定义的算法和大小。因此,在固定位置屏蔽数字的简单技术可以更好地工作。

让我们再举一个隐藏部分电子邮件地址的例子,这些地址的大小和复杂性各不相同。在这种情况下,我们必须遵循不同的技术来隐藏角色,以免泄露完整的信息:

| 数据类型 | 输入 | 输出 | 方法 |
| 电子邮件 | hello@world.com | h.l.o@w.r.d.com | 甚至隐藏 |
| | simple@book.com | .i.p.e@.o.k.c.m | 奇怪的隐藏 |
| | something@something.com | s...th.ng@..me...com | 复杂隐藏 |

这些技术可以简单到:

  • 偶数隐藏:在这个技巧中,我们隐藏了每个处于偶数位置的角色
  • 奇数隐藏:我们隐藏输入数据中的每个奇数字符
  • 复杂隐藏:在这项技术中,我们理解我们正在使用 NLP 处理的数据,然后尝试应用一种算法,这种算法不会泄露太多信息,不会让任何聪明人解码

清除

顾名思义,当应用于输入数据时,这会导致数据丢失。根据我们处理的数据的重要性,我们需要应用这种技术。这种技术的典型例子是为一列中的所有记录设置一个NULL值。因为这种空数据不能用来推断任何有意义的东西,所以这种技术有助于确保机密数据不会被发送到数据处理的其他阶段。

让我们举几个擦除的例子:

| 输入数据 | 输出数据 | 什么被抹掉了 |
| 空每月收入 1000 印度卢比 | 拉维每月收入为空 | 工资和姓名 |
| 空手机号码为 0123456789 | 拉维的手机号码为空 | 手机号码和姓名 |

从这些例子中,你可能会想:为什么我们要否定这些价值观?当我们对 PII 不感兴趣,但对我们的数据库/输入中有多少工资记录或手机号码记录感兴趣时,这种技术很有用。

这个概念也可以扩展到其他用例。

截断

擦除的另一种变体是截断,即我们使所有输入数据的大小一致。当我们非常确定信息丢失在管道的进一步处理中被接受时,这是有用的。

这也可以是一种智能截断,我们知道我们正在处理的数据。让我们看看这个电子邮件地址的例子:

| 输入 | 输出 | 什么被截断了 |
| alice@localhost.com | alice | @localhost.com |
| bob@localhost.com | bob | @localhost.com |
| rob@localhost.com | rob | @localhost.com |

从前面的例子中,我们可以看到电子邮件中的所有域部分都被截断了,因为它们都属于同一个域。这项技术节省了存储空间。

差异

这种技术对于本质上是数字的数据类型非常有用。它也可以应用于日期/时间值。

这遵循一种统计方法,即我们尝试通过算法将输入数据改变+/-X %的因子。X 的价值纯粹取决于我们正在进行的分析,不应该对理解业务数字产生总体影响。

让我们看几个例子:

| 输入数据 | 输出数据 | 方法 | 解释 |
| One hundred | One hundred and ten | 固定方差 | 增加 10% |
| -100 | Ninety | 固定方差 | 减少 10% |
| 2000 年 1 月 1 日 | 2000 年 2 月 1 日 | 固定方差 | 添加 1 个月 |
| 2000 年 8 月 1 日 | 2000 年 7 月 1 日 | 固定方差 | 减少 1 个月 |
| One hundred | One hundred and one | 动态方差 | 增加或减少 1%至 5% |
| One hundred | One hundred and five | 动态的 | 增加或减少 1%至 5% |

洗牌

这也被认为是实现数据匿名的标准技术之一。这个过程更适用于我们有多个属性(数据库术语中的列)的数据记录的情况。在这种技术中,记录中的数据在一列中被打乱,以确保记录级别的信息被改变。但从统计数据来看,该列中的数据值保持不变。

:在对一个组织的薪资范围做分析的时候,我们其实可以对整个薪资列做一个洗牌,所有员工的薪资从来都不符合现实。但是我们可以利用这些数据对范围进行分析。

在这种情况下,也可以使用复杂的方法,我们可以根据资历、地理位置等其他领域进行洗牌。这种技术的最终目标是保留数据的意义,同时使发现这些属性的原始所有者成为不可能。

让我们用一些示例数据来看看这一点:

有五个样本员工记录及其工资信息。上表有原始的工资明细,下表有打乱的工资记录。仔细看数据就明白了。请记住,在洗牌时,可以应用随机算法来增加发现真相的复杂性。

数据安全

当企业做出非常关键的决策时,数据已经成为非常重要的资产。由于生成和使用这些数据的基础设施的复杂性,对这些数据的访问模式进行一些控制是非常重要的。在 Hadoop 生态系统中,我们有 Apache Ranger,这是另一个帮助管理大数据安全性的开源项目。

什么是 Apache 游侠?

Apache Ranger 是一个应用,它使数据架构师能够在大数据生态系统中实施安全策略。该项目的目标是为所有 Hadoop 应用提供一种统一的方式来遵守定义的安全准则。

以下是 Apache 游侠的一些特性:

  • 集中管理
  • 细粒度授权
  • 标准化授权
  • 多种授权方式
  • 集中审计

使用 Ambari 的 Apache Ranger 安装

在本节中,我们将使用 Apache Ambari 安装 Ranger。本节假设已经有一个正在运行的 Ambari 实例。

安巴里管理员用户界面

打开主节点上运行的 Ambari web 界面;然后点击添加服务,如截图所示:

这将打开一个模态窗口,添加服务向导,它将带我们完成 Apache Ambari 的完整安装的几个步骤。

adservice

模态窗口出现后,从列表中选择 Apache Ranger 服务,然后单击屏幕上的下一步。

这显示在下面的截图中:

服务安排

一旦选择了服务,我们将看到用户界面中的下一步,我们需要选择安装和运行该服务的服务器。

我已经为 Ranger 选择了节点-3(参见绿色标签):

Screenshot showing how to choose servers on which this services is going to be installed and run

之后,选择页面底部的下一步。

服务客户安置

在这一步中,我们可以选择安装该服务客户端的位置。使用复选框标记您的首选项。

它们看起来像这样:

做出选择后,单击下一步。

在母版上创建数据库

我们已经在主节点上安装了 MySQL 数据库服务器。在继续执行 Ambari 向导的下一步之前,我们必须创建一个新的数据库并分配一些权限:

我们还必须使用ambari-server setup命令注册 JDBC 驱动程序:

bash-$ sudo ambari-server setup --jdbc-db=mysql --jdbc-driver=/usr/share/java/mysql-connector-java.jar
Using python /usr/bin/python
Setup ambari-server
Copying /usr/share/java/mysql-connector-java.jar to /var/lib/ambari-server/resources
If you are updating existing jdbc driver jar for mysql with mysql-connector-java.jar. Please remove the old driver jar, from all hosts. Restarting services that need the driver, will automatically copy the new jar to the hosts.
JDBC driver was successfully initialized.
Ambari Server 'setup' completed successfully.

完成这一步后,我们可以回到安巴里向导。

Ranger 数据库配置

在向导中,系统会提示我们输入数据库名称、用户名和密码。请根据我们在上一步中所做的选择填写它们:

添加设置后,请单击测试连接。这样以后会节省很多时间。

如有错误,请返回上一步;查看是否有拼写错误,然后重新运行。

完成更改后,单击下一步。

配置更改

因为我们正在向 Ranger 添加一个服务,所以 Ambari 显示了 Ranger 正常工作所需的配置更改列表。大部分都是默认的。

这些更改看起来像下面的截图。一旦更改看起来不错,单击确定继续:

配置审查

在这一步中,我们将看到向导中到目前为止所做的更改列表,并看到打印更改摘要和部署游侠的选项。

只有当我们点击部署时,游侠软件才会被安装。在此之前,它都保存在浏览器缓存中。

屏幕如下所示:

部署进度

一旦游侠的安装开始,它应该看起来像截图中的样子。应该不会有任何故障,因为我们已经正确设置了所有配置。如果有任何故障,请检查日志并通过单击“上一步”按钮查看配置:

应用重启

部署完成后,我们需要重启所有受影响的 Hadoop 组件,如下图所示:

一旦所有组件都重新启动,安巴里仪表板看起来相当健康,我们已经完成了 Apache Ranger 的安装。

在下一步中,我们将看到如何使用 Apache Ranger 来处理我们的数据安全。

Apache 游侠用户指南

一旦 Apache Ranger 的部署完成,我们就可以使用 Apache Ranger 提供的网络界面来管理我们整个 Hadoop 基础架构的安全性。

登录到用户界面

如果您没有更改默认设置,Ranger 默认在非 SSL 模式下运行在端口6080上。在安装在端口6080 ( http://<server-ip>:6080)的服务器上打开一个网络浏览器,会出现如下屏幕提示:

使用默认用户名admin和密码admin登录(出于安全考虑,请在首次登录后更改密码)。

登录成功后,我们将进入访问管理器部分。

存取管理程序

访问管理器允许我们定义基于服务和标签的策略。此屏幕截图显示了服务的默认列表和配置的策略:

如您所见,已经为 HDFS 服务和 KAFKA 服务定义了策略,因为它们已经安装在 Ambari 设置中。

当我们想要定义一个新的服务时,我们可以点击+图标并定义服务细节。

服务详情

在我们开始定义服务的授权规则之前,我们需要定义一个服务,然后向该服务添加授权策略。这些是从用户界面定义服务所需的强制属性:

| 界面元素名称 | 描述 |
| 服务名称 | 代理配置中定义的服务名称 |
| 用户名 | 服务用户的名称 |
| 密码 | 服务用户的密码 |
| URL 名称代码 | 名称节点的网址 |

单击应用下方的+图标可以定义新服务(例如,HDFSKafka等)

之后,服务定义屏幕如下所示:

Screenshot of the service definition screen after defining new services

我们需要为我们的服务定义填写所有必要的值,然后点击保存。稍后,我们需要向该服务添加策略来执行访问和审核。

HDFS 的政策定义和审计

对于 Ranger 中的每个服务,我们可以将不同的策略与服务中的资源相关联。在 HDFS 的情况下,资源将是文件/目录路径。

在本节中,我们将为三个用户定义一个名为“项目”的 HDFS 路径新策略:hdfs-alicehdfs-bobhdfs-tom。其中只允许hdfs-alice所有权限,其余用户只有读取权限。

我们将看到一旦策略到位,Ranger 如何实施访问限制。

让我们看看创建策略的屏幕:

Screenshot showing how Ranger enforces access restrictions

一旦我们点击添加按钮,这个策略就被注册并添加到当前服务中。

现在,让我们回到 Unix 终端,看看 Ranger 是如何执行策略的。

该屏幕显示如何允许hdfshdfs-alice用户创建目录/projects/projects/1,但如何拒绝hdfs-tom:

Apache Ranger 在 web 界面中也有一个审计部分,在这里我们可以看到这些访问模式。

该屏幕显示策略拒绝hdfs-tom并授予hdfs-alice访问权限:

Screenshot showing access denied to hdfs-tom and access granted to hdfs-alice by the policy

像这样,我们可以定义自己的策略,并自定义hdfs应该如何允许/拒绝对多个资源的访问。

游侠的强大和灵活来自于它的可配置性。不需要任何配置文件和应用的重启,访问控制就能发挥重要作用。

摘要

在本章中,我们了解了不同的数据生命周期阶段,包括创建、共享、维护、归档、保留和删除数据的时间。

本章让您详细了解了大数据是如何管理的,考虑到它是非结构化或半结构化的,并且到达率快、容量大。

随着业务组织中生成和使用数据的基础架构的复杂性急剧增加,正确保护您的数据变得势在必行。本章进一步介绍了数据安全工具,如 Apache Ranger,以及帮助我们学习如何控制数据访问模式的模式。

在下一章中,我们将了解 Hadoop 安装、其体系结构和关键组件。

三、Hadoop 设计考虑

大数据不一定意味着大数据。如果数据集很小,分析它非常容易。我们可以将其加载到 Excel 电子表格中,并进行所需的计算。但是,随着数据量越来越大,我们必须找到其他方法来处理它。我们可能必须将其加载到一个 RDMBS 表中,并运行一个 SQL 查询来查找给定结构的趋势和模式。此外,如果数据集格式更改为类似电子邮件的格式,那么加载到关系数据库管理系统将成为一个巨大的挑战。更复杂的是,如果数据速度变得像实时一样,用传统的基于关系数据库管理系统的工具分析给定的数据集几乎是不可能的。在现代世界中,术语大数据可以用五个最著名的 V s 来表示,以下是每个 V 的简单解释。

在本章中,我们将涵盖以下主题:

  • 数据结构原理
  • 安装 Hadoop 集群
  • 探索 Hadoop 架构
  • 引入 Yarn
  • Hadoop 集群组合
  • Hadoop 文件格式

理解数据结构原理

让我们来看看一些重要的数据架构原则:

  • 数据是企业的资产:数据具有可衡量的价值。它为企业提供了一些真正的价值。在现代,数据被视为真金白银。
  • 数据在企业范围内共享:数据只被捕获一次,然后被多次使用和分析。多个用户针对不同的用例和需求访问相同的数据。
  • 数据治理:治理数据,保证数据质量。
  • 数据管理:需要对数据进行管理,以达到企业目标。
  • 数据访问:所有用户都要有数据访问权限。
  • 数据安全:数据要妥善保护。
  • 数据定义:数据的每个属性都需要在企业范围内统一定义。

既然我们已经了解了大数据的基础知识及其原理,让我们开始一些实际行动吧。

安装 Hadoop 集群

安装 Hadoop 集群需要执行以下步骤。在撰写本书时,Hadoop 2 . 7 . 3 版本是一个稳定的版本。我们会安装它。

  1. 使用以下命令检查 Java 版本:
Java -version
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
You need to have Java 1.6 onwards 
  1. 借助以下命令,在所有服务器上创建一个 Hadoop 用户帐户,包括所有名称节点和数据节点:
useradd hadoop
passwd hadoop1 

假设我们有四个服务器,我们必须使用所有四个服务器创建一个 Hadoop 集群。这四个服务器的 IPs 如下:192.168.11.1192.168.11.2192.168.11.3192.168.11.4。在这四个服务器中,我们将首先使用一个服务器作为主服务器(名称节点),其余所有服务器都将作为从服务器(数据节点)。

  1. 在名称节点和数据节点这两个服务器上,使用以下命令更改/etc/hosts文件:
vi /etc/hosts--   
  1. 然后将以下内容添加到所有服务器上的所有文件中:
NameNode 192.168.11.1
DataNode1 192.168.11.2
DataNode2 192.168.11.3
DataNode3 192.168.11.4 
  1. 现在,在名称节点和数据节点上设置 SSH:
su - hadoop
ssh-keygen -t rsa
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop@namenode
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop@datanode1
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop@datanode2
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop@datanode3
chmod 0600 ~/.ssh/authorized_keys
exit
  1. 在名称节点和所有数据节点上下载并安装 Hadoop:
mkdir /opt/hadoop
cd /opt/hadoop
wget http://www-eu.apache.org/dist/hadoop/common/hadoop-2.7.3/hadoop-2.7.3.tar.gz
tar -xvf hadoop-2.7.3.tar.gz 
mv Hadoop-2.7.3 hadoop
chown -R hadoop /opt/hadoop
cd /opt/hadoop/Hadoop

在名称节点上配置 Hadoop

登录到名称节点:

cd /opt/Hadoop/conf

vi core-site.xml  

使用这些值查找并更改以下属性:

| 文件名 | 物业名称 | 属性值 |
| core-site.xml | fs.default.name | hdfs://namenode:9000/ |
| | dfs.permissions | False |
| hdfs-site.xml | dfs.data.dir | /opt/hadoop/hadoop/dfs/namenode/data |
| | dfs.name.dir | /opt/hadoop/hadoop/dfs/namenode |
| | dfs.replication | 1 |
| mapred-site.xml | mapred.job.tracker | namenode:9001 |

    vi masters
    namenode

    vi slaves
    datanode1
    datanode2
    datanode3

格式名称模式

以下代码用于格式化名称节点:

 cd /opt/Hadoop/Hadoop/bin

    hadoop -namenode  -format 

启动所有服务

我们用以下代码行启动所有服务:

    ./start-all.sh

有关如何设置 Hadoop 单节点和多节点集群的详细信息,请使用以下链接:https://Hadoop . Apache . org/docs/r 2 . 7 . 0/Hadoop-project-dist/Hadoop-common/ClusterSetup . html

探索 HDFS 建筑

HDFS 建筑基于主模式和从模式。名称节点是主节点,所有数据节点都是从属节点。以下是关于这两个节点需要注意的一些要点。

定义名称节点

名称节点是 Hadoop 集群中所有数据节点的主节点。它只存储以树的形式存储的文件和目录的元数据。重要的一点是,除了元数据,NameNode 从不存储任何其他数据。名称节点跟踪以块的形式写入数据节点的所有数据。默认块大小为 256 兆字节(可配置)。没有名称节点,就无法读取数据节点文件系统上的数据。元数据使用两个文件本地存储在名称节点上——文件系统名称空间映像文件、FSImage 和编辑日志。FSImage 是从 NameNode 编辑日志开始的文件系统的快照—自 NameNode 启动以来文件系统的所有更改,当 NameNode 启动时,它读取 FSImage 文件并编辑日志文件。所有的事务(编辑)都被合并到 FSImage 文件中。将 FSImage 文件写入磁盘,并创建一个新的空编辑日志文件来记录所有编辑。由于名称节点不经常重新启动,编辑日志文件变得非常大,难以管理。重新启动名称节点时,需要很长时间才能重新启动,因为所有编辑都需要应用到 FSImage 文件。在名称节点崩溃的情况下,编辑日志文件中的所有元数据都不会写入 FSImage 文件,并且会丢失。

辅助名称节点

辅助名称节点的名称令人困惑。它不充当名称节点。它的主要功能是从名称节点获取文件系统更改,并定期将其合并到名称节点 FSImage。将编辑日志文件更改写入 FSImage 称为提交。定期提交有助于减少名称节点的启动时间。辅助名称节点也称为提交节点。

名称节点安全模式

这是 HDFS 群集的只读模式。不允许客户端对文件系统或块进行任何修改。在启动过程中,NameNode 自动以安全模式启动,对 FSImage 应用编辑,自动禁用安全模式,并以正常模式重新启动。

数据节点

数据节点是 Hadoop 集群的主力。它们的主要功能是以块的形式存储和检索数据。他们总是以心跳的形式向名称节点传达他们的状态。这就是名称节点跟踪任何数据节点的方式,无论它们是活的还是死的。数据节点保留已知数据块和复制因子的三个副本。数据节点与其他数据节点通信,以复制数据块来维护数据复制。

数据复制

HDFS 体系结构支持将非常大的文件放置在集群中的机器上。每个文件都存储为一系列块。为了确保容错,每个数据块将被复制三次到三台不同的机器上。它被称为复制因子,可以在群集级别或单个文件级别进行更改。它是一个命名节点,负责做出与数据块复制相关的所有决策。名称节点从每个数据节点获取心跳和块报告。心跳确保数据节点是活动的。块报告包含数据节点上所有块的列表。

机架感知

HDFS 数据块放置将通过将一个数据块副本放置在不同的机架上,使用机架感知来实现容错,如下图所示:

让我们详细了解一下图:

  • 第一个副本与发起请求的数据节点放在同一机架上,例如机架 1 和数据节点 1
  • 第二个副本放在另一个机架的任何数据节点上,例如,机架 2,数据节点 2
  • 第三个副本放置在同一机架的任何数据节点上,例如机架 2、数据节点 3

自定义机架拓扑脚本包含选择适当数据节点的算法,可以使用 Unix 外壳、Java 或 Python 开发。可通过更改Core-site.xml文件中的topology.script.file.name参数在集群上激活。

HDFS WebUI

下表显示了 HDFS 网络界面中的服务:

| 服务 | 协议 | 港口 | URL |
| WebUI 名称节点 | 超文本传送协议 | 50070 | http://namenode:50070/ |
| 数据节点网络界面 | 超文本传送协议 | 50075 | http://datanode:50075/ |
| 辅助名称节点 | 超文本传送协议 | 50090 | http://Snamenode:50090/ |

引入 Yarn

另一个资源协商者 ( Yarn)分离资源管理、调度和处理组件。它有助于实现集群资源的 100%资源利用率。Yarn 基于 Hadoop 调度策略管理集群的中央处理器和内存。Yarn 支持任何类型的应用,并不仅限于 MapReduce。它支持用任何类型的语言编写的应用,前提是可以在 Hadoop 集群上安装二进制文件。

Yarn 结构

让我们在接下来的章节中详细了解 Yarn 的架构。

资源管理程序

资源管理器负责跟踪集群中的资源并调度应用。资源管理器有两个主要组件:调度器和应用管理器。

节点管理器

节点管理器负责启动和管理节点上的容器。容器执行应用主服务器指定的任务。它充当资源管理器的从属。每个节点管理器跟踪其从属节点上的可用数据处理资源,并定期向资源管理器发送报告。Hadoop 集群中的处理资源消耗在称为容器的字节大小的块中。

Yarn 的结构

您可以执行以下步骤来配置 Yarn:

  1. 启动 Hadoop 名称节点、辅助名称节点和数据节点
  2. 年龄〔t0〕。

Find corresponding XML files based on your Hadoop installation.

  1. YARN_CONF_DIR的定义下增加以下内容:
export HADOOP_CONF_DIR="${HADOOP_CONF_DIR:-$YARN_HOME/etc/hadoop}"
export HADOOP_COMMON_HOME="${HADOOP_COMMON_HOME:-$YARN_HOME}"
export HADOOP_HDFS_HOME="${HADOOP_HDFS_HOME:-$YARN_HOME}"  
  1. 年龄〔t0〕:
<?xml version="1.0"?>
<configuration>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce.shuffle</value>
  </property>
  <property>
    <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
    <value>org.apache.hadoop.mapred.ShuffleHandler</value>
  </property>
</configuration> 
  1. 年龄〔t0〕:
<?xml version="1.0"?>
<?xml-stylesheet href="configuration.xsl"?>
<configuration>
  <property>
    <name>mapreduce.framework.name </name>
    <value>yarn</value>
  </property>
</configuration>  
  1. 启动 Yarn 服务:
yarn resourcemanager
yarn nodemanager 

配置 HDFS 高可用性

让我们看看 Hadoop 随着时间的推移带来的变化。

在 Hadoop 1.x 期间

Hadoop 1.x 从单个 NameNode 的架构开始。所有用于向该单一名称节点发送数据块报告的数据节点。架构中有一个辅助名称节点,但它的唯一职责是合并对 FSImage 的所有编辑。通过这种架构,名称节点成为了单点故障 ( SPOF )。由于它拥有 Hadoop 集群所有数据节点的所有元数据,因此在 NameNode 崩溃的情况下,Hadoop 集群在下次重启 NameNode 修复之前不可用。如果名称节点无法恢复,则所有数据节点中的所有数据都将完全丢失。如果关闭名称节点进行计划维护,HDFS 将无法正常使用。因此,有必要通过频繁备份名称节点文件系统来保护现有的名称节点,以最大限度地减少数据丢失。

在 Hadoop 2.x 和更高版本中

为了克服 HDFS 高可用性 ( HA )问题,让 NameNode 成为 SPOF,架构发生了变化。新的体系结构提供了在同一个群集中运行两个冗余名称节点的主动/被动配置和一个热备盘。这允许在机器崩溃的情况下快速故障切换到新的名称节点,或者出于计划维护的目的由管理员发起的正常故障切换。HDFS 高可用性提供了以下两种体系结构选项:

  • 使用共享存储
  • 使用仲裁日志管理器

使用 NFS 的 HDFS 高可用性集群

下图描述了 HDFS 高可用性集群使用 NFS 作为名称节点体系结构所需的共享存储:

重要的架构点

关于使用共享存储体系结构的 HDFS 高可用性,需要记住以下几点:

  • 在集群中,有两个独立的机器:活动状态名称节点和备用状态名称节点。
  • 在任何给定的时间点,只有一个名称节点处于活动状态,另一个处于待机状态。
  • 活动的名称节点管理来自集群中所有客户端数据节点的请求,而备用节点仍然是从属节点。
  • 所有数据节点都以这样的方式进行配置,即它们向活动和备用名称节点发送其数据块报告和心跳。
  • 备用名称节点保持其状态与活动名称节点同步。
  • 活动节点和备用节点都可以访问共享存储设备上的文件系统(例如,从 NAS 装载的 NFS)
  • 当客户端对文件系统进行任何更改时,活动的名称节点会对网络共享目录中的编辑日志文件进行相应的更改(编辑)。
  • 备用名称节点对其自己的名称空间进行所有相应的更改。这样,它与活动的名称节点保持同步。
  • 在活动名称节点不可用的情况下,备用名称节点会确保吸收共享网络目录中的所有更改(编辑),并将其自身提升为活动名称节点。
  • Hadoop 管理员应该对共享存储应用隔离方法,以避免在给定时间使两个名称节点都处于活动状态的情况。在发生故障切换时,隔离方法会切断对先前活动名称节点的访问,以便对共享存储进行任何更改,从而确保顺利故障切换到备用名称节点。之后,备用名称节点成为活动名称节点。

高可用性名称的配置具有共享存储的节点

hdfs-site.xml中添加以下属性:

| 属性 | |
| dfs.nameservices | cluster_name |
| dfs.ha.namenodes.cluster_name | NN1NN2 |
| dfs.namenode.rpc-address.cluster_name.NN1 | machine1:8020 |
| dfs.namenode.rpc-address.cluster_name.NN2 | machine2:8020 |
| dfs.namenode.http-address.cluster_name.NN1 | machine1:50070 |
| dfs.namenode.http-address.cluster_name.NN2 | machine2:50070 |
| dfs.namenode.shared.edits.dir | file:///mnt/filer1/dfs/ha-name-dir-shared |
| dfs.client.failover.proxy.provider.cluster_name | org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider |
| dfs.ha.fencing.methods | sshfence |
| dfs.ha.fencing.ssh.private-key-files | /home/myuser/.ssh/id_rsa |
| dfs.ha.fencing.methods | sshfence([[username][:port]]) |
| dfs.ha.fencing.ssh.connect-timeout | 30000 |

将以下属性添加到core-site.xml:

| 属性 | |
| fs.defaultFS | hdfs://cluster_name |

使用仲裁日志管理器的 HDFS 高可用性集群

下图描述了仲裁日志管理器 ( QJM )体系结构,用于在活动和备用名称节点之间共享编辑日志:

重要的架构点

关于使用 QJM 架构的 HDFS 高可用性,需要记住以下几点:

  • 在集群中,有两个独立的机器—活动状态名称节点和备用状态名称节点。
  • 在任何时间点,名称节点中恰好有一个处于活动状态,另一个处于待机状态。
  • 活动的名称节点管理来自集群中所有客户端数据节点的请求,而备用节点仍然是从属节点。
  • 所有数据节点都以这样的方式进行配置,即它们向活动和备用名称节点发送其数据块报告和心跳。
  • 活动节点和备用节点都通过与一组名为日志节点 ( JNs )的独立守护进程通信来保持彼此同步。
  • 当客户端进行任何文件系统更改时,活动的名称节点会将修改记录持久地记录到大多数这些 JNs 中。
  • 备用节点通过与 JNs 通信,立即将这些更改应用到自己的名称空间。
  • 在活动名称节点不可用的情况下,备用名称节点会确保吸收来自 JNs 的所有更改(编辑),并将自己提升为活动名称节点。
  • 为了避免在给定时间使两个名称节点都处于活动状态的情况,JNs 一次只允许一个名称节点作为编写器。这允许新的活动名称节点安全地进行故障转移。

带 QJM 的高可用性名称节点的配置

将以下属性添加到hdfs-site.xml:

| 属性 | |
| dfs.nameservices | cluster_name |
| dfs.ha.namenodes.cluster_name | NN1NN2 |
| dfs.namenode.rpc-address.cluster_name.NN1 | machine1:8020 |
| dfs.namenode.rpc-address.cluster_name.NN2 | machine2:8020 |
| dfs.namenode.http-address.cluster_name.NN1 | machine1:50070 |
| dfs.namenode.http-address.cluster_name.NN2 | machine2:50070 |
| dfs.namenode.shared.edits.dir | qjournal://node1:8485;node2:8485;node3:8485/cluster_name |
| dfs.client.failover.proxy.provider.cluster_name | org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider |
| dfs.ha.fencing.methods | sshfence |
| dfs.ha.fencing.ssh.private-key-files | /home/myuser/.ssh/id_rsa |
| dfs.ha.fencing.methods | sshfence([[username][:port]]) |
| dfs.ha.fencing.ssh.connect-timeout | 30000 |

将以下属性添加到core-site.xml:

| 属性 | |
| fs.defaultFS | hdfs://cluster_name |
| dfs.journalnode.edits.dir | /path/to/journal/node/local/datat |

自动故障转移

非常重要的是要知道,上述两种体系结构仅支持手动故障转移。为了实现自动故障转移,我们必须引入两个额外的组件 ZooKeeper quorum,以及ZKFailoverController(ZKFC)流程和更多的配置更改。

重要的架构点

  • 活动和备用的每个名称节点都运行 ZKFC 进程。
  • 名称节点的状态由 ZKFC 监控和管理。
  • ZKFC 定期 pings 其本地名称节点,以确保名称节点是活动的。如果它没有恢复 ping,它会将该名称节点标记为不健康。
  • 健康的名称节点持有一个特殊的锁。如果名称节点变得不健康,该锁将被自动删除。
  • 如果本地名称节点是健康的,并且 ZKFC 看到锁当前没有被任何其他名称节点持有,它将尝试获取锁。如果它成功获得了锁,那么它就赢得了选举。现在,该名称节点负责运行故障转移,以使其本地名称节点处于活动状态。

配置自动故障转移

将以下属性添加到hdfs-site.xml以配置自动故障转移:

| 属性 | |
| dfs.ha.automatic-failover.enabled | true |
| ha.zookeeper.quorum | zk1:2181zk2:2181zk3:2181 |

Hadoop 集群组合

众所周知,Hadoop 集群由主服务器和从服务器组成:主节点——管理基础设施,从节点——分布式数据存储和数据处理。边缘节点不是 Hadoop 集群的一部分。该机器用于与 Hadoop 集群交互。用户没有任何权限直接登录到任何主节点和数据节点,但是他们可以登录到边缘节点来运行 Hadoop 集群上的任何作业。EdgeNode 上没有存储任何应用数据。数据始终存储在 Hadoop 集群的数据节点上。根据在 Hadoop 集群上运行作业的用户数量,可以有多个 EdgeNode。如果有足够的硬件可用,最好将每个主节点和数据节点托管在单独的机器上。但是,在典型的 Hadoop 集群中,有三个主节点。

请注意,假设我们将 HBase 用作群集中的 NoSQL 数据存储。

典型的 Hadoop 集群

Hadoop 集群组合如下所示:

以下是一些需要考虑的硬件规格:

  • 名称节点和备用名称节点。
  • 内存需求取决于要创建的文件和数据块副本的数量。通常,建议命名节点至少使用 64 GB - 96 GB 内存。
  • 名称节点需要可靠的存储来托管 FSImage 和编辑日志。建议这些主节点至少有 4 TB - 6 TB 的 SAS 存储。为命名节点提供 RAID 5 - 6 存储是一个好主意。如果群集是高可用性群集,则规划您的 Hadoop 群集时,应在主节点上配置 JNs。

就处理器而言,建议至少有 2 个运行在 2 千兆赫的四核处理器,以处理主节点的消息流量。

  • 数据节点/从属节点每个节点应该至少有 64 GB 内存。建议通常每个 Hadoop 守护进程需要 2 GB - 3 GB 内存,如 DataNode、节点管理器 ZooKeeper 等;5 GB 用于操作系统和其他服务;每个 MapReduce 任务 5 GB - 8 GB。
  • 数据节点可能有商品存储,至少有 8 TB - 10 TB 磁盘存储,配备 7,200 转/分的 SATA 驱动器。硬盘配置应该在只是一堆磁盘 ( JBOD )。
  • 建议所有数据节点至少有 8 个处理器,即 2.5 千兆赫内核和 24 核处理器。
  • 建议每个机架内有 1gb 到 10gb 的网络连接。对于所有从节点,建议使用 1 GB 网络带宽,对于主节点,建议使用 10 GB 带宽。
  • 如果您计划将来扩展您的 Hadoop 集群,您也可以添加额外的机器。

请阅读以下来自 Hortonworks 和 Cloudera 的文章,以获取更多参考:

Hadoop 部署的最佳实践

以下是 Hadoop 部署应遵循的一些最佳实践:

  • 从小处着手:和其他软件项目一样,一个实现 Hadoop 也涉及风险和成本。设置一个四节点的小型 Hadoop 集群总是更好。这个小集群可以设置为概念验证 ( 概念验证)。在使用任何 Hadoop 组件之前,都可以将其添加到现有的 Hadoop POC 集群中作为技术证明 ( POT )。它允许基础设施和开发团队了解大数据项目需求。在成功完成 POC 和 POT 后,可以向现有集群添加额外的节点。
  • Hadoop 集群监控:要了解集群的健康状况,需要对 NameNode 和所有数据节点进行适当的监控。它有助于在节点出现问题时采取纠正措施。如果某项服务出现故障,及时采取行动有助于避免将来出现大问题。设置 Gangalia 和 Nagios 是配置警报和监控的流行选择。在 Hortonworks 集群、Ambari 监控和 Cloudera 集群的情况下,Cloudera (CDH)管理器监控可以很容易设置。
  • 自动化部署:使用 Puppet 或 Chef 等工具对于 Hadoop 部署至关重要。使用自动化工具而不是手动部署来部署 Hadoop 集群变得超级容易和高效。使用可用的工具/组件重视数据分析和数据处理。优先使用 Hive 或 Pig 脚本来解决问题,而不是编写繁重的自定义 MapReduce 代码。目标应该是少开发多分析。
  • 高可用性的实施:在决定高可用性基础设施和架构时,应仔细考虑需求和数据增长的任何增加。如果出现任何故障或崩溃,系统应该能够自我恢复或故障转移到另一个数据中心/站点。
  • 安全性:需要通过创建用户和组,并将用户映射到组来保护数据。设置适当的权限和强制使用强密码应该可以锁定每个用户组。
  • 数据保护:在将敏感数据移动到 Hadoop 集群之前,识别敏感数据至关重要。了解隐私政策和政府法规对于更好地识别和缓解合规暴露风险非常重要。

Hadoop 文件格式

在 Hadoop 中,有许多可用的文件格式。用户可以根据用例选择任何格式。每种格式在存储和性能方面都有特殊的功能。让我们详细讨论每种文件格式。

文本/CSV 文件

文本和 CSV 文件在 Hadoop 数据处理算法中非常常见。文件中的每一行都被视为新记录。通常,每行以 n 字符结束。这些文件不支持列标题。因此,在处理时,总是需要额外的一行代码来删除列标题。CSV 文件通常使用 GZIP 编解码器压缩,因为它们不支持块级压缩;这增加了更多的加工成本。不用说,它们不支持模式演化。

JSON

JSON 格式在所有现代编程语言中都变得非常流行。这些文件是集合名称/值对。JSON 格式通常用于数据交换应用,它被视为对象、记录、结构或数组。这些文件是文本文件,支持模式演化。从 JSON 文件中添加或删除属性非常容易。与文本/CSV 文件一样,JSON 文件不支持块级压缩。

序列文件

序列文件是由二进制键/值对组成的平面文件。它们在 MapReduce(https://wiki.apache.org/hadoop/MapReduce)中被广泛用作输入/输出格式。它们主要用于一系列 MapReduce 作业中的中间数据存储。序列文件很适合作为小文件的容器。如果 HDFS 有太多的小文件,可以将它们打包成一个序列文件,以提高文件处理效率。序列文件有三种格式:未压缩、记录压缩和块压缩键/值记录。序列文件支持块级压缩,但不支持模式演化。

欧罗欧欧欧罗欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧欧

Avro 是 Hadoop 社区中广泛使用的文件类型。它之所以受欢迎,是因为它有助于模式进化。它包含二进制格式的序列化数据。Avro 文件是可拆分的,支持块压缩。它包含数据和元数据。它使用一个单独的 JSON 文件来定义模式格式。当 Avro 数据存储在一个文件中时,它的模式也随之存储,这样文件以后可以被任何程序处理。如果读取数据的程序期望不同的模式,这很容易解决,因为两种模式都存在。

镶木地板

拼花地板以扁平柱状格式存储嵌套数据结构。在存储和性能方面,拼花比任何行级文件格式都更高效。Parquet 以面向列的方式存储二进制数据。在拼花格式中,新的列被添加到结构的末尾。Cloudera 主要支持 Impala 实现的这种格式,但最近变得非常流行。这种格式对于 SQL 查询很好,因为只有选择性的列被读取以降低输入输出成本,所以 SQL 查询从具有许多列的宽表中读取特定的列。

妖魔

ORC 文件是优化的记录列文件格式,是 RC 文件的扩展版本。这些非常适合压缩,并且最适合 Hive 读取、写入和处理数据以减少访问时间和存储空间时的 Hive SQL 性能。这些文件不支持真正的模式演化。它们主要由 Hortonworks 支持,不适合 Impala SQL 处理。

哪种文件格式更好?

答案是:这取决于你的用例。通常,选择文件格式的标准基于查询读取和查询写入性能。此外,这取决于您使用的 Hadoop 发行版。ORC 文件格式对于使用 Hortonworks 发行版的 Hive 和 Tez 来说是最好的,对于 Cloudera Impala 实现来说,建议使用拼花文件。对于涉及模式演化的用例,Avro 文件是最适合的。如果要使用 Sqoop 从 RDBMS 导入数据,文本/CSV 文件格式是更好的选择。对于存储地图中间输出,序列文件是最终选择。

摘要

在本章中,主要目的是了解各种 Hadoop 设计替代方案。说到 Hadoop 集群及其在典型生产环境中部署的最佳实践,我们学到了很多。我们从对 Hadoop 的基本了解开始,然后开始 Hadoop 配置、安装和 HDFS 架构。我们还了解了实现 HDFS 高可用性的各种技术。我们还研究了 Yarn 架构。最后,我们研究了各种文件格式,以及如何根据您的用例选择一种格式。

在下一章中,我们将看到如何将数据摄取到新创建的 Hadoop 集群中。

四、数据移动技术

在最后一章中,我们学习了如何创建和配置 Hadoop 集群、HDFS 体系结构、各种文件格式以及 Hadoop 集群的最佳实践。我们还学习了 Hadoop 高可用性技术。

因为我们现在知道如何创建和配置 Hadoop 集群,所以在本章中,我们将了解将数据摄入 Hadoop 集群的各种技术。我们知道 Hadoop 的优势,但是现在,我们需要 Hadoop 集群中的数据来利用它的真正力量。

数据摄取被认为是 Hadoop 数据生命周期的第一步。数据可以以批处理或(实时)记录流的形式被吸收到 Hadoop 中。Hadoop 是一个完整的生态系统,MapReduce 是 Hadoop 的批处理生态系统。

下图显示了各种数据摄取工具:

在接下来的几节中,我们将详细了解每种工具。

在本章中,我们将介绍以下在 Hadoop 集群之间传输数据的方法:

  • Apache Sqoop
  • ApacheFlume
  • Apache 尼菲
  • Apache 卡夫卡连接

批处理与实时处理

在我们深入研究不同的数据摄取技术之前,让我们讨论一下批处理和实时(流)处理之间的区别。下面解释这两种生态系统的区别。

成批处理

以下各点描述了批处理系统:

  • 在处理大量数据时非常高效。
  • 所有数据处理步骤(即数据收集、数据接收、数据处理和结果呈现)都作为一个单独的批处理作业来完成。
  • 吞吐量比延迟更重要。延迟总是超过一分钟。
  • 吞吐量直接取决于数据的大小和可用的计算系统资源。
  • 可用的工具包括 Apache Sqoop、MapReduce 作业、Spark 作业、Hadoop DistCp 实用程序等。

实时处理

以下几点描述了实时处理与批处理的区别:

  • 延迟非常重要,例如,不到一秒钟
  • 计算相对简单
  • 数据作为一个独立的单元进行处理
  • 可用的工具包括 Apache Storm、Spark Streaming、Apache Fink、Apache Kafka 等

Apache Sqoop

Apache Sqoop 是一种工具,旨在高效地在 Hadoop 集群和结构化数据存储(如关系数据库)之间传输大量数据。在典型的用例中,例如数据湖,总是需要将数据从基于关系数据库管理系统的数据仓库存储导入到 Hadoop 集群中。在数据导入和数据聚合之后,需要将数据导出回 RDBMS。Sqoop 允许从结构化数据存储(如关系数据库管理系统、企业数据仓库和 NoSQL 系统)轻松导入和导出数据。在 Sqoop 的帮助下,数据可以从外部系统调配到 Hadoop 集群中,并填充 Hive 和 HBase 中的表。Sqoop 使用基于连接器的架构,该架构支持插件来提供与外部系统的连接。在内部,Sqoop 使用 MapReduce 算法来导入和导出数据。默认情况下,所有 Sqoop 作业运行四个地图作业。在接下来的几节中,我们将详细了解 Sqoop 的导入和导出功能。

Sqoop 导入

下图显示了将数据从关系数据库管理系统表导入到 Hadoop 集群的 Sqoop 导入功能:

进口到 HDFS

以下是将数据导入 HDFS 的示例命令:

$sqoop import -connect jdbc:mysql://localhost/dbname -table <table_name>   --username <username> --password >password> -m 4

导入分两步完成,如下所示。

  1. Sqoop 扫描数据库并收集要导入的表元数据
  2. Sqoop 提交一个仅映射作业,并使用必要的元数据传输实际数据

导入的数据保存在 HDFS 文件夹中。用户可以指定替代文件夹。导入的数据根据导入的表保存在 HDFS 的一个目录中。与 Sqoop 操作的大多数方面一样,用户可以指定任何替代目录来填充文件。通过显式指定字段分隔符和记录终止符,可以轻松覆盖数据复制的格式。用户可以使用不同的格式,如 Avro、ORC、Parquet、序列文件、文本文件等,将文件存储到 HDFS,例如,将 MySQL 表导入 HDFS。下面是一个将 MySQL 表导入 HDFS 的示例:

$ mysql>  create database  sales;
$ mysql>  use sales;
$  mysql>   create table customer 
 (cust_num int not null,cust_fname  varchar(30),cust_lname varchar     (30),cust_address  varchar (30),cust_city varchar (20),cust_state      varchar (3), cust_zip  varchar (6),primary key (cust_num));
$ ctrl-C   -- to exit from MySQL

在命令提示符下,运行以下sqoop命令导入 MySQL 销售数据库表customer:

$  sqoop import --connect jdbc:mysql://127.0.0.1:3306/sales --username root --password hadoop --table customer  --fields-terminated-by ","  --driver com.mysql.jdbc.Driver --target-dir /user/data/customer

验证 HDFS 的customer文件夹,如下所示:

$ hadoop fs -ls /user/data/customerFound 5 items-rw-r--r--   1 root hdfs          0 2017-04-28 23:35 /user/data/customer/_SUCCESS-rw-r--r--   1 root hdfs        154 2017-04-28 23:35 /user/data/customer/part-m-00000-rw-r--r--   1 root hdfs         95 2017-04-28 23:35 /user/data/customer/part-m-00001-rw-r--r--   1 root hdfs         96 2017-04-28 23:35 /user/data/customer/part-m-00002-rw-r--r--   1 root hdfs        161 2017-04-28 23:35 /user/data/customer/part-m-00003

让我们创建一个外部 Hive 表来验证记录,如下面的代码片段所示:

$ hive$hive > CREATE EXTERNAL TABLE customer_H 
(cust_num int,cust_fname  string,cust_lname  string,cust_address string,  cust_city  string,cust_state  string,cust_zip   string) 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','LINES TERMINATED BY 'n'LOCATION '/user/data/customer';
$hive> select * from customer_H; 

| 卡斯特姆 | 成本 Fname | cust lname | 客户地址 | 城市 | 状态 | zip |
| one | 詹姆斯 | 屁股 | 北蓝胶街 6649 号 | 新奥尔良 | 在那里 | Seventy thousand one hundred and sixteen |
| Two | 艺术 | Venere 8 | W Cerritos 鸟#54 | 布里奇波特 | 新泽西州 | Eight thousand and fourteen |
| three | 莉娜?莉娜 | 爸爸的外套 | 主街 639 号 | 安克雷奇 | 阿拉斯加 | Ninety-nine thousand five hundred and one |
| four | 唐娜蒂 | folder(文件夹) | 中心街 34 号 | 哈密尔顿 | 俄亥俄州 | Forty-five thousand and eleven |
| five | 新美乐股份公司 | 莫拉斯卡 | 3 Mcauley 博士 | 亚什兰 | 俄亥俄州 | Forty-four thousand eight hundred and five |
| six | 御津... | 托尔纳 | 7 Eads St | 芝加哥 | 伊利诺伊 | Sixty thousand six hundred and thirty-two |
| seven | 利奥塔 | -比利 | 杰克逊大道西 7 号 | 圣何塞 | 加拿大 | Ninety-five thousand one hundred and eleven |
| eight | 明智的 | 维什尔 | 波士顿大道 5 号 88 | 苏福尔斯 | 南达科他州 | Fifty-seven thousand one hundred and five |
| nine | 克里斯 | 结婚的人 | 228 Runamuck Pl 编号 2808 | 巴尔的摩 | 医学博士 | Twenty-one thousand two hundred and twenty-four |
| Ten | 米娜 | 哥们儿 | 杰罗德大街 2371 号 | 煤炭村 | 和 | Nineteen thousand four hundred and forty-three |

以下是将 MySQL 表导入到 Hive 的示例:

$ sqoop import --connect jdbc:mysql://127.0.0.1:3306/sales --username root --password hadoop --table customer  --driver com.mysql.jdbc.Driver --m 1 --hive-import  --hive-table customor_H

验证表格配置单元:

$hive$use default;
$ show tables;

您将看到customer_H表是在默认数据库下创建的。如果要在不同的数据库下创建customer_H表,例如销售数据库,必须提前创建销售数据库。另外,您必须将-hive-table参数更改为--hive-table销售cutomer_H增量负载(仅插入)。典型的数据加载要求是只加载源表中发生的增量更改。假设一个新客户11被插入到源customer MySQL 表中:

insert into customer values (11,'Abel','Maclead','25 E 75th St #69','Los Angeles','CA','90034');

为了只容纳新记录(即客户 11),我们必须在原始的sqoop命令中添加一些额外的参数。新的sqoop命令如下:

sqoop import --connect jdbc:mysql://127.0.0.1:3306/sales --username root --password hadoop --table customer  --driver com.mysql.jdbc.Driver --incremental append --check-column cust_num 
      --last-value 10 
    --m 1 --split-by cust_state --target-dir /user/data/customer

运行此命令后,Sqoop 将只拾取新行(即cust_num,也就是11):

$hive> select * from  customer_H;

| 卡斯特姆 | 成本 Fname | cust lname | 客户地址 | 城市 | 状态 | zip |
| one | 詹姆斯 | 屁股 | 北蓝胶街 6649 号 | 新奥尔良 | 在那里 | Seventy thousand one hundred and sixteen |
| Two | 艺术 | Venere 8 | W Cerritos 鸟#54 | 布里奇波特 | 新泽西州 | Eight thousand and fourteen |
| three | 莉娜?莉娜 | 爸爸的外套 | 主街 639 号 | 安克雷奇 | 阿拉斯加 | Ninety-nine thousand five hundred and one |
| four | 唐娜蒂 | folder(文件夹) | 中心街 34 号 | 哈密尔顿 | 俄亥俄州 | Forty-five thousand and eleven |
| five | 新美乐股份公司 | 莫拉斯卡 | 3 Mcauley 博士 | 亚什兰 | 俄亥俄州 | Forty-four thousand eight hundred and five |
| six | 御津... | 托尔纳 | 7 Eads St | 芝加哥 | 伊利诺伊 | Sixty thousand six hundred and thirty-two |
| seven | 利奥塔 | -比利 | 杰克逊大道西 7 号 | 圣何塞 | 加拿大 | Ninety-five thousand one hundred and eleven |
| eight | 明智的 | 维什尔 | 波士顿大道 5 号 88 | 苏福尔斯 | 南达科他州 | Fifty-seven thousand one hundred and five |
| nine | 克里斯 | 结婚的人 | 228 Runamuck Pl 编号 2808 | 巴尔的摩 | 医学博士 | Twenty-one thousand two hundred and twenty-four |
| Ten | 米娜 | 哥们儿 | 杰罗德大街 2371 号 | 煤炭村 | 和 | Nineteen thousand four hundred and forty-three |
| Eleven | 亚伯 | 麦克劳德 | 东 75 街 25 号 69 号 | 洛杉矶 | 加拿大 | Ninety thousand and thirty-four |

对于增量负载,我们不能直接使用 Sqoop 导入来更新数据。

请按照给定链接中的步骤进行行级更新: 现在,我们来看一个将 MySQL 表的子集导入 Hive 的例子。下面的命令展示了如何将 MySQL 中的customer表的一个子集导入到 Hive 中。例如,我们有State = "OH"的仅导入客户数据:

$ sqoop import --connect jdbc:mysql://127.0.0.1:3306/sales --username root --password hadoop --table sales.customer  --driver com.mysql.jdbc.Driver --m 1 --where "city = 'OH' --hive-import  --hive-table customer_H_1$ hive> select * from customer_H_1;

| 卡斯特姆 | 成本 Fname | cust lname | 客户地址 | 城市 | 状态 | zip |
| four | 唐娜蒂 | folder(文件夹) | 中心街 34 号 | 哈密尔顿 | 俄亥俄州 | Forty-five thousand and eleven |
| five | 新美乐股份公司 | 莫拉斯卡 | 3 Mcauley 博士 | 亚什兰 | 俄亥俄州 | Forty-four thousand eight hundred and five |

将 MySQL 表导入到 HBase 表中

以下是将数据导入到 HBase 表的示例命令:

$sqoop import -connect jdbc:mysql://localhost/dbname -table <table_name>  --username <username> --password >password>  --hive-import -m 4                                    --hbase-create-table --hbase-table <table_name>--column-family <col family name>

Sqoop 将数据导入 HBase 表列族。数据被转换并以 UTF 8 字节格式插入。

Sqoop 导出

下图显示了从 Hadoop 集群中导出数据的 Sqoop 导出功能:

额外的业务功能可能需要在类似数据湖的用例中处理的数据。Sqoop 可用于将数据从 HDFS 或从 Hive 表中导出回 RDBMS。在将数据导出回 RDBMS 表的情况下,目标表必须存在于 MySQL 数据库中。HDFS 文件中的行或配置单元表中的记录作为sqoop命令的输入,被称为目标表中的行。这些记录被读取并解析成一组记录,并用用户指定的分隔符进行分隔。

以下是将数据从 HDFS 导出到 MySQL 表的命令。让我们在 MySQL 中创建一个表来存储从 HDFS 导出的数据:

$ mysql>  use sales;$  mysql>   create table customer_export (      cust_num int not null,      cust_fname  varchar(30),      cust_lname varchar (30),      cust_address  varchar (30),      cust_city varchar (20),      cust_state  varchar (3),      cust_zip  varchar (6),      primary key (cust_num));

$  sqoop export --connect jdbc:mysql://127.0.0.1:3306/sales --driver com.mysql.jdbc.Driver --username root --password hadoop --table customer_exported  --export-dir /user/data/customer

--table参数指定将要填充的表格。Sqoop 分割数据,并使用单独的地图任务将分割结果推送到数据库中。每个地图任务都执行实际的数据传输。--export-dir <directory h>是导出数据的目录:

$ mysql>  use sales;$ mysql>  select * from customer_exported;

| 卡斯特姆 | 成本 Fname | cust lname | 客户地址 | 城市 | 状态 | zip |
| one | 詹姆斯 | 屁股 | 北蓝胶街 6649 号 | 新奥尔良 | 在那里 | Seventy thousand one hundred and sixteen |
| Two | 艺术 | Venere 8 | W Cerritos 鸟#54 | 布里奇波特 | 新泽西州 | Eight thousand and fourteen |
| three | 莉娜?莉娜 | 爸爸的外套 | 主街 639 号 | 安克雷奇 | 阿拉斯加 | Ninety-nine thousand five hundred and one |
| four | 唐娜蒂 | folder(文件夹) | 中心街 34 号 | 哈密尔顿 | 俄亥俄州 | Forty-five thousand and eleven |
| five | 新美乐股份公司 | 莫拉斯卡 | 3 Mcauley 博士 | 亚什兰 | 俄亥俄州 | Forty-four thousand eight hundred and five |
| six | 御津... | 托尔纳 | 7 Eads St | 芝加哥 | 伊利诺伊 | Sixty thousand six hundred and thirty-two |
| seven | 利奥塔 | -比利 | 杰克逊大道西 7 号 | 圣何塞 | 加拿大 | Ninety-five thousand one hundred and eleven |
| eight | 明智的 | 维什尔 | 波士顿大道 5 号 88 | 苏福尔斯 | 南达科他州 | Fifty-seven thousand one hundred and five |
| nine | 克里斯 | 结婚的人 | 228 Runamuck Pl 编号 2808 | 巴尔的摩 | 医学博士 | Twenty-one thousand two hundred and twenty-four |
| Ten | 米娜 | 哥们儿 | 杰罗德大街 2371 号 | 煤炭村 | 和 | Nineteen thousand four hundred and forty-three |

水道

Flume 是一种可靠、可用且分布式的服务,可高效地收集、聚合和传输大量日志数据。它具有基于流式数据流的灵活简单的体系结构。Apache Flume 的当前版本是 1.7.0,于 2016 年 10 月发布。

ApacheFlume 建筑

下图描述了 ApacheFlume 的结构:

让我们仔细看看 ApacheFlume 架构的组件:

  • 事件:事件是带有可选字符串头的字节有效载荷。它表示 Flume 可以从其源传送到目的地的数据单位。
  • :事件从源到目的地的传输被认为是数据流,或者只是流。
  • 代理:托管 Flume 组件的独立过程,如源、通道和 Flume。因此,它能够接收、存储事件并将事件转发到下一跳目的地。
  • 源码:源码是一个接口实现。它能够在特定机制的帮助下消费交付给它的事件。
  • 渠道:是一个商店,事件通过在代理内运行的来源传递到渠道。放置在通道中的事件会一直保留在通道中,直到接收器将其取出进行进一步传输。渠道在确保这一点上发挥着重要作用。
  • Sink :是接口实现,和源码一样。它可以从通道中移除事件,并将它们传输到流中的下一个代理,或者传输到其最终目的地。
  • 拦截器:它们帮助改变一个正在进行的事件。可以根据选择的标准删除或修改事件。拦截器是实现org.apache.flume.interceptor.Interceptor接口的类。

使用 Flume 的数据流

整个 Flume 代理在一个 JVM 进程中运行,该进程包括所有组件(源、通道和接收器)。Flume 源从外部源接收事件,如网络服务器、外部文件等。信源将事件推送到信道,信道存储事件,直到被信宿接收。通道将有效负载(消息流)存储在本地文件系统或内存中,具体取决于源的类型。例如,如果源是文件,则有效负载存储在本地。接收器从信道获取有效负载,并将其推送到外部数据存储。代理内的源和接收器异步运行。有时,Flume 可能会将有效载荷推送到另一个 Flume 代理。我们将在下一节讨论这个场景。

Flume 复杂数据流架构

在下面的体系结构中,有三个源(服务器)。为了从存储在这些服务器上的日志文件中提取数据,我们必须在每台服务器上安装 Flume 软件。安装后,文件名需要添加到flume.conf文件中。Flume 从文件中收集所有数据,并通过通道将其推送到相应的接收器。上述架构中有多个接收器;Hive HDFS,和另一个 Flume,它连接到另一个服务器上安装的另一个 Flume 代理。它将数据从接收器推送到源,并将数据写入卡森德拉数据存储。

请注意,这不是一个好的架构,但我提到它是为了解释 FlumeFlume 和 Flume 源如何连接。

下图显示了涉及多个代理的复杂数据流:

Flume 设置

Flume 代理配置存储在本地文本文件中。请参考本书代码库中的示例 Flume 代理配置文件。Flume 1.7.0 支持各种源和汇。广泛使用的 Flume 来源(总结)如下:

| 来源 | 描述 |
| 我有消息来源 | 侦听 Avro 端口并从外部 Avro 客户端流接收事件 |
| 执行源 | 运行一个给定的 Unix 命令,并期望该过程能够持续地以标准输出方式产生数据 |
| 假脱机目录源 | 从磁盘上的文件中获取数据 |
| Taildir 源 | 在文件中检测到新行后,几乎实时跟踪文件 |
| 卡夫卡来源 | 阅读卡夫卡主题的信息 |
| 系统日志源 | 读取系统日志数据(支持系统日志-TCP 和系统日志-UDP) |
| HTTP 源 | 通过 HTTP POSTGET接受 Flume 事件 |

广泛使用的 Flume 可总结如下:

| Flume | 描述 |
| 阿夫罗锌 | 事件被转换为 Avro 事件,并发送到配置的主机名/端口对 |
| HDFS Flume | 将事件写入 HDFS |
| HiveFlume | 将文本或 JSON 数据写入 Hive 表 |
| 氢碱锌 | 将数据写入 HBase |
| 吗啡太阳沉 | 几乎实时地将其加载到 Apache Solr 服务器中 |
| Elasticsearch sink | 将数据写入弹性搜索集群 |
| 卡夫卡 Flume | 将数据写入卡夫卡的主题 |

广泛使用的 Flume 渠道(总结)如下:

| 通道 | 描述 |
| JDBC 海峡 | 事件存储在数据库支持的存储中 |
| 卡夫卡频道 | 事件存储在卡夫卡集群中 |
| 文件通道 | 事件存储在文件中 |
| 可溢出存储通道 | 事件存储在内存中;如果内存已满,则存储在磁盘上 |

广泛使用的 Flume 拦截器可总结如下:

| 拦截器 | 描述 |
| 时间戳拦截器 | 将事件的处理时间添加到事件标题中 |
| 主机拦截器 | 添加代理的主机名 |
| 搜索并替换拦截器 | 支持 Java 正则表达式 |
| Regex 过滤拦截器 | 根据正则表达式过滤事件 |
| Regex 提取拦截器 | 提取并追加匹配的正则表达式组作为事件的头 |

日志聚合用例

在日常业务场景中,我们总是发现需要获取日志文件并从中获取意义。例如,我们总是发现需要从不同的应用和服务器获取日志,并将它们合并在一起以发现趋势和模式。让我进一步扩展这个例子。让我们假设我们有五个 web 服务器部署在五个不同的服务器上。我们希望获得所有五个网络服务器日志,并将它们合并/聚合在一起,通过将一个副本存储在 HDFS,另一个副本将被发送到卡夫卡主题进行实时分析来进一步分析它们。问题是我们如何设计基于 Flume 的日志聚合架构。以下是我们的网络服务器日志聚合场景的 Flume 体系结构:

让我们详细介绍一下架构:总共有五个 web 服务器。每个 web 服务器生成一个日志文件,并将其存储在本地。Flume 代理安装在每个网络服务器上。Flume 代理只不过是一个(JVM)进程,它承载着事件从外部源流向下一个目的地(跳)的组件。每个 Flume 代理基于flume.conf的本地配置访问日志文件。每个 Flume 代理读取日志文件并将数据推送到 Flume 收集器。日志文件的每一行都被视为一条消息(有效负载)。Flume 收集器从所有 web 服务器获取消息,对所有消息进行筛选和聚合,并将这些消息推送到数据存储。以下是 Flume 剂和收集器剂flume.conf的样品flume.conf:

## Sample Flume Agent Configuration  
## This conf file should deploy on each webserver 
##   

a1.sources = apache 
a1.sources.apache.type = exec 
a1.sources.apache.command = gtail -F /var/log/httpd/access_log 
a1.sources.apache.batchSize = 1 
a1.sources.apache.channels = memoryChannel 

a1.channels = memoryChannel 
a1.channels.memoryChannel.type = memory 
a1.channels.memoryChannel.capacity = 100 

## Collector Details 

a1.sinks = AvroSink 
a1.sinks.AvroSink.type = avro 
a1.sinks.AvroSink.channel = memoryChannel 
a1.sinks.AvroSink.hostname = 10.0.0.10 
a1.sinks.AvroSink.port = 6565 

收集器flume.conf文件如下:


## Collector get data from all agents 

collector.sources = AvroIn 
collector.sources.AvroIn.type = avro 
collector.sources.AvroIn.bind = 0.0.0.0 
collector.sources.AvroIn.port = 4545 
collector.sources.AvroIn.channels = mc1 mc2 

collector.channels = mc1 mc2 
collector.channels.mc1.type = memory 
collector.channels.mc1.capacity = 100 

collector.channels.mc2.type = memory 
collector.channels.mc2.capacity = 100 

## Write copy to Local Filesystem (Debugging) 
# http://flume.apache.org/FlumeUserGuide.html#file-roll-sink 
collector.sinks.LocalOut.type = file_roll 
collector.sinks.LocalOut.sink.directory = /var/log/flume 
collector.sinks.LocalOut.sink.rollInterval = 0 
collector.sinks.LocalOut.channel = mc1 

## Write to HDFS 
collector.sinks.HadoopOut.type = hdfs 
collector.sinks.HadoopOut.channel = mc2 
collector.sinks.HadoopOut.hdfs.path = /flume/events/%{log_type}/%{host}/%y-%m-%d 
collector.sinks.HadoopOut.hdfs.fileType = DataStream 
collector.sinks.HadoopOut.hdfs.writeFormat = Text 
collector.sinks.HadoopOut.hdfs.rollSize = 0 
collector.sinks.HadoopOut.hdfs.rollCount = 10000 
collector.sinks.HadoopOut.hdfs.rollInterval = 600 

Apache 尼菲

什么是 Apache NiFi?在任何组织中,我们都知道有各种各样的系统。一些系统生成数据,而其他系统使用这些数据。Apache NiFi 是为了自动化从一个系统到另一个系统的数据流而构建的。Apache NiFi 是一个数据流管理系统,附带一个 web UI,帮助实时构建数据流。它支持基于流的编程。图形编程包括一系列数据移动通过的节点和边。在 NiFi 中,这些节点被转换成处理器,边缘被转换成连接器。数据存储在一个名为流文件的信息包中。该流文件包括内容、属性和边缘。作为用户,您使用连接器将处理器连接在一起,以定义应该如何处理数据。

Apache NiFi 的主要概念

下表描述了 Apache NiFi 的主要组件:

| 组件名称 | 描述 |
| 流文件 | 在系统中运行的数据包 |
| 流文件处理器 | 执行数据路由、转换和数据移动的实际工作 |
| connection-连线 | 处理器之间的实际数据链接 |
| 流量控制器 | 促进处理器之间的流文件交换 |
| 流程组 | 数据输入和数据输出处理器的特定组 |

Apache NiFi 体系结构

下图显示了 Apache NiFi 架构的组件(来源:https://nifi.apache.org/docs.html):

这些组件如下:

  • 网络服务器:这里托管了 NiFi 基于 HTTP 的 UI
  • 文件控制器:提供线程,管理扩展运行的时间表
  • 扩展:扩展在 JVM 中运行和执行
  • 文件流存储库:这将跟踪它所知道的当前在流中活动的给定流文件的状态
  • 内容存储库:这是给定流文件的实际内容字节所在的地方
  • 起源存储库:这里存储了所有的起源事件数据

主要特征

以下是 Apache NiFi 的主要特性:

  • 保证交付:在 NiFi 中出现数据量增加、电源故障以及网络和系统故障的情况下,有必要对数据进行可靠的保证交付。在数据流系统中,NiFi 确保了 NiFi 和数据之间的事务性通信,数据到达数据被传递到的点。
  • 带背压和压力释放的数据缓冲:在任何数据流中,可能涉及的系统存在一些问题;有些可能会停机,有些可能会变慢。在这种情况下,数据缓冲对于处理进出数据流的数据变得非常重要。

NiFi 支持在所有队列达到特定的数据限制和年限时,使用背压对其进行缓冲。NiFi 以最大可能的吞吐率做到这一点,同时保持良好的响应时间。

  • 优先排队:一般情况下,数据队列保持自然顺序或插入顺序。但是,很多时候,当数据插入速度快于带宽时,您必须优先从队列中检索数据。默认值是最早的数据优先。但是 NiFi 支持根据大小、时间等对队列进行优先级排序,即先最大后最新。
  • 流量特定的服务质量(QoS) :有些情况下,我们必须在特定的时间段内处理数据,比如一秒钟之内等等,否则数据就失去了价值。这些关注点的特定配置的细粒度流是由 Apache NiFi 实现的。
  • 数据起源:当对象流过系统时,NiFi 自动记录、索引并提供可用的起源数据——甚至跨扇入、扇出、转换等等。这些信息对于支持法规遵从性、故障排除、优化和其他场景变得极其重要。
  • 可视化指挥控制 : Apache NiFi 允许用户对数据流进行交互管理。它为数据流的每一个变化提供即时反馈。因此,用户理解并立即纠正数据流中的任何问题、错误或问题。基于数据流的分析结果,用户可以对其数据流进行更改,对队列进行优先级排序,添加更多数据流,等等。
  • 流程模板:可以开发、设计和共享数据流。模板允许主题专家构建和发布他们的流程设计,并让其他人从中受益和协作。
  • 扩展 : NiFi 允许我们扩展它的关键组件。
  • 扩展点:处理器、控制器服务、报告任务、优先级排序器和客户用户界面。
  • 多角色安全:可以对每个组件应用多粒度、多角色的安全,让管理员用户拥有细粒度的访问控制。
  • 聚类 : NiFi 旨在通过将多个节点聚集在一起的方式进行横向扩展。这样,它可以通过向集群添加更多节点来处理更多数据。

关于 Apache NiFi 的入门,请使用以下链接:https://NiFi . Apache . org/docs/NiFi-docs/html/入门. html

让我们想象一个场景。我有一个运行日志文件。它是动态更新的。我想根据文件的内容捕获并监控其中的每一行。我想发给我的卡夫卡经纪人。我还想把我所有的错误记录送到 HDFS 存档和进一步分析。不同的线路类型会发给不同的卡夫卡经纪人。例如,错误、信息和成功类型将被发送到三个不同的卡夫卡主题,即错误、信息和成功。为此,我开发了以下 NiFi 工作流。下表给出了每个处理器的详细说明:

| 处理器 | 目的 | 属性 | |
| TailFile | 跟踪日志文件 | 文件到尾部 | /var/log/apache.log |
| 拆分文本 | 要将日志条目拆分为行 | 行分割计数 | 1 |
| 路由内容 | 做出路由决定 | | |
| PutHDFS | 向 HDFS 发送错误 | | HDFS 详情 |
| 出版卡夫卡 | 向卡夫卡主题传递数据 | 经纪人和主题名称 | 主机名:端口,主题窗格 |

实时日志捕获数据流

以下示例工作流显示了如何将日志文件数据推送到 HDFS,然后再移动到卡夫卡经纪人那里:

卡夫卡连接

卡夫卡连接是 Apache 卡夫卡的一部分。它是一个使用连接器将数据从一个系统摄取到另一个系统的框架。连接器有两种类型:源连接器和接收器连接器。接收器连接器从源系统导入数据,并写入卡夫卡主题。接收器连接器从卡夫卡主题中读取数据,并将其导出到目标系统。Kafka Connect 提供开箱即用的各种信号源和接收器连接器。

卡夫卡连接——简史

Kafka Connect 主要于 2015 年 11 月在 Kafka 0.9.x 中推出,除了 Kafka 0.9.x 的各种功能外,Connect APIs 还是一个全新的功能。随后,2016 年 5 月,新版卡夫卡 0.10.0 发布。在那个版本中,卡夫卡流应用编程接口是一个新的和令人兴奋的功能。但是,在 2017 年 3 月,正是卡夫卡版本 0.10.2,卡夫卡连接获得了真正的动力。作为 Kafka 0.10.2 的一部分,发布了改进的简化连接 API 和单消息转换 API。

为什么是卡夫卡连接?

卡夫卡连接有助于简化进出卡夫卡的数据。它提供了很多现成的连接器。在我看来,这是对像我这样的开发人员最好的激励,因为我不必开发单独的代码来开发自己的连接器来导入和导出数据;为此,我总是可以重用现成的连接器。另外,如果我愿意,我总是可以使用 Kafka Connect APIs 开发自己独特的连接器。此外,所有连接器都是基于配置的。常见的来源和目标是数据库、搜索引擎、NoSQL 数据存储以及像 SAP、GoldenGate、Salesforce、HDFS、Elasticsearch 等应用。有关所有可用来源和连接器的详细列表,请参考https://www.confluent.io/product/connectors/

卡夫卡连接功能

以下是卡夫卡连接的一些特征:

  • 可扩展:这是一个在 Apache Kafka 和其他系统之间实现可扩展和可靠的流数据的框架
  • 简单:这使得定义将大量数据移入和移出卡夫卡的连接器变得简单
  • 偏移管理:框架完成了正确记录连接器偏移的大部分工作
  • 简单操作:这个服务有一个 RESTful API,用于管理和部署连接器
  • 分布式:框架可以集群化,会自动在集群间分配连接器,保证连接器始终运行
  • 开箱即用连接器:关于所有可用来源和连接器的详细列表,请参考https://www.confluent.io/product/connectors/

卡夫卡连接建筑

下图展示了卡夫卡连接架构:

卡夫卡集群由卡夫卡经纪人组成:三个经纪人,如图所示。来源可以是任何类型,例如,数据库、NoSQL、推特等等。在源和卡夫卡集群之间,有一个由工人组成的卡夫卡连接集群。卡夫卡连接的工作包括以下步骤:

  1. 工作人员根据配置从源中提取数据
  2. 获取数据后,连接器将数据推送到 Kafka 集群
  3. 如果需要使用流应用(如 Spark、Storm 等)对数据进行转换、过滤、连接或聚合,那么流 API 将在 Kafka 内外更改数据
  4. 根据配置,连接器将从 Kafka 中拉出数据并将其写入接收器

一些卡夫卡连接的概念如下:

  • 源连接器从公共数据源获取数据。
  • 接收器连接器将数据发布到公共数据源。
  • 卡夫卡连接使快速可靠地将数据输入卡夫卡变得容易。
  • 它是 ETL 管道的一部分。
  • 从小型管道扩展到全公司管道非常容易。
  • 代码是可重用的。
  • 卡夫卡连接集群有多个加载的连接器。每个连接器都是一段可重用的代码(Java JARs)。有很多可以利用的开源连接器。
  • 每个连接器任务都是连接器类和配置的组合。任务链接到连接器配置。一项工作创造可能创造多项任务。因此,如果您有一个连接器和一个配置,那么可以创建两个或多个任务。
  • 卡夫卡连接工人和服务器执行任务。一个工作进程就是一个 java 进程。工作者可以是独立的,也可以是分布式的。

卡夫卡连接工人模式

卡夫卡连接工人有两种模式:

  • 独立模式
  • 分布式模式

独立模式

独立模式是运行所有连接器和任务的单个进程(工作进程)。配置捆绑在一个流程中。它不容错也不可扩展,而且很难监控。由于它易于设置,所以主要用于开发和测试期间。

分布式模式

在分布式模式下,多个工作人员(进程)运行您的连接器和任务。使用 REST 应用编程接口提交配置。它是可扩展的和容错的。如果有任何工作人员死亡,它会自动重新平衡集群上的所有任务。由于它具有可伸缩性和容错性,因此主要用于生产环境。

Kafka Connect 集群分布式架构

以下是 Kafka Connect 集群分布式体系结构细节的表示:

在上图中,我们可以看到以下细节:

  • 我们有源连接器 1 ,有三个任务:任务 1任务 2任务 3 。这三项任务分布在四名工人中:工人 1工人 3工人 4
  • 我们还有源连接器 2 ,有两个任务:任务 1任务 2 。这两项任务由两个工人分担:工人 2工人 3
  • 我们还有下沉连接器 3 有四个任务:任务 1任务 2任务 3任务 4 。这四项任务分布在四名工人中:工人 1工人 2工人 3工人 4
  • 现在,发生了一些事情工人 4 死亡,我们完全失去了那个工人。
  • 作为容错的一部分,重新平衡活动开始了。连接器 1任务 3工人 4 移动到工人 2 。同样的,连接器 3****任务 4连接器 4 移动到连接器 1

下图显示了重新平衡后的卡夫卡连接集群:

例 1

在独立模式下,来自源文件Demo-Source.txt的流数据被移动到目标文件Demo-Sink.txt,如下图所示:

为了在独立模式下将数据从源文件Demo-Source.txt流式传输到目标文件Demo-Sink.txt,我们需要执行以下步骤:

  1. 开始卡夫卡:
$ /bin/kafka-server-start.sh config/server.properties 
  1. 创建主题:
$ .bin/kafka-topics --create --topic demo-1-standalone --partitions 3 --replication-factor 1 --zookeeper 127.0.0.1:2181 
  1. 配置source-file-stream-standalone.properties文件:
name=source-file-stream-standalone 
connector.class=org.apache.kafka.connect.file.FileStreamSourceConnector 
tasks.max=1 
file=demo-source.txt 
topic=file-source-topic 
  1. 配置file-stream-standalone.properties文件:
name=sinkfile-stream-standalone 
connector.class=org.apache.kafka.file.FileStreamSourceConnector 
tasks.max=1 
file=demo-sink.txt 
topics=file-source-topic 
  1. 配置file-worker.properties文件:
bootstrap.servers=127.0.0.1:9092 
key.converter=org.apache.kafka.connect.json.JsonConverter 
key.converter.schemas.enable=false 
value.converter=org.apache.kafka.connect.json.JsonConverter 
value.converter.schemas.enable=false 
# we always leave the internal key to JsonConverter 
internal.key.converter=org.apache.kafka.connect.json.JsonConverter 
internal.key.converter.schemas.enable=false 
internal.value.converter=org.apache.kafka.connect.json.JsonConverter 
internal.value.converter.schemas.enable=false 
rest.port=8086 
rest.host.name=127.0.0.1 
# this config is only for standalone workers 
offset.storage.file.filename=standalone.offsets 
offset.flush.interval.ms=10000 
  1. 启动卡夫卡连接。打开另一个终端并运行以下命令:
$ .bin/connect-standalone config/file-worker.properties config/source-file-stream-standalone.properties config/ sink-file-stream-standalone.properties 
  1. 将数据添加到demo-source.txt文件。打开另一个终端并运行以下命令:
$ touch demo-source.txt 

$ echo "Test Line 1 " >>  demo-source.txt 

$ echo "Test Line 2 " >>  demo-source.txt 

$ echo "Test Line 2 " >>  demo-source.txt 
  1. 阅读demo-sink.txt文件:
$ cat demo-sink.file 

例 2

在分布式模式下,来自源文件Demo-Source.txt的流数据被移动到目标文件Demo-Sink.txt。如果您想使用分布式模式运行前面的示例,您必须在步骤 34 中向source-file-streamsink-file-stream添加以下参数:

key.converter=org.apache.kafka.connect.json.JsonConverter 
key.converter.schemas.enable=true 
value.converter=org.apache.kafka.connect.json.JsonConverter 
value.converter.schemas.enable=true 

摘要

在本章中,我们学习了生产环境中使用的所有流行的数据摄取工具。Sqoop 主要用于将数据导入和导出 RDBMS 数据存储。Apache Flume 在实时系统中用于导入数据,主要来自文件源。它支持各种各样的源和汇。Apache NiFi 是一个相当新的工具,最近变得非常流行。它还支持基于图形用户界面的 ETL 开发。自 HDP 2.4 版本发布以来,Hortonworks 已经开始支持该工具。Apache Kafka Connect 是市场上另一个流行的工具。它也是融合数据平台的一部分。Kafka Connect 可以将整个数据库或从所有应用服务器收集的指标摄取到 Kafka 主题中,使数据可用于低延迟的流处理。

由于到目前为止我们已经知道如何构建 Hadoop 集群以及如何在其中摄取数据,我们将在下一章学习数据建模技术。

五、Hadoop 中的数据建模

到目前为止,我们已经学习了如何创建 Hadoop 集群以及如何将数据加载到其中。在前一章中,我们学习了各种数据摄取工具和技术。正如我们现在所知,市场上有各种各样的开源工具,但是有一个单一的银弹工具可以承担我们所有的用例。每个数据摄取工具都有某些独特的功能;在典型的用例中,它们可以被证明是非常有效和有用的。例如,当用于从关系数据库管理系统导入和导出 Hadoop 数据时,Sqoop 更有用。

在本章中,我们将学习如何在 Hadoop 集群中存储和建模数据。像数据摄取工具一样,也有各种可用的数据存储。这些数据存储支持不同的数据模型——即列数据存储、键值对等;而且它们支持各种文件格式,比如 ORC、Parquet 和 AVRO 等等。有非常受欢迎的数据存储,这些天在生产中被广泛使用,例如,Hive、HBase、Cassandra 等等。我们将进一步了解以下两种数据存储和数据建模技术:

  • Apache Hive
  • 阿帕契巴塞

首先,我们将从基本概念开始,然后我们将学习如何应用现代数据建模技术来加快数据访问。简而言之,我们将在本章中讨论以下主题:

  • Apache Hive 和 RDBMS
  • 支持的数据类型
  • Hive 架构及其工作原理

Apache Hive

Hive 是 Hadoop 中的一个数据处理工具。正如我们在上一章中了解到的,数据摄取工具在 Hadoop 中加载数据并生成 HDFS 文件;我们需要根据我们的业务需求来查询这些数据。我们可以使用 MapReduce 编程访问数据。但是使用 MapReduce 进行数据访问非常慢。要访问几行 HDFS 文件,我们必须编写单独的映射器、减速器和驱动程序代码。所以,为了避免这种复杂性,Apache 引入了 Hive。Hive 支持一个类似于 SQL 的接口,该接口有助于使用 SQL 命令访问相同行的 HDFS 文件。Hive 最初由脸书开发,但后来被 Apache 公司接管。

Apache Hive 和 RDBMS

我提到 Hive 提供了一个类似 SQL 的接口。考虑到这一点,出现的问题是:Hive 和 Hadoop 上的 RDBMS 一样吗?答案是没有。Hive 不是数据库。Hive 不存储任何数据。Hive 将表信息存储为元数据的一部分,称为模式,并指向 HDFS 的文件。Hive 使用名为 HiveQL ( HQL )的类似 SQL 的接口访问存储在 HDFS 文件中的数据。Hive 支持在 HDFS 访问和修改数据的 SQL 命令。Hive 不是 OLTP 的工具。它不提供任何行级插入、更新或删除。Hive 的当前版本(0.14 版本)确实支持使用完整的 ACID 属性进行插入、更新和删除,但是该功能效率不高。此外,此功能不支持所有文件格式。例如,更新仅支持 ORC 文件格式。基本上,Hive 是为批处理而设计的,不像 RDBMS 那样支持事务处理。因此,Hive 更适合数据仓库应用,用于提供数据汇总、查询和分析。在内部,Hive SQL 查询由其编译器转换为 MapReduce。用户无需担心编写任何复杂的映射器和缩减器代码。Hive 仅支持查询结构化数据。使用 Hive SQL 访问非结构化数据非常复杂。您可能需要为此编写自己的自定义函数。Hive 支持各种文件格式,如文本文件、序列文件、ORC 和 Parquet,它们提供了显著的数据压缩。

支持的数据类型

Hive 版本 0.14 支持以下数据类型:

| 数据类型组 | 数据类型 | 格式 |
| 线 | STRING | column_name STRING |
| VARCHAR | column_name VARCHAR(max_length) |
| CHAR | column_name CHAR(length) |
| 数字的 | TINYINT | column_name TINYINT |
| SMALLINT | column_name SMALLINT |
| INT | column_name INT |
| BIGINT | column_name BIGINT |
| FLOAT | column_name FLOAT |
| DOUBLE | column_name DOUBLE |
| DECIMAL | column_name DECIMAL[(precision[,scale])] |
| 日期/时间类型 | TIMESTAMP | column_name TIMESTAMP |
| DATE | column_name DATE |
| INTERVAL | column_name INTERVAL year to month |
| 杂项类型 | BOOLEAN | column_name BOOLEAN |
| BINARY | column_name BINARY |
| 复数型 | ARRAY | column_name ARRAY < type > |
| MAPS | column_name MAP < primitive_type, type > |
| STRUCT | column_name STRUCT < name : type [COMMENT 'comment_string'] > |
| UNION | column_name UNIONTYPE <int, double, array, string> |

Hive 是如何工作的

Hive 数据库由由分区组成的表组成。可以通过简单的查询语言访问数据,Hive 支持覆盖或追加数据。在特定的数据库中,表中的数据是序列化的,每个表都有一个对应的 HDFS 目录。每个表都可以细分为多个分区,这些分区决定了数据在表目录的子目录中的分布方式。分区内的数据可以进一步分解成桶。

Hive 建筑

以下是 Hive 架构的表示:

上图显示 Hive 架构分为三个部分——即客户端、服务和元存储。配置单元 SQL 的执行如下:

  • Hive SQL 查询:可以使用以下方式之一向 Hive 服务器提交 Hive 查询:WebUI、JDBC/ODBC 应用和 Hive CLI。对于基于节俭的应用,它将提供一个节俭客户端进行通信。
  • 查询执行:Hive 服务器一旦接收到查询,就会进行编译,转换成性能更好的优化查询计划,并转换成 MapReduce 作业。在这个过程中,配置单元服务器与元存储交互以获取查询元数据。
  • 作业执行:MapReduce 作业在 Hadoop 集群上执行。

Hive 数据模型管理

Hive 以以下四种方式处理数据:

  • Hive 表
  • Hive 表分区
  • Hive 分区
  • Hive 视图

我们将在接下来的章节中详细介绍它们。

Hive 表

配置单元表非常类似于任何关系数据库管理系统表。表格分为行和列。每个列(字段)都用正确的名称和数据类型定义。我们已经在支持的数据类型部分看到了 Hive 中所有可用的数据类型。配置单元表分为两种类型:

  • 托管表
  • 外部表格

我们将在下面的章节中了解这两种类型。

托管表

以下是定义 Hive 托管表的示例命令:

Create Table < managed_table_name>  
   Column1 <data type>, 
   Column2 <data type>, 
   Column3 <data type> 
Row format delimited Fields Terminated by "t"; 

当执行前面的查询时,Hive 会创建该表,元数据也会相应地在 metastore 中更新。但是桌子是空的。因此,可以通过执行以下命令将数据加载到该表中:

Load data inpath <hdfs_folder_name> into table <managed_table_name>; 

执行前一个命令后,数据从<hdfs_folder_name>移动到 Hive 表的默认位置/user/hive/warehouse/<managed_table_name。该默认文件夹/user/hive/warehousehive-site.xml中定义,可以更改为任何文件夹。现在,如果我们决定删除该表,我们可以通过发出以下命令来完成:

Drop table <managed_table_name>; 

/user/hive/warehouse/<managed_table_name文件夹将被删除,存储在元存储中的元数据将被删除。

外部表格

以下是定义 Hive 外部表的示例命令:

Create Table < external_table_name>  
   Column1 <data type>, 
   Column2 <data type>, 
   Column3 <data type> 
Row format delimited Fields Terminated by "t" 
Location <hdfs_folder_name>; 

当执行前面的查询时,Hive 会创建该表,元数据也会相应地在 metastore 中更新。但是,同样,桌子是空的。因此,可以通过执行以下命令将数据加载到该表中:

Load data inpath <hdfs_folder_name> into table <external_table_name>; 

该命令不会将任何文件移动到任何文件夹,而是创建一个指向文件夹位置的指针,并在 metastore 的元数据中进行更新。文件保持在查询的相同位置(<hdfs_folder_name>)。现在,如果我们决定删除该表,我们可以通过发出以下命令来完成:

Drop table <managed_table_name>;  

文件夹/user/hive/warehouse/<managed_table_name不会被删除,只会删除元存储中存储的元数据。文件保留在同一个位置— <hdfs_folder_name>

Hive 表分区

对表进行分区意味着根据分区键的值将表分成不同的部分。分区键可以是任何列,例如日期、部门、国家等。由于数据以部分形式存储,查询响应时间变得更快。分区不是扫描整个表,而是在主表文件夹中创建子文件夹。Hive 将根据查询的WHERE子句只扫描表的特定部分。配置单元表分区类似于任何 RDBMS 表分区。目的也是一样的。随着我们不断向表中插入数据,表的数据大小会变大。假设我们创建了一个ORDERS表,如下所示:

hive> create database if not exists ORDERS; 
OK 
Time taken: 0.036 seconds 

hive> use orders; 
OK 
Time taken: 0.262 seconds 

hive> CREATE TABLE if not exists ORDEERS_DATA 
    > (Ord_id INT, 
    > Ord_month INT, 
    > Ord_customer_id INT, 
    > Ord_city  STRING, 
    > Ord_zip   STRING, 
    > ORD_amt   FLOAT 
    > ) 
    > ROW FORMAT DELIMITED 
    > FIELDS TERMINATED BY  ',' 
    > ; 
OK 
Time taken: 0.426 seconds 
hive> 

我们将加载如下示例文件ORDERS_DATA表,如下所示:

101,1,100,'Los Angeles','90001',1200 
102,2,200,'Los Angeles','90002',1800 
103,3,300,'Austin','78701',6500 
104,4,400,'Phoenix','85001',7800 
105,5,500,'Beverly Hills','90209',7822 
106,6,600,'Gaylord','49734',8900 
107,7,700,'Los Angeles','90001',7002 
108,8,800,'Los Angeles','90002',8088 
109,9,900,'Reno','89501',6700 
110,10,1000,'Los Angeles','90001',8500 
111,10,1000,'Logan','84321',2300 
112,10,1000,'Fremont','94539',9500 
113,10,1000,'Omaha','96245',7500 
114,11,2000,'New York','10001',6700 
115,12,3000,'Los Angeles','90003',1000 

然后我们将orders.txt加载到/tmp HDFS 文件夹:

[root@sandbox order_data]# hadoop fs -put /root/order_data/orders.txt /tmp 

[root@sandbox order_data]# hadoop fs -ls /tmp 
Found 3 items 
-rw-r--r--   1 root      hdfs        530 2017-09-02 18:06 /tmp/orders.txt 

如下所示加载ORDERS_DATA表:

hive> load data inpath '/tmp/orders.txt' into table ORDERS_DATA; 
Loading data to table orders.orders_data 
Table orders.orders_data stats: [numFiles=1, numRows=0, totalSize=530, rawDataSize=0] 
OK 
Time taken: 0.913 seconds 

hive> select * from ORDERS_DATA; 
OK 
101      1     100   'Los Angeles'     '90001'     1200.0 
102      2     200   'Los Angeles'     '90002'     1800.0 
103      3     300   'Austin'    '78701'     6500.0 
104      4     400   'Phoenix'   '85001'     7800.0 
105      5     500   'Beverly Hills'   '90209'     7822.0 
106      6     600   'Gaylord'   '49734'     8900.0 
107      7     700   'Los Angeles'     '90001'     7002.0 
108      8     800   'Los Angeles'     '90002'     8088.0 
109      9     900   'Reno'      '89501'     6700.0 
110      10    1000  'Los Angeles'     '90001'     8500.0 
111      10    1000  'Logan'     '84321'     2300.0 
112      10    1000  'Fremont'   '94539'     9500.0 
113      10    1000  'Omaha'     '96245'     7500.0 
114      11    2000  'New York'  '10001'     6700.0 
115      12    3000  'Los Angeles'     '90003'     1000.0 
Time taken: 0.331 seconds, Fetched: 15 row(s) 

假设我们想在ORDERS_DATA表中插入城市数据。每个城市的订单数据大小为 1 TB。所以ORDERS_DATA表的总数据量为 15 TB(表中有 15 个城市)。现在,如果我们编写以下查询来获取在Los Angeles预订的所有订单:

hive>  select * from ORDERS where Ord_city = 'Los Angeles' ; 

查询将运行得非常慢,因为它必须扫描整个表。显而易见的想法是,我们可以为每个城市创建 10 个不同的orders表,并将orders数据存储在ORDERS_DATA表的相应城市中。但除此之外,我们可以对ORDERS_PART表进行如下划分:

hive> use orders; 

hive> CREATE TABLE orders_part 
    > (Ord_id INT, 
    > Ord_month INT, 
    > Ord_customer_id INT, 
    > Ord_zip   STRING, 
    > ORD_amt   FLOAT 
    > ) 
    > PARTITIONED BY  (Ord_city INT) 
    > ROW FORMAT DELIMITED 
    > FIELDS TERMINATED BY  ',' 
    > ; 
OK 
Time taken: 0.305 seconds 
hive> 

现在,Hive 将表组织成分区,以便根据列或分区键将相似类型的数据分组在一起。假设每个城市有 10 个orders档案,也就是Orders1.txtOrders10.txt。以下示例显示了如何将每个月的文件加载到每个对应的分区:

load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Los Angeles'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Austin'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Phoenix'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Beverly Hills'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Gaylord'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city=Reno'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Fremont'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Omaha'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='New York'); 
load data inpath '/tmp/orders.txt' into table orders_part partition(Ord_city='Logan'); 

[root@sandbox order_data]# hadoop fs -ls /apps/hive/warehouse/orders.db/orders_part 
Found 10 items 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Austin 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Beverly Hills 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Fremont 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Gaylord 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:33 /apps/hive/warehouse/orders.db/orders_part/ord_city=Logan 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Los Angeles 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=New York 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Omaha 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:32 /apps/hive/warehouse/orders.db/orders_part/ord_city=Phoenix 
drwxrwxrwx   - root hdfs          0 2017-09-02 18:33 /apps/hive/warehouse/orders.db/orders_part/ord_city=Reno 
[root@sandbox order_data]  

对数据进行分区可以极大地提高查询的性能,因为数据已经根据列值被分离到文件中,这可以减少映射器的数量,并极大地减少生成的 MapReduce 作业中数据的洗牌和排序量。

配置静态分区和动态分区

如果您想在 Hive 中使用静态分区,应该按如下方式设置属性:

set hive.mapred.mode = strict;  

在前面的例子中,我们已经看到,我们必须将每个月度订单文件分别插入到每个静态分区中。与动态分区相比,静态分区节省了加载数据的时间。我们必须单独向表中添加一个分区,并将文件移动到表的分区中。如果我们有很多分区,编写一个查询来加载每个分区中的数据可能会变得很麻烦。我们可以通过动态分区来克服这个问题。在动态分区中,我们可以用一条 SQL 语句将数据插入分区表,但仍然在每个分区中加载数据。与静态分区相比,动态分区加载数据的时间更长。当表中存储了大量数据时,动态分区是合适的。如果您想对多个列进行分区,但不知道它们有多少列,那么动态分区也是合适的。以下是您应该允许的配置单元动态分区属性:

SET hive.exec.dynamic.partition = true;
SET hive.exec.dynamic.partition.mode = nonstrict;  

下面是一个动态分区的例子。假设我们要将数据从ORDERS_PART表加载到名为ORDERS_NEW的新表中:

hive> use orders; 
OK 
Time taken: 1.595 seconds 
hive> drop table orders_New; 
OK 
Time taken: 0.05 seconds 
hive> CREATE TABLE orders_New 
    > (Ord_id INT, 
    > Ord_month INT, 
    > Ord_customer_id INT, 
    > Ord_city  STRING, 
    > Ord_zip   STRING, 
    > ORD_amt   FLOAT 
    > ) 
    > ) 
    > PARTITIONED BY  (Ord_city STRING) 
    > ROW FORMAT DELIMITED 
    > FIELDS TERMINATED BY  ',' 
    > ; 
OK 
Time taken: 0.458 seconds 
hive> 

将数据从ORDERS_PART表载入ORDER_NEW表。这里,Hive 将动态加载ORDERS_NEW表的所有分区:

hive> SET hive.exec.dynamic.partition = true; 
hive> SET hive.exec.dynamic.partition.mode = nonstrict; 
hive>  
    > insert into table orders_new  partition(Ord_city) select * from orders_part; 
Query ID = root_20170902184354_2d409a56-7bfc-416e-913a-2323ea3b339a 
Total jobs = 1 
Launching Job 1 out of 1 
Status: Running (Executing on YARN cluster with App id application_1504299625945_0013) 

-------------------------------------------------------------------------------- 
        VERTICES      STATUS  TOTAL  COMPLETED  RUNNING  PENDING  FAILED  KILLED 
-------------------------------------------------------------------------------- 
Map 1 ..........   SUCCEEDED      1          1        0        0       0       0 
-------------------------------------------------------------------------------- 
VERTICES: 01/01  [==========================>>] 100%  ELAPSED TIME: 3.66 s      
-------------------------------------------------------------------------------- 
Loading data to table orders.orders_new partition (ord_city=null) 
    Time taken to load dynamic partitions: 2.69 seconds 
   Loading partition {ord_city=Logan} 
   Loading partition {ord_city=Los Angeles} 
   Loading partition {ord_city=Beverly Hills} 
   Loading partition {ord_city=Reno} 
   Loading partition {ord_city=Fremont} 
   Loading partition {ord_city=Gaylord} 
   Loading partition {ord_city=Omaha} 
   Loading partition {ord_city=Austin} 
   Loading partition {ord_city=New York} 
   Loading partition {ord_city=Phoenix} 
    Time taken for adding to write entity : 3 
Partition orders.orders_new{ord_city=Austin} stats: [numFiles=1, numRows=1, totalSize=13, rawDataSize=12] 
Partition orders.orders_new{ord_city=Beverly Hills} stats: [numFiles=1, numRows=1, totalSize=13, rawDataSize=12] 
Partition orders.orders_new{ord_city=Fremont} stats: [numFiles=1, numRows=1, totalSize=15, rawDataSize=14] 
Partition orders.orders_new{ord_city=Gaylord} stats: [numFiles=1, numRows=1, totalSize=13, rawDataSize=12] 
Partition orders.orders_new{ord_city=Logan} stats: [numFiles=1, numRows=1, totalSize=15, rawDataSize=14] 
Partition orders.orders_new{ord_city=Los Angeles} stats: [numFiles=1, numRows=6, totalSize=82, rawDataSize=76] 
Partition orders.orders_new{ord_city=New York} stats: [numFiles=1, numRows=1, totalSize=15, rawDataSize=14] 
Partition orders.orders_new{ord_city=Omaha} stats: [numFiles=1, numRows=1, totalSize=15, rawDataSize=14] 
Partition orders.orders_new{ord_city=Phoenix} stats: [numFiles=1, numRows=1, totalSize=13, rawDataSize=12] 
Partition orders.orders_new{ord_city=Reno} stats: [numFiles=1, numRows=1, totalSize=13, rawDataSize=12] 
OK 
Time taken: 10.493 seconds 
hive>  

让我们看看ORDERS_NEW中创建了多少分区:

hive> show partitions ORDERS_NEW; 
OK 
ord_city=Austin 
ord_city=Beverly Hills 
ord_city=Fremont 
ord_city=Gaylord 
ord_city=Logan 
ord_city=Los Angeles 
ord_city=New York 
ord_city=Omaha 
ord_city=Phoenix 
ord_city=Reno 
Time taken: 0.59 seconds, Fetched: 10 row(s) 
hive>  

现在很清楚何时使用静态和动态分区。在将数据加载到配置单元表中之前,如果事先知道分区列值,则可以使用静态分区。在动态分区的情况下,分区列值只有在将数据加载到配置单元表中时才知道。

Hive 分区

分时段是一种将大型数据集分解成更易于管理的组的技术。分时段是基于散列函数的。当一个表被分时段时,所有具有相同列值的表记录将进入同一个时段。物理上,每个存储桶就像一个分区一样,是表文件夹中的一个文件。在分区表中,Hive 可以将数据分组到多个文件夹中。但是,当分区数量有限,并且数据在所有分区中平均分布时,分区证明是有效的。如果有大量的分区,那么它们的使用效率就会降低。所以在这种情况下,我们可以使用 bucketing。我们可以在创建表的过程中显式地创建多个桶。

Hive 桶是如何工作的

下图详细显示了 Hive bucketing 的工作原理:

如果我们决定在一个表中为一列创建三个桶,在我们的示例中为(Ord_city),那么 Hive 将创建三个桶,编号为 0-2 ( n-1 )。在记录插入期间,Hive 将对每条记录的Ord_city列应用哈希函数来决定哈希键。然后 Hive 将对每个哈希值应用一个模运算符。我们也可以在非分区表中使用 bucketing。但是,当分块特性用于分区表时,我们将获得最佳性能。倒挂有两个主要好处:

  • 提高查询性能:在同一个桶列的连接过程中,我们可以显式指定桶的数量。由于每个桶的数据大小相同,映射端连接在桶表中的性能优于非桶表。在地图端连接中,左侧表桶将确切知道右侧桶中的数据集,以便高效地执行表连接。
  • 改进采样:因为数据已经被分割成更小的块了。

让我们考虑一下我们的ORDERS_DATA表示例。在CITY列中进行分区。可能并非所有城市的订单分配都是平等的。有些城市的订单可能比其他城市多。在这种情况下,我们将有不平衡的分区。这将影响查询性能。对订单多的城市的查询将比订单少的城市慢。我们可以通过打破僵局来解决这个问题。表中的桶由表 DDL 中的CLUSTER子句定义。以下示例详细解释了 bucketing 功能。

在非分区表中创建桶

首先,我们将创建一个ORDERS_BUCK_non_partition表:

SET hive.exec.dynamic.partition = true; 
SET hive.exec.dynamic.partition.mode = nonstrict; 
SET hive.exec.mx_dynamic.partition=20000; 
SET hive.exec.mx_dynamic.partition.pernode=20000; 
SET hive.enforce.bucketing = true; 

hive> use orders; 
OK 
Time taken: 0.221 seconds 
hive>  
    > CREATE TABLE ORDERS_BUCKT_non_partition 
    > (Ord_id INT, 
    > Ord_month INT, 
    > Ord_customer_id INT, 
    > Ord_city  STRING, 
    > Ord_zip   STRING, 
    > ORD_amt   FLOAT 
    > ) 
    > CLUSTERED BY (Ord_city) into 4 buckets stored as textfile; 
OK 
Time taken: 0.269 seconds 
hive>  

To refer to all Hive SET configuration parameters, please use this URL:
https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties.

加载新创建的非分区桶表:

hive> insert into ORDERS_BUCKT_non_partition select * from orders_data; 
Query ID = root_20170902190615_1f557644-48d6-4fa1-891d-2deb7729fa2a 
Total jobs = 1 
Launching Job 1 out of 1 
Tez session was closed. Reopening... 
Session re-established. 
Status: Running (Executing on YARN cluster with App id application_1504299625945_0014) 

-------------------------------------------------------------------------------- 
        VERTICES      STATUS  TOTAL  COMPLETED  RUNNING  PENDING  FAILED  KILLED 
-------------------------------------------------------------------------------- 
Map 1 ..........   SUCCEEDED      1          1        0        0       0       0 
Reducer 2 ......   SUCCEEDED      4          4        0        0       0       0 
-------------------------------------------------------------------------------- 
VERTICES: 02/02  [==========================>>] 100%  ELAPSED TIME: 9.58 s      
-------------------------------------------------------------------------------- 
Loading data to table orders.orders_buckt_non_partition 
Table orders.orders_buckt_non_partition stats: [numFiles=4, numRows=15, totalSize=560, rawDataSize=545] 
OK 
Time taken: 15.55 seconds 
hive> 

以下命令显示 Hive 已经在表中创建了四个存储桶(文件夹)00000[0-3]_0:


[root@sandbox order_data]# hadoop fs -ls /apps/hive/warehouse/orders.db/orders_buckt_non_partition 
Found 4 items 
-rwxrwxrwx   1 root hdfs         32 2017-09-02 19:06 /apps/hive/warehouse/orders.db/orders_buckt_non_partition/000000_0 
-rwxrwxrwx   1 root hdfs        110 2017-09-02 19:06 /apps/hive/warehouse/orders.db/orders_buckt_non_partition/000001_0 
-rwxrwxrwx   1 root hdfs        104 2017-09-02 19:06 /apps/hive/warehouse/orders.db/orders_buckt_non_partition/000002_0 
-rwxrwxrwx   1 root hdfs        314 2017-09-02 19:06 /apps/hive/warehouse/orders.db/orders_buckt_non_partition/000003_0 
[root@sandbox order_data]# 

在分区表中创建桶

首先,我们将创建一个桶形分区表。这里,表在Ord_city列上被划分为四个桶,但又被细分为Ord_zip列:

SET hive.exec.dynamic.partition = true; 
SET hive.exec.dynamic.partition.mode = nonstrict; 
SET hive.exec.mx_dynamic.partition=20000; 
SET hive.exec.mx_dynamic.partition.pernode=20000; 
SET hive.enforce.bucketing = true; 

hive> CREATE TABLE ORDERS_BUCKT_partition 
    > (Ord_id INT, 
    > Ord_month INT, 
    > Ord_customer_id INT, 
    > Ord_zip   STRING, 
    > ORD_amt   FLOAT 
    > ) 
    > PARTITIONED BY  (Ord_city STRING) 
    > CLUSTERED BY (Ord_zip) into 4 buckets stored as textfile; 
OK 
Time taken: 0.379 seconds 

用具有动态分区的另一个分区表(ORDERS_PART)加载分桶分区表:

hive> SET hive.exec.dynamic.partition = true; 
hive> SET hive.exec.dynamic.partition.mode = nonstrict; 
hive> SET hive.exec.mx_dynamic.partition=20000; 
Query returned non-zero code: 1, cause: hive configuration hive.exec.mx_dynamic.partition does not exists. 
hive> SET hive.exec.mx_dynamic.partition.pernode=20000; 
Query returned non-zero code: 1, cause: hive configuration hive.exec.mx_dynamic.partition.pernode does not exists. 
hive> SET hive.enforce.bucketing = true; 
hive> insert into ORDERS_BUCKT_partition partition(Ord_city) select * from orders_part; 
Query ID = root_20170902194343_dd6a2938-6aa1-49f8-a31e-54dafbe8d62b 
Total jobs = 1 
Launching Job 1 out of 1 
Status: Running (Executing on YARN cluster with App id application_1504299625945_0017) 

-------------------------------------------------------------------------------- 
        VERTICES      STATUS  TOTAL  COMPLETED  RUNNING  PENDING  FAILED  KILLED 
-------------------------------------------------------------------------------- 
Map 1 ..........   SUCCEEDED      1          1        0        0       0       0 
Reducer 2 ......   SUCCEEDED      4          4        0        0       0       0 
-------------------------------------------------------------------------------- 
VERTICES: 02/02  [==========================>>] 100%  ELAPSED TIME: 7.13 s      
-------------------------------------------------------------------------------- 
Loading data to table orders.orders_buckt_partition partition (ord_city=null) 
    Time taken to load dynamic partitions: 2.568 seconds 
   Loading partition {ord_city=Phoenix} 
   Loading partition {ord_city=Logan} 
   Loading partition {ord_city=Austin} 
   Loading partition {ord_city=Fremont} 
   Loading partition {ord_city=Beverly Hills} 
   Loading partition {ord_city=Los Angeles} 
   Loading partition {ord_city=New York} 
   Loading partition {ord_city=Omaha} 
   Loading partition {ord_city=Reno} 
   Loading partition {ord_city=Gaylord} 
    Time taken for adding to write entity : 3 
Partition orders.orders_buckt_partition{ord_city=Austin} stats: [numFiles=1, numRows=1, totalSize=22, rawDataSize=21] 
Partition orders.orders_buckt_partition{ord_city=Beverly Hills} stats: [numFiles=1, numRows=1, totalSize=29, rawDataSize=28] 
Partition orders.orders_buckt_partition{ord_city=Fremont} stats: [numFiles=1, numRows=1, totalSize=23, rawDataSize=22] 
Partition orders.orders_buckt_partition{ord_city=Gaylord} stats: [numFiles=1, numRows=1, totalSize=23, rawDataSize=22] 
Partition orders.orders_buckt_partition{ord_city=Logan} stats: [numFiles=1, numRows=1, totalSize=26, rawDataSize=25] 
Partition orders.orders_buckt_partition{ord_city=Los Angeles} stats: [numFiles=1, numRows=6, totalSize=166, rawDataSize=160] 
Partition orders.orders_buckt_partition{ord_city=New York} stats: [numFiles=1, numRows=1, totalSize=23, rawDataSize=22] 
Partition orders.orders_buckt_partition{ord_city=Omaha} stats: [numFiles=1, numRows=1, totalSize=25, rawDataSize=24] 
Partition orders.orders_buckt_partition{ord_city=Phoenix} stats: [numFiles=1, numRows=1, totalSize=23, rawDataSize=22] 
Partition orders.orders_buckt_partition{ord_city=Reno} stats: [numFiles=1, numRows=1, totalSize=20, rawDataSize=19] 
OK 
Time taken: 13.672 seconds 
hive>  

Hive 视图

配置单元视图是一个逻辑表。它就像任何 RDBMS 视图一样。概念是一样的。创建视图时,Hive 不会在其中存储任何数据。创建视图时,Hive 会冻结元数据。Hive 不支持任何 RDBMS 的物化视图概念。视图的基本目的是隐藏查询的复杂性。有时,HQL 包含复杂的连接、子查询或筛选器。在视图的帮助下,整个查询可以在虚拟表中展平。

当在基础表上创建视图时,对该表的任何更改,甚至添加或删除该表,都会在视图中失效。此外,创建视图时,它只更改元数据。但是当查询访问该视图时,它会触发 MapReduce 作业。视图是没有关联存储的纯逻辑对象(Hive 中目前不支持物化视图)。当查询引用视图时,将对视图的定义进行评估,以便生成一组行供查询进一步处理。(这是概念性的描述。事实上,作为查询优化的一部分,Hive 可能会将视图的定义与查询相结合,例如,将过滤器从查询下推到视图中。)

创建视图时,视图的架构被冻结;对基础表的后续更改(例如,添加一列)不会反映在视图的架构中。如果基础表以不兼容的方式被删除或更改,后续查询无效视图的尝试将会失败。视图是只读的,不能作为LOAD / INSERT / ALTER更改元数据的目标。视图可能包含ORDER BYLIMIT条款。如果引用查询也包含这些子句,查询级子句将在 view 子句之后(以及查询中的任何其他操作之后)进行计算。例如,如果一个视图指定了LIMIT 5,一个引用查询作为(select * from v LIMIT 10)执行,那么最多将返回五行。

视图的语法

让我们看几个观点的例子:

CREATE VIEW [IF NOT EXISTS] [db_name.]view_name [(column_name [COMMENT column_comment], ...) ] 
  [COMMENT view_comment] 
  [TBLPROPERTIES (property_name = property_value, ...)] 
  AS SELECT ...;

我将使用以下几个例子来演示视图的优势。假设我们有两个表,Table_XTable_Y,模式如下:Table_XXCol_1弦、XCol_2弦、XCol_3弦、Table_YYCol_1弦、YCol_2弦、YCol_3弦和YCol_4弦。要创建与基表完全相同的视图,请使用以下代码:

Create view table_x_view as select * from Table_X; 

要在基表的选定列上创建视图,请使用以下命令:

Create view table_x_view as select xcol_1,xcol_3  from Table_X; 

要创建一个视图来过滤基表列的值,我们可以使用:

Create view table_x_view as select * from Table_X where XCol_3 > 40 and  XCol_2 is not null; 

要创建隐藏查询复杂性的视图:

create view table_union_view  as select XCol_1, XCol_2, XCol_3,Null from Table_X 
   where XCol_2  = "AAA" 
   union all 
   select YCol_1, YCol_2, YCol_3, YCol_4 from Table_Y 
   where YCol_3 = "BBB"; 

   create view table_join_view as select * from Table_X 
   join Table_Y on Table_X. XCol_1 = Table_Y. YCol_1; 

Hive 索引

索引的主要目的是轻松搜索记录并加快查询速度。Hive 索引的目标是提高对表中某些列的查询查找速度。如果没有索引,带有像WHERE tab1.col1 = 10这样的谓词的查询将加载整个表或分区,并处理所有的行。但是如果col1有索引,那么只需要加载和处理文件的一部分。索引能够提供的查询速度的提高是以创建索引的额外处理和存储索引的磁盘空间为代价的。有两种类型的索引:

  • 紧凑索引
  • 位图索引

主要区别在于将行的映射值存储在不同的块中。

紧凑索引

在 HDFS,数据以块的形式存储。但是扫描哪个数据存储在哪个块中是很耗时的。紧凑索引存储索引列的值及其blockId。所以查询不会转到表中。相反,查询将直接转到紧凑索引,其中存储了列值和blockId。不需要扫描所有的块来寻找数据!因此,在执行查询时,它将首先检查索引,然后直接进入该块。

位图索引

位图索引将索引列值和行列表的组合存储为位图。位图索引通常用于具有不同值的列。我们来回顾几个例子:基表、Table_XXCol_1整数、XCol_2字符串、XCol_3整数、XCol_4字符串。创建索引:

CREATE INDEX table_x_idx_1 ON TABLE table_x (xcol_1) AS 'COMPACT';  
SHOW INDEX ON table_x_idx;  
DROP INDEX table_x_idx ON table_x; 

CREATE INDEX table_x_idx_2 ON TABLE table_x (xcol_1) AS 'COMPACT' WITH DEFERRED REBUILD;  
ALTER INDEX table_x_idx_2 ON table_x REBUILD;  
SHOW FORMATTED INDEX ON table_x; 

前面的索引是空的,因为它是用DEFERRED REBUILD子句创建的,不管表中是否包含任何数据。创建该索引后,需要使用REBUILD命令来构建索引结构。创建索引后,如果基础表中的数据发生变化,必须使用REBUILD命令更新索引。创建索引并将其存储在文本文件中:

CREATE INDEX table_x_idx_3 ON TABLE table_x (table_x) AS 'COMPACT' ROW FORMAT DELIMITED  
FIELDS TERMINATED BY 't'  
STORED AS TEXTFILE; 

创建位图索引:

CREATE INDEX table_x_bitmap_idx_4 ON TABLE table_x (table_x) AS 'BITMAP' WITH DEFERRED REBUILD;  
ALTER INDEX table_x_bitmap_idx_4 ON table03 REBUILD;  
SHOW FORMATTED INDEX ON table_x; 
DROP INDEX table_x_bitmap_idx_4 ON table_x; 

使用 Hive 的 JSON 文档

JSON,是一种结构化数据的最小可读格式。它主要用于在服务器和 web 应用之间传输数据,作为 XML 的替代。JSON 建立在两种结构上:

  • 名称/值对的集合。在各种语言中,这被实现为对象、记录、结构、字典、哈希表、键控列表或关联数组。
  • 有序的值列表。在大多数语言中,这是通过数组、向量、列表或序列来实现的。

请点击以下网址阅读更多关于 JSON 的内容:http://www.json.org/

示例 1–使用 Hive (Hive 0.14 和更高版本)访问简单的 JSON 文档

在这个例子中,我们将看到如何使用 HiveQL 查询简单的 JSON 文档。假设我们想要访问HiveSample-Json-simple.json中的以下Sample-Json-simple.json文件:

{"username":"abc","tweet":"Sun shine is bright.","time1": "1366150681" } 
{"username":"xyz","tweet":"Moon light is mild .","time1": "1366154481" } 

查看Sample-Json-simple.json文件:

[root@sandbox ~]# cat Sample-Json-simple.json 
{"username":"abc","tweet":"Sun shine is bright.","timestamp": 1366150681 } 
{"username":"xyz","tweet":"Moon light is mild .","timestamp": 1366154481 } 
[root@sandbox ~]#  

Sample-Json-simple.json载入 HDFS:

[root@sandbox ~]# hadoop fs -mkdir  /user/hive-simple-data/ 
[root@sandbox ~]# hadoop fs -put Sample-Json-simple.json /user/hive-simple-data/ 

创建外部配置单元表,simple_json_table:

hive> use orders; 
OK 
Time taken: 1.147 seconds 
hive>  
CREATE EXTERNAL TABLE simple_json_table ( 
username string, 
tweet string, 
time1 string) 
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' 
LOCATION '/user/hive-simple-data/'; 
OK 
Time taken: 0.433 seconds 
hive>  

现在验证记录:

hive> select * from simple_json_table ; 
OK 
abc      Sun shine is bright.    1366150681 
xyz      Moon light is mild .    1366154481 
Time taken: 0.146 seconds, Fetched: 2 row(s) 
hive>  

示例 2–使用 Hive 访问嵌套的 JSON 文档(Hive 0.14 和更高版本)

我们将看到如何使用 HiveQL 查询嵌套 JSON 文档。假设我们想要访问HiveSample-Json-complex.json中的以下Sample-Json-complex.json文件:

{"DocId":"Doc1","User1":{"Id":9192,"Username":"u2452","ShippingAddress":{"Address1":"6373 Sun Street","Address2":"apt 12","City":"Foster City","State":"CA"},"Orders":[{"ItemId":5343,"OrderDate":"12/23/2017"},{"ItemId":7362,"OrderDate":"12/24/2017"}]}} 

Sample-Json-simple.json载入 HDFS:

[root@sandbox ~]# hadoop fs -mkdir  /user/hive-complex-data/ 
[root@sandbox ~]# hadoop fs -put Sample-Json-complex.json /user/hive-complex-data/ 

创建外部配置单元表,json_nested_table:

hive>  
CREATE EXTERNAL TABLE json_nested_table( 
DocId string, 
user1 struct<Id: int, username: string, shippingaddress:struct<address1:string,address2:string,city:string,state:string>, orders:array<struct<ItemId:int,orderdate:string>>> 
) 
ROW FORMAT SERDE 
'org.apache.hive.hcatalog.data.JsonSerDe' 
LOCATION 
'/user/hive-complex-data/'; 
OK 
Time taken: 0.535 seconds 
hive>  

核实记录:

hive> select DocId,user1.username,user1.orders FROM json_nested_table; 
OK 
Doc1     u2452   [{"itemid":5343,"orderdate":"12/23/2017"},{"itemid":7362,"orderdate":"12/24/2017"}] 
Time taken: 0.598 seconds, Fetched: 1 row(s) 
hive>  

示例 3–使用 Hive 和 Avro 的模式演化(Hive 0.14 和更高版本)

在生产中,我们必须改变表结构来满足新的业务需求。必须更改表架构才能添加/删除/重命名表列。任何这些变化都会对下游的 ETL 作业产生负面影响。为了避免这些,我们必须对 ETL 作业和目标表进行相应的更改。

模式演化允许您更新用于写入新数据的模式,同时保持与旧数据模式的向后兼容性。然后,您可以一起阅读它,就好像所有的数据都有一个模式一样。请在以下网址阅读更多关于 Avro 序列化的内容:https://avro.apache.org/。在下面的例子中,我将演示 Avro 和 Hive 表如何在没有 ETL 作业失败的情况下吸收源表的模式变化。我们将在 MySQL 数据库中创建一个客户表,并使用 Avro 文件将其加载到目标 Hive 外部表中。然后,我们将向源表中再添加一列,以查看 Hive 表是如何在没有任何错误的情况下吸收该更改的。连接到 MySQL 创建源表(customer):

mysql -u root -p 

GRANT ALL PRIVILEGES ON *.* TO 'sales'@'localhost' IDENTIFIED BY 'xxx';  

mysql -u sales  -p 

mysql> create database orders; 

mysql> use orders; 

CREATE TABLE customer( 
cust_id INT , 
cust_name  VARCHAR(20) NOT NULL, 
cust_city VARCHAR(20) NOT NULL, 
PRIMARY KEY ( cust_id ) 
); 

将记录插入customer表:

INSERT into customer (cust_id,cust_name,cust_city) values (1,'Sam James','Austin'); 
INSERT into customer (cust_id,cust_name,cust_city) values (2,'Peter Carter','Denver'); 
INSERT into customer (cust_id,cust_name,cust_city) values (3,'Doug Smith','Sunnyvale'); 
INSERT into customer (cust_id,cust_name,cust_city) values (4,'Harry Warner','Palo Alto'); 

在 Hadoop 上,运行以下sqoop命令将customer表和 Avro 文件中的数据导入 HDFS:


hadoop fs -rmr /user/sqoop_data/avro 
sqoop import -Dmapreduce.job.user.classpath.first=true  
--connect jdbc:mysql://localhost:3306/orders   
--driver com.mysql.jdbc.Driver  
--username sales --password xxx  
--target-dir /user/sqoop_data/avro  
--table customer  
--as-avrodatafile  

验证目标 HDFS 文件夹:

[root@sandbox ~]# hadoop fs -ls /user/sqoop_data/avro 
Found 7 items 
-rw-r--r--   1 root hdfs          0 2017-09-09 08:57 /user/sqoop_data/avro/_SUCCESS 
-rw-r--r--   1 root hdfs        472 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00000.avro 
-rw-r--r--   1 root hdfs        475 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00001.avro 
-rw-r--r--   1 root hdfs        476 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00002.avro 
-rw-r--r--   1 root hdfs        478 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00003.avro 

创建一个 Hive 外部表来访问 Avro 文件:

use orders; 
drop table customer ; 
CREATE EXTERNAL TABLE customer  
( 
cust_id INT , 
cust_name  STRING , 
cust_city STRING   
) 
STORED AS AVRO 
location '/user/sqoop_data/avro/'; 

验证 Hivecustomer表:

hive> select * from customer; 
OK 
1  Sam James   Austin 
2  Peter Carter      Denver 
3  Doug Smith  Sunnyvale 
4  Harry Warner      Palo Alto 
Time taken: 0.143 seconds, Fetched: 4 row(s) 
hive>  

完美!我们没有错误。我们使用 Avro 序列化成功地将源customer表导入到目标 Hive 表中。现在,我们向源表中添加一列,并再次导入它,以验证我们可以在没有任何模式更改的情况下访问目标 Hive 表。连接到 MySQL 并再添加一列:

mysql -u sales  -p 

mysql>  
ALTER TABLE customer 
ADD COLUMN cust_state VARCHAR(15) NOT NULL; 

mysql> desc customer; 
+------------+-------------+------+-----+---------+-------+ 
| Field      | Type        | Null | Key | Default | Extra | 
+------------+-------------+------+-----+---------+-------+ 
| cust_id    | int(11)     | NO   | PRI | 0       |       | 
| cust_name  | varchar(20) | NO   |     | NULL    |       | 
| cust_city  | varchar(20) | NO   |     | NULL    |       | 
| CUST_STATE | varchar(15) | YES  |     | NULL    |       | 
+------------+-------------+------+-----+---------+-------+ 
4 rows in set (0.01 sec) 

mysql>  

现在插入行:

INSERT into customer (cust_id,cust_name,cust_city,cust_state) values (5,'Mark Slogan','Huston','TX'); 
INSERT into customer (cust_id,cust_name,cust_city,cust_state) values (6,'Jane Miller','Foster City','CA'); 

在 Hadoop 上,运行以下sqoop命令导入customer表,以追加新的地址列和数据。我使用了appendwhere "cust_id > 4"参数只导入新行:

sqoop import -Dmapreduce.job.user.classpath.first=true  
--connect jdbc:mysql://localhost:3306/orders   
--driver com.mysql.jdbc.Driver  
--username sales --password xxx  
--table customer  
--append  
--target-dir /user/sqoop_data/avro  
--as-avrodatafile  
--where "cust_id > 4"  

验证 HDFS 文件夹:

[root@sandbox ~]# hadoop fs -ls /user/sqoop_data/avro 
Found 7 items 
-rw-r--r--   1 root hdfs          0 2017-09-09 08:57 /user/sqoop_data/avro/_SUCCESS 
-rw-r--r--   1 root hdfs        472 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00000.avro 
-rw-r--r--   1 root hdfs        475 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00001.avro 
-rw-r--r--   1 root hdfs        476 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00002.avro 
-rw-r--r--   1 root hdfs        478 2017-09-09 08:57 /user/sqoop_data/avro/part-m-00003.avro 
-rw-r--r--   1 root hdfs        581 2017-09-09 09:00 /user/sqoop_data/avro/part-m-00004.avro 
-rw-r--r--   1 root hdfs        586 2017-09-09 09:00 /user/sqoop_data/avro/part-m-00005.avro 

现在,让我们验证我们的目标 Hive 表仍然能够访问旧的和新的 Avro 文件:

hive> select * from customer; 
OK 
1  Sam James   Austin 
2  Peter Carter      Denver 
3  Doug Smith  Sunnyvale 
4  Harry Warner      Palo Alto 
Time taken: 0.143 seconds, Fetched: 4 row(s 

太好了。没有错误。尽管如此,一切如常;现在,我们将向 Hive 表中添加一个新列,以查看新添加的 Avro 文件:


hive> use orders; 
hive> ALTER TABLE customer ADD COLUMNS (cust_state STRING); 
hive> desc customer; 
OK 
cust_id              int                                          
cust_name            string                                       
cust_city            string                                       
cust_state           string                                       
Time taken: 0.488 seconds, Fetched: 4 row(s 

验证配置单元表中的新数据:

hive> select * from customer; 
OK 
1  Sam James   Austin      NULL 
2  Peter Carter      Denver      NULL 
3  Doug Smith  Sunnyvale   NULL 
4  Harry Warner      Palo Alto   NULL 
5  Mark Slogan Huston      TX 
6  Jane Miller Foster City CA 
Time taken: 0.144 seconds, Fetched: 6 row(s) 
hive>  

太棒了。看看客户号56。我们可以看到新添加的带有值的列(cust_state)。您可以使用相同的技术尝试删除列和替换列功能。现在,我们对如何使用 Apache Hive 访问数据有了一个相当好的想法。在下一节中,我们将了解如何使用 NoSQL 数据存储区 HBase 访问数据。

阿帕契巴塞

我们刚刚了解了 Hive,这是一个用户可以使用 SQL 命令访问数据的数据库。但是在某些数据库中,用户不能使用 SQL 命令。这些数据库被称为 NoSQL 数据存储。糖化血红蛋白酶是一个 NoSQL 数据库。那么,NoSQL 到底是什么意思呢?NoSQL 的意思不仅仅是 SQL。在像 HBase 这样的 NoSQL 数据存储中,RDBMS 的主要功能,如验证和一致性,都有所放松。此外,关系数据库管理系统或 SQL 数据库与 NoSQL 数据库之间的另一个重要区别是写模式与读模式。在写入时模式中,数据在写入表时被验证,而读取时模式支持在读取时验证数据。这样,由于在写入数据时放松了基本数据验证,NoSQL 数据存储支持存储巨大的数据速度。目前市场上大约有 150 家 NoSQL 数据存储。这些 NoSQL 数据存储中的每一个都有一些独特的功能。一些受欢迎的 NoSQL 数据存储是 HBase、Cassandra、MongoDB、Druid、Apache Kudu 和 Coculuo 等等。

You can get a detailed list of all types of NoSQL databases at http://nosql-database.org/.

HBase 是一个流行的 NoSQL 数据库,被许多大公司使用,如脸书、谷歌等。

HDFS 与糖化血红蛋白酶的区别

下面解释了 HDFS 和糖化血红蛋白之间的主要区别。Hadoop 建立在 HDFS 之上,它支持存储大容量(千兆字节)的数据集。这些数据集使用批处理作业,通过使用 MapReduce 算法来访问。为了在如此庞大的数据集中找到一个数据元素,需要对整个数据集进行扫描。另一方面,HBase 建立在 HDFS 之上,为大型表提供快速的记录查找(和更新)。HBase 会在内部将您的数据放入 HDFS 上的索引存储文件中,以便进行高速查找。

Hive 和糖化血红蛋白酶的区别

HBase 是一个数据库管理系统;它支持事务处理和分析处理。Hive 是一个数据仓库系统,只能用于分析处理。HBase 支持低延迟和随机数据访问操作。Hive 只支持批处理,这导致了高延迟。HBase 不支持任何与表数据交互的 SQL 接口。您可能需要编写 Java 代码来读写 HBase 表中的数据。有时,处理涉及多个数据集连接的数据集时,Java 代码变得非常复杂。但是 Hive 支持用 SQL 进行非常简单的访问,这使得向其表中读写数据变得非常容易。在 HBase 中,数据建模涉及到灵活的数据模型和面向列的数据存储,必须支持数据反规格化。在向表中写入数据时,将决定 HBase 表的列。在 Hive 中,数据模型包括具有固定模式的表,如关系数据库管理系统数据模型。

糖化血红蛋白酶的主要特性

以下是糖化血红蛋白酶的几个关键特性:

  • 排序的行键:在 HBase 中,数据处理通过三个基本操作/API 关闭:获取、放置和扫描。所有这三个 API 都使用 rowkeys 访问数据,以确保数据访问的顺畅。由于扫描是在一系列行上完成的,因此 HBase 会根据行键按字典顺序对行进行排序。使用这些排序的行键,可以简单地从开始和停止行键定义扫描。这对于在一次数据库调用中获取所有相关数据来说是非常强大的。应用开发人员可以设计一个系统,通过基于时间戳查询最近的行来访问最近的数据集,因为所有行都是基于最新的时间戳以排序顺序存储在表中的。
  • 控制数据分片 : HBase Table rowkey 强烈影响数据分片。表数据按行键、列族和列键按升序排序。可靠的 rowkey 设计对于确保数据在 Hadoop 集群中均匀分布非常重要。由于行键决定了表的行的排序顺序,表中的每个区域最终负责一部分行键空间的物理存储。
  • 强一致性:相比可用性,HBase 更倾向于一致性。它还支持基于每行的 ACID 级语义。当然,这会影响写入性能,而写入性能往往会更慢。总的来说,这种权衡对应用开发人员有利,他们将保证数据存储始终具有正确的数据价值。
  • 低延迟处理 : HBase 支持对存储的所有数据进行快速、随机的读写。
  • 灵活性 : HBase 支持任何类型——结构化、半结构化、非结构化。
  • 可靠性:多次复制 HBase 表数据块,确保数据不丢失。HBase 还支持容错。即使在任何区域服务器出现故障的情况下,表数据也始终可供处理。

糖化血红蛋白数据模型

这些是糖化血红蛋白数据模型的关键组成部分:

  • :在 HBase 中,数据存储在一个名为的逻辑对象中,该对象有多行。
  • :HBase 中的一行由一个行键和一列或多列组成。row 键对行进行排序。目标是以相关行彼此靠近的方式存储数据。行键可以是一列或多列的组合。行键类似于表的主键,必须是唯一的。HBase 使用行键来查找列中的数据。例如,customer_id可以是customer表的行键。
  • :HBase 中的一列由一个列族和一个列限定符组成。
  • 列限定符:是表的列名。
  • 单元格:这是行、列族和列限定符的组合,包含一个值和一个代表该值版本的时间戳。
  • 列族:它是位于同一位置并存储在一起的列的集合,通常是出于性能原因。每个列族都有一组存储属性,如缓存、压缩和数据编码。

关系数据库管理系统表和面向列的数据存储之间的区别

我们都知道数据是如何存储在任何 RDBMS 表中的。看起来是这样的:

| ID | Column_1 | Column_2 | Column_3 | Column_4 |
| one | A | Eleven | P | xx |
| Two | B | Twelve | Q | YY |
| three | C | Thirteen | 稀有 | 锯齿形 |
| four | D | Fourteen | S | XX1 |

列标识用作表的唯一/主键,用于访问表中其他列的数据。但是在像 HBase 这样的面向列的数据存储中,同一个表被分为键和值,存储方式如下:

| | |
| | | 列值 |
| one | Column_1 | A |
| one | Column_2 | 11 |
| one | Column_3 | P |
| one | Column_4 | XX |
| Two | Column_1 | B |
| Two | Column_2 | 12 |
| Two | Column_3 | Q |
| Two | Column_4 | YY |
| three | Column_1 | C |
| three | Column_2 | 13 |
| three | Column_3 | R |
| three | Column_4 | ZZ |

在 HBase 中,每个表都是一种排序的映射格式,其中每个键都按升序排序。在内部,每个键和值都被序列化,并以字节数组格式存储在磁盘上。每个列值都由其对应的键访问。因此,在上表中,我们定义了一个键,它是两列的组合,行+列。例如,为了访问第 1 行的Column_1数据元素,我们必须使用一个键,第 1 行+ column_1。这就是为什么行键设计在 HBase 中非常关键。在创建 HBase 表之前,我们必须为每一列确定一个列族。列族是列的集合,它们位于同一位置并存储在一起,通常是出于性能原因。每个列族都有一组存储属性,如缓存、压缩和数据编码。例如,在典型的CUSTOMER表中,我们可以定义两个列族,即cust_profilecust_address。与客户地址相关的所有列都被分配到列族cust_address;所有其他列,即cust_idcust_namecust_age,都被分配给列族cust_profile。分配完列族后,我们的示例表如下所示:

| | |
| | | 柱族 | | 时间戳 |
| one | Column_1 | cf_1 | A | 1407755430 |
| one | Column_2 | cf_1 | 11 | 1407755430 |
| one | Column_3 | cf_1 | P | 1407755430 |
| one | Column_4 | cf_2 | XX | 1407755432 |
| Two | Column_1 | cf_1 | B | 1407755430 |
| Two | Column_2 | cf_1 | 12 | 1407755430 |
| Two | Column_3 | cf_1 | Q | 1407755430 |
| Two | Column_4 | cf_2 | YY | 1407755432 |
| three | Column_1 | cf_1 | C | 1407755430 |
| three | Column_2 | cf_1 | 13 | 1407755430 |
| three | Column_3 | cf_1 | R | 1407755430 |
| three | Column_4 | cf_2 | ZZ | 1407755432 |

向表中插入数据时,HBase 会自动为单元格的每个版本添加时间戳。

HBase 体系结构

如果我们想从一个糖化血红蛋白表中读取数据,我们必须给出一个合适的行标识,糖化血红蛋白将根据给定的行标识执行查找。HBase 使用以下排序的嵌套映射来返回行 ID 的列值:行 ID、列族、时间戳处的列和值。HBase 始终部署在 Hadoop 上。以下是典型安装:

它是 HBase 群集的主服务器,负责区域服务器的管理、监视和管理,例如将区域分配给区域服务器、区域拆分等。在分布式集群中,主机通常运行在 Hadoop 命名节点上。

ZooKeeper 是 HBase 集群的协调者。HBase 使用 ZooKeeper 作为分布式协调服务来维护集群中的服务器状态。ZooKeeper 维护哪些服务器是活动的和可用的,并提供服务器故障通知。区域服务器负责管理区域。区域服务器部署在数据节点上。它为读取和写入提供数据。区域服务器由以下附加组件组成:

  • 区域:HBase 表按行键范围水平划分为区域。区域包含表格中区域开始键和结束键之间的所有行。预写日志 ( WAL )用于存储尚未存储在磁盘上的新数据。
  • MemStore 是一个写缓存。它存储尚未写入磁盘的新数据。在写入磁盘之前会对其进行排序。每个区域每个列族都有一个 MemStore。Hfile 将行存储为磁盘/HDFS 上的排序关键字/值:

简而言之,HBase 架构

  • HBase 群集由一个活动主服务器和一个或多个备份主服务器组成
  • 群集有多个区域服务器
  • HBase 表总是很大,行被分成称为区域的分区/碎片
  • 每个区域服务器托管一个或多个区域
  • HBase 目录被称为 META 表,它存储表区域的位置
  • ZooKeeper 存储元表的位置
  • 在写入过程中,客户端向 HRegionServer 发送放入请求
  • 数据被写入沃尔
  • 然后,数据被推入 MemStore,并向客户端发送确认
  • 一旦 MemStore 中积累了足够的数据,它就会将数据刷新到 HDFS 的 Hfile 中
  • HBase 压缩过程定期激活,将多个 Hfile 合并为一个 Hfile(称为压缩)

HBase rowkey 设计

Rowkey 设计是 HBase 表格设计中非常关键的一部分。在按键设计过程中,必须小心避免过热。如果密钥设计不当,所有数据将被摄入到几个节点中,从而导致集群不平衡。然后,所有读取都必须指向这几个节点,导致数据读取速度变慢。我们必须设计一个密钥,帮助将数据平等地加载到集群的所有节点。可以通过以下技术避免热灌注:

  • 键加盐:就是在键的开头加一个任意值,确保行在所有表区域中平均分布。例子有aa-customer_idbb-customer_id等等。
  • 密钥散列:密钥可以被散列,散列值可以用作行密钥,例如HASH(customer_id)
  • 带有反向时间戳的密钥:在这种技术中,您必须定义一个常规密钥,然后给它附加一个反向时间戳。必须通过从任意最大值中减去时间戳来反转时间戳,然后将其附加到密钥上。比如customer_id是你的行 ID,新的键会是customer_id +反向时间戳。

以下是设计 HBase 表时的指导原则:

  • 每个表定义不超过两个列族
  • 保持列族名称尽可能小
  • 保持列名尽可能小
  • 保持行键长度尽可能小
  • 不要将行版本设置在较高的级别
  • 该表不应超过 100 个区域

示例 4–将数据从 MySQL 表加载到 HBase 表

我们将使用之前创建的相同的customer表:

mysql -u sales -p
mysql> use orders;
mysql> select * from customer;
+---------+--------------+--------------+------------+
| cust_id | cust_name | cust_city | InsUpd_on |
+---------+--------------+--------------+------------+
| 1 | Sam James | Austin | 1505095030 |
| 2 | Peter Carter | Denver | 1505095030 |
| 3 | Doug Smith | Sunnyvale | 1505095030 |
| 4 | Harry Warner | Palo Alto | 1505095032 |
| 5 | Jen Turner | Cupertino | 1505095036 |
| 6 | Emily Stone | Walnut Creek | 1505095038 |
| 7 | Bill Carter | Greenville | 1505095040 |
| 8 | Jeff Holder | Dallas | 1505095042 |
| 10 | Mark Fisher | Mil Valley | 1505095044 |
| 11 | Mark Fisher | Mil Valley | 1505095044 |
+---------+--------------+--------------+------------+
10 rows in set (0.00 sec)

启动 HBase,并在 HBase 中创建一个customer表:

hbase shell
create 'customer','cf1'

使用 Sqoop 在 HBase 中加载 MySQL customer表数据:

hbase
sqoop import --connect jdbc:mysql://localhost:3306/orders --driver com.mysql.jdbc.Driver --username sales --password sales1 --table customer --hbase-table customer --column-family cf1 --hbase-row-key cust_id

验证 HBase 表:

hbase shell
scan 'customer'

您必须看到 HBase 表中的所有 11 行。

示例 5–将数据从 MySQL 表增量加载到 HBase 表

mysql -u sales -p
mysql> use orders;

插入新客户并更新现有客户:

mysql> Update customer set cust_city = 'Dublin', InsUpd_on = '1505095065' where cust_id = 4;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> INSERT into customer (cust_id,cust_name,cust_city,InsUpd_on) values (12,'Jane Turner','Glen Park',1505095075);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from customer;
+---------+--------------+--------------+------------+
| cust_id | cust_name | cust_city | InsUpd_on |
+---------+--------------+--------------+------------+
| 1 | Sam James | Austin | 1505095030 |
| 2 | Peter Carter | Denver | 1505095030 |
| 3 | Doug Smith | Sunnyvale | 1505095030 |
| 4 | Harry Warner | Dublin | 1505095065 |
| 5 | Jen Turner | Cupertino | 1505095036 |
| 6 | Emily Stone | Walnut Creek | 1505095038 |
| 7 | Bill Carter | Greenville | 1505095040 |
| 8 | Jeff Holder | Dallas | 1505095042 |
| 10 | Mark Fisher | Mil Valley | 1505095044 |
| 11 | Mark Fisher | Mil Valley | 1505095044 |
| 12 | Jane Turner | Glen Park | 1505095075 | +---------+--------------+--------------+------------+
11 rows in set (0.00 sec)
mysql>

示例 6–将 MySQL 客户更改的数据加载到 HBase 表中

这里,我们使用InsUpd_on列作为我们的 ETL 日期:

sqoop import --connect jdbc:mysql://localhost:3306/orders --driver com.mysql.jdbc.Driver --username sales --password sales1 --table customer --hbase-table customer --column-family cf1 --hbase-row-key cust_id --append -- -m 1 --where "InsUpd_on > 1505095060"

hbase shell
hbase(main):010:0> get 'customer', '4'
COLUMN          CELL
cf1:InsUpd_on   timestamp=1511509774123, value=1505095065
cf1:cust_city   timestamp=1511509774123, value=Dublin cf1:cust_name   timestamp=1511509774123, value=Harry Warner
3 row(s) in 0.0200 seconds

hbase(main):011:0> get 'customer', '12'
COLUMN           CELL
cf1:InsUpd_on    timestamp=1511509776158, value=1505095075
cf1:cust_city    timestamp=1511509776158, value=Glen Park
cf1:cust_name    timestamp=1511509776158, value=Jane Turner
3 row(s) in 0.0050 seconds

hbase(main):012:0>

示例 7–蜂窝基站集成

现在,我们将使用 Hive 外部表访问 HBase customer表:

create external table customer_hbase(cust_id string, cust_name string, cust_city string, InsUpd_on string)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'with serdeproperties ("hbase.columns.mapping"=":key,cf1:cust_name,cf1:cust_city,cf1:InsUpd_on")tblproperties("hbase.table.name"="customer");

hive> select * from customer_hbase;
OK
1 Sam James Austin 1505095030
10 Mark Fisher Mil Valley 1505095044
11 Mark Fisher Mil Valley 1505095044
12 Jane Turner Glen Park 1505095075
2 Peter Carter Denver 1505095030
3 Doug Smith Sunnyvale 1505095030
4 Harry Warner Dublin 1505095065
5 Jen Turner Cupertino 1505095036
6 Emily Stone Walnut Creek 1505095038
7 Bill Carter Greenville 1505095040
8 Jeff Holder Dallas 1505095042
Time taken: 0.159 seconds, Fetched: 11 row(s)
hive>

摘要

在本章中,我们看到了如何使用一个名为 Hive 的 Hadoop SQL 接口来存储和访问数据。我们研究了 Hive 中的各种分区和索引策略。工作示例帮助我们理解了使用 Hive 中的 Avro 进行 JSON 数据访问和模式演化。在本章的第二部分,我们研究了一个名为 HBase 的 NoSQL 数据存储及其与关系数据库管理系统的区别。HBase 表的行设计对于平衡读写以避免区域热点至关重要。我们必须牢记本章中讨论的 HBase 表设计最佳实践。该工作示例显示了将数据摄取到 HBase 表中的更简单的路径及其与 Hive 的集成。

在下一章中,我们将了解设计实时数据分析的工具和技术。

六、设计实时流数据管道

这本书的前三章都涉及批量数据。了解了 Hadoop 的安装、数据接收工具和技术以及数据存储之后,让我们来看看数据流。我们不仅要考虑如何处理实时数据流,还要考虑如何围绕数据流设计管道。

在本章中,我们将涵盖以下主题:

  • 实时流概念
  • 实时流组件
  • Apache 弗林克对 Spark
  • ApacheSpark 对 Storm

实时流概念

让我们在以下几节中了解一些与实时流应用相关的关键概念。

数据流

数据流是从一端到另一端、从发送者到接收者、从生产者到消费者的连续数据流。数据的速度和数量可能会有所不同;它可能是每秒 1 GB 的数据,也可能是每秒或每分钟 1 KB 的数据。

批处理与实时数据处理

在批处理中,数据是分批收集的,每批数据都被发送进行处理。批处理间隔可以是从一天到一分钟的任何时间。在当今的数据分析和商业智能世界中,数据不会在超过一天的时间内被批量处理。否则,业务团队将无法洞察日常业务发生了什么。例如,企业数据仓库团队可能会收集过去 24 小时内发出的所有订单,并将收集到的所有订单发送给分析引擎进行报告。

批次也可以是一分钟。在 Spark 框架中(我们将在第 7 章大规模数据处理框架中学习 Spark),数据是以微批处理的方式处理的。

在实时处理中,一旦在源端产生事件,数据(事件)就从生产者(发送者)传输(流传输)到消费者(接收者)。例如,在电子商务网站上,一旦客户在该网站上下了相同的订单,订单就会立即在分析引擎中得到处理。这样做的好处是,该公司的业务团队可以实时(在几毫秒或亚毫秒内)全面了解其业务。这将有助于他们调整促销以增加收入,所有这些都是实时的。

下图解释了流处理架构:

复杂事件处理

复杂事件处理 ( CEP )是将来自多个来源的数据进行组合以发现复杂关系或模式的事件处理。CEP 的目标是识别有意义的事件(如机会或威胁),并尽快做出反应。从根本上说,CEP 是将业务规则应用于流式事件数据。例如,CEP 用于用例,如股票交易、欺诈检测、医疗索赔处理等。

下图解释了流处理架构:

持续可用性

任何实时应用都应该随时可用,没有任何中断。事件收集、处理和存储组件应配置有下划线的高可用性假设。任何组件的任何故障都会对业务运行造成重大干扰。例如,在信用卡欺诈检测应用中,需要拒绝所有欺诈交易。如果应用中途停止,并且无法拒绝欺诈交易,那么将导致重大损失。

低延迟

在任何实时应用中,事件应该在几毫秒内从源流向目标。源收集事件,处理框架将事件移动到其目标数据存储中,在那里可以进一步分析事件以发现趋势和模式。所有这些都应该实时发生,否则可能会影响业务决策。例如,在信用卡欺诈检测应用中,预期应分析所有传入的交易,以发现可能的欺诈交易(如果有)。如果流处理花费的时间超过了所需的时间,这些事务可能会通过系统,给业务造成重大损失。

可扩展的处理框架

硬件故障可能会导致流处理应用中断。为了避免这种常见情况,我们总是需要一个处理框架,该框架提供内置的 API 来支持连续计算、容错事件状态管理、故障情况下的检查点功能、运行中聚合、窗口等。幸运的是,最近的所有 Apache 项目,如 Storm、Spark、Flink 和 Kafka,都支持所有这些开箱即用的特性。开发人员可以使用 Java、Python 和 Scala 来使用这些应用编程接口。

水平可伸缩性

流处理平台应该支持水平可伸缩性。这意味着在传入数据负载较高的情况下,向集群添加更多物理服务器,以保持吞吐量服务级别协议。这样,可以通过增加更多的节点来提高处理性能,而不是在现有的服务器上增加更多的 CPU 和内存;这就叫做垂直伸缩

储存;储备

流的优选格式是键值对。JSON 和 Avro 格式很好地代表了这种格式。保存键值类型数据的首选存储是 NoSQL 数据存储,如 HBase 和 Cassandra。目前市场上总共有 100 个 NoSQL 开源数据库。选择合适的数据库非常具有挑战性,因为所有这些数据库都为数据持久性提供了一些独特的功能。几个例子是模式不可知、高度可分发、商品硬件支持、数据复制等等。

下图解释了所有流处理组件:

在本章中,我们将详细讨论消息队列和流处理框架。在下一章中,我们将集中讨论数据索引技术。

实时流组件

在接下来的部分中,我们将介绍一些重要的实时流组件。

消息队列

消息队列允许您发布和订阅事件/记录流。在我们的实时流体系结构中,有多种可供选择的消息队列。比如有 RabbitMQ、ActiveMQ、Kafka。其中,卡夫卡因其各种独特的特征而获得了巨大的知名度。因此,我们将详细讨论卡夫卡的建筑。关于 RabbitMQ 和 ActiveMQ 的讨论超出了本书的范围。

那么什么是卡夫卡呢?

Kafka 是一个快速、可扩展、持久且容错的发布-订阅消息传递系统。Apache Kafka 是一个开源的流处理项目。它提供了统一的高吞吐量,是处理实时数据流的低延迟平台。它提供了一个分布式存储层,支持大规模可扩展的发布/订阅消息队列。Kafka Connect 通过连接外部系统支持数据导入和导出。Kafka Streams 为流处理提供了 Java APIs。Kafka 与 Apache Spark、Apache Cassandra、Apache HBase、Apache Spark 等结合使用,实现实时流处理。

Apache Kafka 最初由 LinkedIn 开发,随后在 2011 年初开源。2014 年 11 月,几名在领英从事卡夫卡工作的工程师创建了一家名为 Confluent 的新公司,专注于卡夫卡。请使用此网址https://www.confluent.io/了解更多关于融合平台的信息。

卡夫卡特色

以下是卡夫卡的特点:

  • Kafka 可扩展 : Kafka 集群由多个物理服务器组成,有助于分配数据负载。在需要额外吞吐量的情况下,它很容易扩展,因为可以添加额外的服务器来维护服务级别协议。
  • Kafka 持久:在流处理过程中,Kafka 将消息持久保存在持久存储上。该存储可以是服务器本地磁盘或 Hadoop 集群。在消息处理失败的情况下,可以从磁盘访问消息并重放消息以再次处理消息。默认情况下,消息存储七天;这可以进一步配置。
  • 卡夫卡可靠:卡夫卡借助名为数据复制的功能提供消息可靠性。每个消息至少复制三次(这是可配置的),以便在数据丢失的情况下,可以使用消息的副本进行处理。
  • Kafka 支持高性能吞吐量:由于其独特的架构、分区、消息存储和水平可扩展性,Kafka 有助于每秒处理万亿字节的数据。

卡夫卡式建筑

下图展示了卡夫卡的建筑:

卡夫卡建筑组件

让我们详细看看每个组件:

  • 制作人:制作人发布特定卡夫卡主题的消息。生产者可以在每条信息记录上附上一个密钥。默认情况下,生产者以循环方式向主题分区发布消息。有时,可以将生产者配置为根据消息关键字的哈希值将消息写入特定的主题分区。
  • 主题:所有消息都存储在一个主题中。主题是记录发布到的类别或订阅源名称。主题可以与关系数据库中的表进行比较。多个使用者可以订阅一个主题来使用消息记录。
  • 分区:一个话题分为多个分区。Kafka 通过将主题划分为分区,并将每个分区放在 Kafka 集群的单独代理(服务器)上,提供了主题并行性。每个分区在存储消息的磁盘上都有一个单独的分区日志。每个分区都包含一个有序的、不可变的消息序列。每条消息都分配有唯一的序列号,称为偏移量。使用者可以从分区中的任何点读取消息,无论是从开始还是从任何偏移量。
  • 消费者:消费者订阅一个话题,消费消息。为了提高可伸缩性,可以将同一应用的使用者分组到一个使用者组中,每个使用者都可以从一个唯一的分区中读取消息。
  • 经纪人:卡夫卡被分成多个服务器叫做经纪人。所有经纪人加起来叫做卡夫卡集群。卡夫卡经纪人处理来自生产者的信息写作和来自消费者的信息阅读。卡夫卡经纪人存储了来自制作人的所有信息。默认期限为七天。此期限(保留期)可以根据要求进行配置。保留期直接影响到卡夫卡经纪人的本地存储。如果配置了更长的保留期,则需要更多的存储。保留期结束后,邮件会自动丢弃。
  • Kafka Connect :根据 Kafka 文档,Kafka Connect 允许构建和运行可重用的生产者或消费者,将 Kafka 主题连接到现有的应用或数据系统。例如,关系数据库的连接器可能捕获表的每一个变化。
  • Kafka Streams:Stream API 允许应用充当流处理器,消耗来自一个或多个主题的输入流,并产生到一个或多个输出主题的输出流,有效地将输入流转换为输出流。

卡夫卡连接深潜

卡夫卡连接是融合平台的一部分。它与卡夫卡融为一体。使用 Kafka Connect,构建从多个源到多个目标的数据管道非常容易。源连接器从另一个系统导入数据(例如,从关系数据库导入卡夫卡),而接收器连接器导出数据(例如,卡夫卡主题的内容到 HDFS 文件)。

卡夫卡连接建筑

下图展示了卡夫卡连线的架构:

数据流可以解释如下:

  • 各种信号源连接到卡夫卡连接集群卡夫卡连接集群从数据源提取数据。
  • Kafka Connect Cluster 由一组工作进程组成,这些工作进程是执行连接器的容器,任务自动相互协调以分配工作并提供可伸缩性和容错性。
  • 卡夫卡连接集群将数据推送到卡夫卡集群
  • 卡夫卡集群将数据保存在代理本地磁盘或 Hadoop 上。
  • Storm、Spark Streaming 和 Flink 等流应用从卡夫卡集群中提取数据进行流转换、聚合、连接等。这些应用可以将数据发送回 Kafka,或者将其保存到外部数据存储中,例如 HBase、Cassandra、MongoDB、HDFS 等。
  • 卡夫卡连接集群卡夫卡集群提取数据,并将其推入接收器。
  • 用户可以扩展现有的卡夫卡连接器或开发全新的连接器。

卡夫卡连接工人独立模式与分布式模式

用户可以通过两种方式运行 Kafka Connect:独立模式或分布式模式。

在独立模式下,一个进程运行所有连接器。它不是容错的。因为它只使用一个进程,所以它是不可扩展的。一般来说,它对用户的开发和测试很有用。

在分布式模式下,多个工人运行 Kafka Connect。在这种模式下,Kafka Connect 具有可伸缩性和容错性,因此用于生产部署。

让我们了解更多关于卡夫卡和卡夫卡连接(独立模式)。在本例中,我们将执行以下操作:

  1. 安装卡夫卡
  2. 创建主题
  3. 生成一些消息来验证生产者和消费者
  4. 卡夫卡连接-文件-源和文件-接收器
  5. 卡夫卡连接-JDBC-来源

下图显示了一个使用卡夫卡连接的用例:

让我们通过运行几个例子来看看卡夫卡和卡夫卡连接是如何工作的。有关更多详细信息,请使用以下链接获取卡夫卡汇的文档:https://docs.confluent.io/current/

安装卡夫卡

让我们执行以下步骤来安装卡夫卡:

  1. https://www.confluent.io/download/下载汇合
  2. 点击融合开源
  3. tar.gz下载文件confluent-oss-4.0.0-2.11.tar.gz并执行以下操作:
tar xvf confluent-oss-4.0.0-2.11.tar.gz
cd /opt/confluent-4.0.0/etc/kafka
vi server.properties
  1. 取消注释listeners=PLAINTEXT://:9092
  2. 开始汇合:
$ ./bin/confluent start schema-registry
  1. 开始zookeeper:
zookeeper is [UP]
  1. 开始kafka:
kafka is [UP]
  1. 开始schema-registry:
schema-registry is [UP]
A4774045:confluent-4.0.0 m046277$

创建主题

执行以下步骤创建主题:

  1. 列出现有主题
  2. 打开另一个终端,输入以下命令:
/opt/confluent-4.0.0
bin/kafka-topics --list --zookeeper localhost:2181
_schemas
  1. 创建主题:
bin/kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic my-first-topic

Created topic "my-first-topic"
  1. 仔细检查新创建的主题:
bin/kafka-topics --list --zookeeper localhost:2181
_schemas
my-first-topic

生成消息来验证生产者和消费者

执行以下步骤生成消息以验证生产者和消费者:

  1. 给卡夫卡发信息my-first-topic:
bin/kafka-console-producer --broker-list localhost:9092 --topic my-first-topic
test1
test2
test3
  1. 开始消费者消费消息
  2. 打开另一个终端,输入以下命令:
$ bin/kafka-console-consumer --bootstrap-server localhost:9092 --topic my-first-topic --from-beginning
test3
test2
test1
  1. 转到生产者终端并输入另一条消息:
test4
  1. 验证消费终端,查看是否能看到test4信息

卡夫卡连接使用文件源和接收器

让我们看一下如何使用文件“源”和“接收器”创建主题,这有以下帮助:

cd /opt/confluent-4.0.0/etc/kafka
vi connect-file-test-source.properties
name=local-file-source
connector.class=FileStreamSource
tasks.max=1
file=/opt/kafka_2.10-0.10.2.1/source-file.txt
topic=my-first-topic
vi connect-file-test-sink.properties
name=local-file-sink
connector.class=FileStreamSink
tasks.max=1
file=/opt/kafka_2.10-0.10.2.1/target-file.txt
topics=my-first-topic

请执行以下步骤:

  1. 启动源连接器和接收器连接器:
cd /opt/confluent-4.0.0
$ ./bin/connect-standalone config/connect-standalone.properties config/connect-file-test-source.properties config/connect-file-test-sink.properties

echo 'test-kafka-connect-1' >> source-file.txt
echo 'test-kafka-connect-2' >> source-file.txt
echo 'test-kafka-connect-3' >> source-file.txt
echo 'test-kafka-connect-4' >> source-file.txt
  1. 再次检查卡夫卡主题是否收到了消息:
$ ./bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-first-topic

test3
test1
test4

{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-1"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-2"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-3"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-4"}

test2
  1. 验证target-file.txt:
$ cat target-file.txt

{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-1"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-2"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-3"}
{"schema":{"type":"string","optional":false},"payload":"test-kafka-connect-4"}

卡夫卡连接使用 JDBC 和文件接收器连接器

下图显示了如何将数据库表中的所有记录推送到文本文件:

让我们使用卡夫卡连接实现前面的例子:

  1. 安装 SQLite:
$ sqlite3 firstdb.db

SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.

sqlite>
sqlite> CREATE TABLE customer(cust_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, cust_name VARCHAR(255));
sqlite> INSERT INTO customer(cust_id,cust_name) VALUES(1,'Jon');
sqlite> INSERT INTO customer(cust_id,cust_name) VALUES(2,'Harry');
sqlite> INSERT INTO customer(cust_id,cust_name) VALUES(3,'James');
sqlite> select * from customer;

1|Jon
2|Harry
3|James
  1. 配置 JDBC 源连接器:
cd /opt/confluent-4.0.0
vi ./etc/kafka-connect-jdbc/source-quickstart-sqlite.properties
name=test-sqlite-jdbc-autoincrement
connector.class=io.confluent.connect.jdbc.JdbcSourceConnector
tasks.max=1
connection.url=jdbc:sqlite:firstdb.db
mode=incrementing
incrementing.column.name=cust_id
topic.prefix=test-sqlite-jdbc-
  1. 配置文件接收器连接器:
cd /opt/confluent-4.0.0
vi etc/kafka/connect-file-sink.properties
name=local-file-sink
connector.class=FileStreamSink
tasks.max=1
file=/opt/confluent-4.0.0/test.sink.txt
topics=test-sqlite-jdbc-customer
  1. 启动卡夫卡连接(.jdbs源和文件接收器):
./bin/connect-standalone ./etc/schema-registry/connect-avro-standalone.properties ./etc/kafka-connect-jdbc/source-quickstart-sqlite.properties ./etc/kafka/connect-file-sink.properties
  1. 验证消费者:
$ ./bin/kafka-avro-console-consumer --new-consumer --bootstrap-server localhost:9092 --topic test-sqlite-jdbc-customer --from-beginning

--new-consumer选项已被弃用,将在未来的主要版本中删除。如果提供了--bootstrap-server选项,则默认使用新消费者:

{"cust_id":1,"cust_name":{"string":"Jon"}}
{"cust_id":2,"cust_name":{"string":"Harry"}}
{"cust_id":3,"cust_name":{"string":"James"}}
  1. 验证目标文件:
tail -f /opt/confluent-4.0.0/test.sink.txt

Struct{cust_id=1,cust_name=Jon}
Struct{cust_id=2,cust_name=Harry}
Struct{cust_id=3,cust_name=James}
  1. 在客户表中再插入几条记录:
sqlite> INSERT INTO customer(cust_id,cust_name) VALUES(4,'Susan');
sqlite> INSERT INTO customer(cust_id,cust_name) VALUES(5,'Lisa');
  1. 验证目标文件:
tail -f /opt/confluent-4.0.0/test.sink.txt

您将在目标文件中看到所有客户记录(cust_id)。使用前面的示例,您可以自定义和实验任何其他接收器。

下表显示了汇流平台上可用的卡夫卡连接器(由汇流开发并完全支持):

| 连接器名称 | 源/汇 |
| JDBC | 源和汇 |
| HDFS | Flume |
| 弹性搜索 | Flume |
| 亚马逊 S3 | Flume |

如需了解更多关于汇流公司认证的其他连接器的信息,请使用以下网址:https://www.confluent.io/product/connectors/

您一定已经观察到 Kafka Connect 是一个基于配置的流处理框架。这意味着我们只需配置源和接收器连接器文件。我们不需要使用 Java 或 Scala 等低级语言编写任何代码。但是,现在,让我们转向一个更流行的实时流处理框架,称为 Apache Storm 。让我们了解一下 Apache Storm 的一些很酷的特性。

ApacheStorm

Apache Storm 是一个免费的开源分布式实时流处理框架。在写这本书的时候,Apache Storm 的稳定发布版本是 1.0.5。Storm 框架主要是用 Clojure 编程语言编写的。最初,它是由内森·马尔斯和巴克特的团队创建和开发的。该项目后来被推特收购。

在他关于 Storm 框架的一次演讲中,Nathan Marz 谈到了使用任何框架的流处理应用,比如 Storm。这些应用涉及队列和工作线程。一些数据源线程将消息写入队列,而其他线程获取这些消息并写入目标数据存储。这里的主要缺点是源线程和目标线程彼此的数据负载不匹配,这导致了数据堆积。它还会导致数据丢失和额外的线程维护。

为了避免前面的挑战,Nathan Marz 想出了一个很棒的架构,将源线程和工作线程抽象成 tumbles 和 Bolts。这些喷口和螺栓被提交给拓扑框架,该框架负责整个流处理。

ApacheStorm 的特征

ApacheStorm 是分布式的。如果流的工作负载增加,可以将多个节点添加到 Storm 集群中,以添加更多的工作人员和更多的处理能力。

是真正的实时流处理系统,支持低延迟。事件可以在毫秒、秒或分钟内从源到达目标,具体取决于用例。

Storm 框架支持多种编程语言,但 Java 是首选。暴风是容错。即使集群中的任何节点出现故障,它也能继续运行。暴风是可靠的。它支持至少一次或恰好一次的处理。

使用 Storm 框架没有复杂性。有关更多详细信息,请参考 Storm 文档:http://storm.apache.org/releases/1.0.4/index.html

Storm 拓扑

下图显示了典型的Storm 拓扑:

Storm 拓扑组件

以下部分解释了 Storm 拓扑的所有组件:

  • 拓扑结构 : 拓扑结构是一个由喷口和螺栓组成的 DAG ( 有向无环图)与溪流分组相连。拓扑持续运行,直到被终止。
  • : 流是一个无界的元组序列。元组可以是任何数据类型。它支持所有的 Java 数据类型。
  • 流分组 : 流分组决定哪个螺栓从喷口接收元组。基本上,这些是关于流如何在不同螺栓之间流动的策略。以下是 Storm 中内置的流分组。
  • 洗牌分组 : 是默认的分组策略。元组是随机分布的,每个螺栓得到相同数量的流来处理。
  • 字段分组 : 在此策略中,一个流字段的相同值将被发送到一个螺栓。例如,如果所有元组按customer_id分组,那么相同customer_id的所有元组将被发送到一个螺栓任务,而另一个customer_id的所有元组将被发送到另一个螺栓任务。
  • 全部分组 : 在全部分组中,每个元组被发送到每个螺栓任务。当必须对同一组数据执行两种不同的功能时,可以使用它。在这种情况下,可以复制流,并且可以在数据的每个副本上计算每个函数。
  • 直接分组:这是一种特殊的分组。在这里,开发人员可以定义组件中的分组逻辑,元组本身就是在其中发出的。元组的生产者决定消费者的哪个任务将接收这个元组。
  • 自定义分组 : 开发者可以通过实现CustomGrouping方法来决定实现自己的分组策略。
  • 喷口 : 一个喷口连接到数据源,并将流吸入 Storm 拓扑。
  • 枪栓 : 喷口向枪栓发射一个元组。bolt 负责事件转换、将事件连接到其他事件、过滤、聚合和窗口。它将元组发送给另一个螺栓,或者将它保持在目标上。拓扑中的所有处理都是通过螺栓完成的。螺栓可以做任何事情,从过滤到函数、聚合、连接、与数据库对话等等。
  • Storm 星团:下图显示了一个Storm 星团的所有组成部分:

**

  • Storm 集群节点 : Storm 集群的三个主要节点是光轮、监督者和动物园管理员。下一节详细解释了所有组件。
  • 光轮节点 : 在 Storm 中,这是一个 Storm 集群的主节点。它分发代码并在集群中启动工作者任务。基本上,它为集群中的每个节点分配任务。它还监控提交的每个作业的状态。在任何作业失败的情况下,Nimbus 会将作业重新分配给集群中的不同主管。在光轮不可用的情况下,工人仍将继续工作。但是,没有 Nimbus,工人在必要时不会被重新分配到其他机器上。在节点不可用的情况下,分配给该节点的任务将超时,Nimbus 会将这些任务重新分配给其他机器。在 Nimbus 和 Supervisor 都不可用的情况下,它们需要像什么都没发生一样重新启动,并且不会影响任何工作进程。
  • 主管节点 : 在 Storm 中,这是一个从节点。它通过动物园管理员与光轮交流。它启动和停止主管内部的工作进程。例如,如果 Supervisor 发现某个特定的工作进程已经死亡,那么它会立即重新启动该工作进程。如果 Supervisor 在尝试几次后未能重新启动工作者,那么它会将此情况传达给 Nimbus,Nimbus 会在不同的 Supervisor 节点上重新启动该工作者。
  • 动物园管理员节点 : 它充当 Storm 集群中主(光轮)和从(监督者)之间的协调者。在生产环境中,通常设置一个 Zookeeper 集群,该集群有三个 Zookeeper 实例(节点)。

在单节点集群上安装 Storm

以下是在单台计算机上安装 Storm Cluster 的步骤:

  1. 安装jdk。确保你已经安装了 1.8:
$ java -version

您应该会看到以下输出:

openjdk version "1.8.0_141"
OpenJDK Runtime Environment (build 1.8.0_141-b16)
OpenJDK 64-Bit Server VM (build 25.141-b16, mixed mod
  1. 创建一个文件夹来下载 Storm 的.tar文件:
$ mkdir /opt/storm
$ cd storm
  1. 创建一个文件夹来保存动物园管理员和 Storm 数据:
$ mkdir /usr/local/zookeeper/data
$ mkdir /usr/local/storm/data
  1. 下载动物园管理员和 Storm:
$ wget http://apache.osuosl.org/zookeeper/stable/zookeeper-3.4.10.tar.gz
$ gunzip zookeeper-3.4.10.tar.gz
$ tar -xvf zookeeper-3.4.10.tar
$ wget http://mirrors.ibiblio.org/apache/storm/apache-storm-1.0.5/apache-storm-1.0.5.tar.gz
$ gunzip apache-storm-1.0.5.tar.gz
$ tar -xvf apache-storm-1.0.5.tar
  1. 配置 Zookeeper 并将以下内容设置为 Zookeeper ( zoo.cfg):
$ cd zookeeper-3.4.10
$ vi con/zoo.cfg
tickTime = 2000
dataDir = /usr/local/zookeeper/data
clientPort = 2181
  1. 按如下方式配置 Storm:
$ cd /opt/ apache-storm-1.0.5
$ vi conf/storm.yaml
  1. 添加以下内容:
storm.zookeeper.servers:
 - "127.0.0.1"
 nimbus.host: "127.0.0.1"
 storm.local.dir: "/usr/local/storm/data"
 supervisor.slots.ports:
 - 6700
 - 6701
 - 6702
 - 6703

(对于额外的工作人员,添加更多端口,如 6704 等)

  1. 启动动物园管理员:
$ cd /opt/zookeeper-3.4.10
$ bin/zkServer.sh start &amp;amp;
  1. 启动光轮:
$ cd /opt/ apache-storm-1.0.5
$ bin/storm nimbus &amp;amp;
  1. 启动主管:
$ bin/storm supervisor &amp;amp;
  1. 验证 Storm 用户界面中的安装:
http://127.0.0.1:8080

使用 Storm 开发实时流管道

在本节中,我们将创建以下三条管道:

  • 带卡夫卡-Storm- MySQL 的流媒体管道
  • 卡夫卡-Storm- HDFS -Hive 流媒体管道

在本节中,我们将看到数据流是如何从 Kafka 到 Storm 再到 MySQL 表的。

整个管道的工作原理如下:

  1. 我们将使用卡夫卡控制台-制作人应用编程接口摄取卡夫卡的客户记录(customer_firstnamecustomer_lastname)。
  2. 之后,Storm 会从卡夫卡那里拉消息。
  3. 将建立与 MySQL 的连接。
  4. Storm 将使用 MySQL-Bolt 将记录摄取到 MySQL 表中。MySQL 会自动生成customer_id
  5. MySQL 表数据(customer_idcustomer_firstnamecustomer_lastname)将使用 SQL 进行访问。

我们将开发以下 Java 类:

  • MysqlConnection.java:这个类将与本地 MySQL 数据库建立连接。
  • MysqlPrepare.java:这个类将准备要插入数据库的 SQL 语句。
  • MysqlBolt:这个类是一个 Storm 螺栓框架,把元组从 Kafka 发射到 MySQL。
  • MySQLKafkaTopology:这是一个 Storm 拓扑框架,它构建了一个工作流,将喷口(卡夫卡)绑定到螺栓(MySQL)。这里,我们使用的是一个本地 Storm 集群。

从卡夫卡到 Storm 再到 MySQL 的流媒体管道

下图显示了管道的组件。在这条管道中,我们将了解消息将如何从卡夫卡到 Storm 再到 MySQL 实时流动:

以下是MysqlConnection.java的完整 Java 代码:

package com.StormMysql;
import java.sql.Connection;
import java.sql.DriverManager;
public class MysqlConnection {
private String server_name;
 private String database_name;
 private String user_name;
 private String password;
 private Connection connection;

public MysqlConnection(String server_name, String database_name, String user_name, String password)
 {
 this.server_name=server_name;
 this.database_name=database_name;
 this.user_name=user_name;
 this.password=password;
 }

public Connection getConnection()
 {
 return connection;
 }

public boolean open()
 {
 boolean successful=true;
 try{
 Class.*forName*("com.mysql.jdbc.Driver");
 connection = DriverManager.*getConnection*("jdbc:mysql://"+server_name+"/"+database_name+"?"+"user="+user_name+"&amp;amp;password="+password);
 }catch(Exception ex)
 {
 successful=false;
 ex.printStackTrace();
 }
 return successful;
 }

public boolean close()
 {
 if(connection==null)
 {
 return false;
 }

boolean successful=true;
 try{
 connection.close();
 }catch(Exception ex)
 {
 successful=false;
 ex.printStackTrace();
 }

return successful;
 }
 }

以下是MySqlPrepare.java的完整代码:

package com.StormMysql;
import org.apache.storm.tuple.Tuple;
import java.sql.PreparedStatement;
public class MySqlPrepare {
 private MysqlConnection conn;

public MySqlPrepare(String server_name, String database_name, String user_name, String password)
 {
 conn = new MysqlConnection(server_name, database_name, user_name, password);
 conn.open();
 }

public void persist(Tuple tuple)
 {
 PreparedStatement statement=null;
 try{
 statement = conn.getConnection().prepareStatement("insert into customer (cust_id,cust_firstname, cust_lastname) values (default, ?,?)");
 statement.setString(1, tuple.getString(0));

statement.executeUpdate();
 }catch(Exception ex)
 {
 ex.printStackTrace();
 }finally {
 if(statement != null)
 {
 try{
 statement.close();
 }catch(Exception ex)
 {
 ex.printStackTrace();
 }
 }
 }
 }

public void close()
 {
 conn.close();
 }
 }

以下是MySqlBolt.java的完整代码:

package com.StormMysql;

import java.util.Map;

import org.apache.storm.topology.BasicOutputCollector;
 import org.apache.storm.topology.OutputFieldsDeclarer;
 import org.apache.storm.topology.base.BaseBasicBolt;
 import org.apache.storm.tuple.Fields;
 import org.apache.storm.tuple.Tuple;
 import org.apache.storm.tuple.Values;
 import org.apache.storm.task.TopologyContext;
 import java.util.Map;

public class MySqlBolt extends BaseBasicBolt {

private static final long *serialVersionUID* = 1L;
 private MySqlPrepare mySqlPrepare;

@Override
 public void prepare(Map stormConf, TopologyContext context)
 {
 mySqlPrepare=new MySqlPrepare("localhost", "sales","root","");
 }

public void execute(Tuple input, BasicOutputCollector collector) {
 *//* *TODO Auto-generated method stub* mySqlPrepare.persist(input);
 *//System.out.println(input);* }
@Override
 public void cleanup() {
 mySqlPrepare.close();
 }
}

以下是KafkaMySQLTopology.java的完整代码:

package com.StormMysql;
import org.apache.storm.Config;
 import org.apache.storm.spout.SchemeAsMultiScheme;
 import org.apache.storm.topology.TopologyBuilder;
 import org.apache.storm.kafka.*;
 import org.apache.storm.LocalCluster;
 import org.apache.storm.generated.AlreadyAliveException;
 import org.apache.storm.generated.InvalidTopologyException;
public class KafkaMySQLTopology
 {
 public static void main( String[] args ) throws AlreadyAliveException, InvalidTopologyException
 {
 ZkHosts zkHosts=new ZkHosts("localhost:2181");
String topic="mysql-topic";
 String consumer_group_id="id7";
SpoutConfig kafkaConfig=new SpoutConfig(zkHosts, topic, "", consumer_group_id);
kafkaConfig.scheme=new SchemeAsMultiScheme(new StringScheme());
KafkaSpout kafkaSpout=new KafkaSpout(kafkaConfig);
TopologyBuilder builder=new TopologyBuilder();
 builder.setSpout("KafkaSpout", kafkaSpout);
 builder.setBolt("MySqlBolt", new MySqlBolt()).globalGrouping("KafkaSpout");
LocalCluster cluster=new LocalCluster();
Config config=new Config();
cluster.submitTopology("KafkaMySQLTopology", config, builder.createTopology());
try{
 Thread.*sleep*(10000);
 }catch(InterruptedException ex)
 {
 ex.printStackTrace();
 }
// cluster.killTopology("KafkaMySQLTopology");
 // cluster.shutdown();
}
 }

使用pom.xml文件在 IDE 中构建您的项目。

从卡夫卡到 Storm 再到 HDFS

在本节中,我们将看到数据流将如何从卡夫卡到斯托姆再到 HDFS,并通过一个 Hive 外部表来访问它们。

下图显示了管道的组件。在这条管道中,我们将了解信息将如何实时从卡夫卡到 Storm 再到 HDFS:

整个管道的工作原理如下:

  1. 我们将使用卡夫卡控制台制作人应用编程接口摄取卡夫卡的客户记录(customer_idcustomer_firstnamecustomer_lastname)
  2. 之后,暴风将从卡夫卡那里获取信息
  3. 将建立与 HDFS 的联系
  4. “Storm”将利用 HDFS-博尔特将记录带入 HDFS
  5. 将创建 Hive 外部表来存储(customer_idcustomer_firstnamecustomer_lastname)
  6. 将使用 SQL 访问配置单元表数据(customer_idcustomer_firstnamecustomer_lastname

我们将开发以下 Java 类:

KafkaTopology.java:这是一个 Storm 拓扑框架,它构建了一个工作流,将喷点(卡夫卡)绑定到螺栓(HDFS)。这里我们使用的是本地 Storm 集群。

在前面的示例管道中,可以开发用于数据流解析和转换的多个单独的类来处理卡夫卡生产者和消费者。

以下是KafkaToplogy.java的完整 Java 代码:

package com.stormhdfs;
import org.apache.storm.Config;
 import org.apache.storm.LocalCluster;
 import org.apache.storm.generated.AlreadyAliveException;
 import org.apache.storm.generated.InvalidTopologyException;
 import org.apache.storm.hdfs.bolt.HdfsBolt;
 import org.apache.storm.hdfs.bolt.format.DefaultFileNameFormat;
 import org.apache.storm.hdfs.bolt.format.DelimitedRecordFormat;
 import org.apache.storm.hdfs.bolt.format.RecordFormat;
 import org.apache.storm.hdfs.bolt.rotation.FileRotationPolicy;
 import org.apache.storm.hdfs.bolt.rotation.FileSizeRotationPolicy;
 import org.apache.storm.hdfs.bolt.sync.CountSyncPolicy;
 import org.apache.storm.hdfs.bolt.sync.SyncPolicy;
 import org.apache.storm.kafka.KafkaSpout;
 import org.apache.storm.kafka.SpoutConfig;
 import org.apache.storm.kafka.StringScheme;
 import org.apache.storm.kafka.ZkHosts;
 import org.apache.storm.spout.SchemeAsMultiScheme;
 import org.apache.storm.topology.TopologyBuilder;
public class KafkaTopology {
 public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException {
// zookeeper hosts for the Kafka clusterZkHosts zkHosts = new ZkHosts("localhost:2181");
// Create the KafkaSpout configuartion
 // Second argument is the topic name
 // Third argument is the zookeeper root for Kafka
 // Fourth argument is consumer group id
SpoutConfig kafkaConfig = new SpoutConfig(zkHosts,
 "data-pipleline-topic", "", "id7");
// Specify that the kafka messages are String
kafkaConfig.scheme = new SchemeAsMultiScheme(new StringScheme());
// We want to consume all the first messages in the topic everytime
 // we run the topology to help in debugging. In production, this
 // property should be false
kafkaConfig.startOffsetTime = kafka.api.OffsetRequest.*EarliestTime*();
RecordFormat format = new DelimitedRecordFormat().withFieldDelimiter("|");
 SyncPolicy syncPolicy = new CountSyncPolicy(1000);
FileRotationPolicy rotationPolicy = new FileSizeRotationPolicy(1.0f,FileSizeRotationPolicy.Units.*MB*);
DefaultFileNameFormat fileNameFormat = new DefaultFileNameFormat();

fileNameFormat.withPath("/user/storm-data");

fileNameFormat.withPrefix("records-");

fileNameFormat.withExtension(".txt");

HdfsBolt bolt =
 new HdfsBolt().withFsUrl("hdfs://127.0.0.1:8020")
 .withFileNameFormat(fileNameFormat)
 .withRecordFormat(format)
 .withRotationPolicy(rotationPolicy)
 .withSyncPolicy(syncPolicy);

// Now we create the topology
TopologyBuilder builder = new TopologyBuilder();

// set the kafka spout class
builder.setSpout("KafkaSpout", new KafkaSpout(kafkaConfig), 1);

// configure the bolts
 // builder.setBolt("SentenceBolt", new SentenceBolt(), 1).globalGrouping("KafkaSpout");
 // builder.setBolt("PrinterBolt", new PrinterBolt(), 1).globalGrouping("SentenceBolt");
builder.setBolt("HDFS-Bolt", bolt ).globalGrouping("KafkaSpout");

// create an instance of LocalCluster class for executing topology in local mode.
LocalCluster cluster = new LocalCluster();
 Config conf = new Config();

// Submit topology for execution
cluster.submitTopology("KafkaTopology", conf, builder.createTopology());

try {
 // Wait for some time before exiting
System.out.println("Waiting to consume from kafka");
 Thread.sleep(10000);
 } catch (Exception exception) {
 System.out.println("Thread interrupted exception : " + exception);
 }

// kill the KafkaTopology
 //cluster.killTopology("KafkaTopology");

// shut down the storm test cluster
 // cluster.shutdown();
}
 }

的配置单元表如下:

CREATE EXTERNAL TABLE IF NOT EXISTS customer (
customer_id INT,
customer_firstname String,
customer_lastname String))
COMMENT 'customer table'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
STORED AS TEXTFILE
location '/user/storm-data';
$ hive > select * from customer;

其他流行的实时数据流框架

除了 Apache Storm,还有相当多的其他开源实时数据流框架。在这一节中,我将只简单地讨论开源的非商业框架。但是,在这一节的最后,我将提供一些商业供应商产品的网址,这些产品提供了一些非常有趣的功能。

卡夫卡溪流原料药

Kafka Streams 是一个用于构建流应用的库。Kafka Streams 是一个用于构建应用和微服务的客户端库,输入和输出数据存储在 Kafka Clusters 中。卡夫卡流应用编程接口转换和丰富数据。

以下是卡夫卡流应用编程接口的重要特性:

  • 它是开源 Apache Kafka 项目的一部分。
  • 它支持非常低延迟(毫秒)的每记录流处理。卡夫卡流应用编程接口中没有微批处理概念。进入流的每条记录都是单独处理的。
  • 它支持无状态处理(过滤和映射)、有状态处理(连接和聚合)和窗口操作(例如,计算最后一分钟、最后 5 分钟、最后 30 分钟或最后一天的数据量,等等)。
  • 要运行卡夫卡流应用编程接口,不需要构建一个有多台机器的独立集群。开发人员可以在他们的 Java 应用或微服务中使用卡夫卡流应用编程接口来处理实时数据。
  • 卡夫卡流应用编程接口是高度可扩展和容错的。
  • 卡夫卡流应用编程接口是完全不依赖于部署的。它可以部署在裸机、虚拟机、Kubernetes 容器和云上。完全没有限制。流应用编程接口从未在卡夫卡经纪人上部署。它是一个独立的应用,就像部署在 Kafka 代理之外的任何其他 Java 应用一样。
  • 它采用了卡夫卡式的安全模式。
  • 从 0.11.0 版本开始,它只支持一次语义。

让我们再次回顾前面的图像,找出卡夫卡流应用编程接口在整个卡夫卡建筑中的确切位置。

这里有几个有用的网址来详细了解卡夫卡流:

Spark 流

请注意,我们将在第 7 章大规模数据处理框架、中讨论 Spark,这是专门针对 Spark 的。但是,在这一节中,我将讨论 Spark Streaming 的一些重要特性。为了更好的理解,建议读者先学习第 7 章大规模数据处理框架,回来进一步阅读本节,了解更多关于 Spark Streaming 的内容。

通常的做法是使用 Hadoop MapReduce 进行批处理,使用 Apache Storm 进行实时流处理。

使用这两种不同的编程模型会导致代码大小、需要修复的 bug 数量和开发工作量的增加;它还引入了一条学习曲线,并引发了其他问题。Spark Streaming 有助于解决这些问题,并提供了一个可扩展的、高效的、有弹性的和集成的(带有批处理)系统。

Spark Streaming 的优势在于其与批处理相结合的能力。可以使用普通的 Spark 编程创建一个 RDD,并将其与 Spark 流结合起来。此外,代码库是相似的,如果需要的话,允许容易的迁移——并且从 Spark 没有学习曲线。

Spark 流是核心 Spark 应用编程接口的扩展。它扩展了 Spark 来进行实时流处理。Spark 流具有以下特征:

  • 它是可扩展的,可以在数百个节点上扩展
  • 它提供高吞吐量并实现二级延迟
  • 它是容错的,可以有效地从故障中接收信息
  • 它集成了批处理和交互式数据处理

Spark Streaming 将数据流应用作为一系列非常小的确定性批处理作业进行处理。

Spark Streaming 用 Scala、Java 和 Python 提供了一个应用编程接口。Spark Streaming 根据时间将实时数据流分为多个批次。时间范围可以从一秒到几分钟/小时。一般来说,批次分为几秒钟。Spark 将每个批处理视为一个 RDD,并基于 RDD 操作(映射、筛选、连接平面图、distinct、reduceByKey 等)处理每个批处理。最后,RDDs 的处理结果被分批返回。

下图描述了 Spark 流数据流:

这里有几个有用的网址来详细了解 Spark 流:

Apache 很不错

Apache Flink 的文档是这样描述 Flink 的:Flink 是一个开源的分布式流处理框架。

Flink 提供准确的结果,并支持无序或延迟到达的数据集。它是有状态和容错的,可以从故障中无缝恢复,同时保持一次应用状态。它可以大规模运行,在数千个节点上运行,具有非常好的吞吐量和延迟特性。

以下是 Apache Flink 的功能:

  • Flink 保证有状态计算的语义只有一次
  • Flink 支持流处理和带有事件时间语义的窗口
  • 除了数据驱动窗口之外,Flink 还支持基于时间、计数或会话的灵活窗口
  • Flink 能够实现高吞吐量和低延迟
  • Flink 的保存点提供了一种状态版本控制机制,使更新应用或重新处理历史数据成为可能,而不会丢失状态,停机时间也最少
  • Flink 被设计为在具有数千个节点的大规模集群上运行,除了独立集群模式之外,Flink 还提供了对 Yarn 和 Mesos 的支持

Flink 的核心是分布式流式数据流引擎。它支持一次处理一个流,而不是一次处理整批流。

Flink 支持以下库:

  • 圆概率误差(circular error probable)
  • 机器学习
  • 图形处理
  • ApacheStorm 兼容性

Flink 支持以下 API:

  • 数据流应用编程接口:这个应用编程接口帮助所有的流、转换,也就是过滤、聚合、计数和窗口
  • 数据集 API :这个 API 帮助所有的批处理数据转换,也就是连接、分组、映射和过滤
  • 表 API :支持关系数据流上的 SQL
  • 流 SQL :支持批量和流表的 SQL

下图描述了 Flink 编程模型:

下图描述了 Flink 架构:

以下是 Flink 编程模型的组成部分:

  • 来源:收集数据并发送给 Flink 引擎的数据源
  • 转换:在这个组件中,整个转换发生
  • 接收器:发送已处理流的目标

这里有几个有用的网址来详细了解 Spark 流:

在接下来的部分中,我们将看一看各种流框架的比较。

Apache 弗林克对 Spark

Spark 流的主要焦点是流批处理操作,称为微批处理。这种编程模型适合许多用例,但并不是所有用例都需要亚秒延迟的实时流处理。例如,信用卡欺诈防范之类的用例需要毫秒级延迟。因此,微批处理编程模型不适合那里。(但是,最新版本的 Spark 2.4 支持毫秒级数据延迟)。

Apache Flink 支持毫秒级延迟,适合欺诈检测等用例。

ApacheSpark 对 Storm

Spark 使用微批处理来处理事件,而 Storm 则逐个处理事件。这意味着 Spark 有几秒钟的延迟,而 Storm 提供了一毫秒的延迟。Spark 流提供了一个高级抽象,称为离散流数据流,它代表一个连续的 rdd 序列。(但是,最新版本的 Spark,2.4 支持毫秒级的数据延迟。)最新的 Spark 版本支持数据帧。

几乎相同的代码(应用编程接口)可以用于 Spark 流和 Spark 批处理作业。这有助于重用两种编程模型的大部分代码库。此外,Spark 支持机器学习和图形应用编程接口。所以,同样的代码库也可以用于那些用例。

摘要

在本章中,我们首先详细了解了实时流处理概念,包括数据流、批处理与实时处理、CEP、低延迟、连续可用性、水平可扩展性、存储等。后来,我们了解了 Apache Kafka,它是现代实时流数据管道的一个非常重要的组成部分。Kafka 的主要特点是可扩展性、耐用性、可靠性和高吞吐量。

我们还了解了卡夫卡连接;它的体系结构、数据流、源和连接器。我们研究了使用文件源、文件接收器、JDBC 源和文件接收器连接器设计数据管道的案例研究。

在后面的章节中,我们学习了各种开源实时流处理框架,比如 Apache Storm 框架。我们也看到了一些实际的例子。Apache Storm 是分布式的,支持低延迟和多种编程语言。Storm 是容错和可靠的。它支持至少一次或恰好一次的处理。

Spark Streaming 有助于解决这些问题,并提供了一个可扩展的、高效的、有弹性的和集成的(带有批处理)系统。Spark Streaming 的优势在于其与批处理相结合的能力。Spark 流是可扩展的,并提供高吞吐量。它支持二级延迟的微批处理,具有容错能力,并集成了批处理和交互式数据处理。

Apache Flink 保证一次语义,支持事件时间语义,高吞吐量和低延迟。它被设计成在大规模集群上运行。**

七、大规模数据处理框架

随着数据源的数量和复杂性不断增加,从数据中获取价值也变得越来越困难。自从 Hadoop 诞生以来,它已经构建了一个可大规模扩展的文件系统,HDFS。它采用了来自函数式编程的 MapReduce 概念来应对大规模数据处理挑战。随着技术不断发展以克服数据挖掘带来的挑战,企业也在寻找方法来迎接这些变化以保持领先地位。

在本章中,我们将重点介绍这些数据处理解决方案:

  • MapReduce
  • ApacheSpark
  • Spark SQL
  • Spark 流

MapReduce

MapReduce 是从函数式编程中借用的一个概念。数据处理分为地图阶段和缩减阶段,在地图阶段进行数据准备,在缩减阶段计算实际结果。MapReduce 发挥重要作用的原因是,当数据被分割到多个分布式服务器时,我们可以实现巨大的并行性。如果没有这个优势,MapReduce 就无法真正发挥出色。

让我们举一个简单的例子来理解 MapReduce 在函数式编程中是如何工作的:

  • 使用我们选择的映射函数来处理输入数据
  • 映射器函数的输出应该处于 reduce 函数可消耗的状态
  • 映射器函数的输出被馈送到 reduce 函数,以生成必要的结果

让我们用一个简单的程序来理解这些步骤。该程序使用以下文本(随机创建)作为输入:

Bangalore,Onion,60
Bangalore,Chilli,10
Bangalore,Pizza,120
Bangalore,Burger,80
NewDelhi,Onion,80
NewDelhi,Chilli,30
NewDelhi,Pizza,150
NewDelhi,Burger,180
Kolkata,Onion,90
Kolkata,Chilli,20
Kolkata,Pizza,120
Kolkata,Burger,160

输入由以下字段组成的数据:城市名称产品名称当日商品价格

我们想写一个程序,显示给定城市所有产品的总成本。这可以通过多种方式实现。但是让我们尝试使用 MapReduce 来处理这个问题,看看它是如何工作的。

映射程序是这样的:

#!/usr/bin/env perl -wl

use strict;
use warnings;

while(<STDIN>) {
    chomp;
    my ($city, $product, $cost) = split(',');
    print "$city $cost";
}

减少程序是:

#!/usr/bin/perl

use strict;
use warnings;

my %reduce;

while(<STDIN>) {
    chomp;
    my ($city, $cost) = split(/\s+/);
    $reduce{$city} = 0 if not defined $reduce{$city};
    $reduce{$city} += $cost;
}

print "-" x 24;
printf("%-10s : %s\n", "City", "Total Cost");
print "-" x 24;

foreach my $city (sort keys %reduce) {
    printf("%-10s : %d\n", $city, $reduce{$city});
}

我们使用 UNIX 终端创建了一个数据管道,如下所示:

[user@node-1 ~]$ cat input.txt | perl map.pl | perl reduce.pl 
------------------------
City : Total Cost
------------------------
Bangalore : 270
Kolkata : 390
NewDelhi : 440

我们可以看到,结果是意料之中的。这是一个非常简单的 MapReduce 案例。让我们试着看看发生了什么:

  • 每个输入行都由map.pl程序处理,并打印城市和价格
  • map.pl程序的输出被馈送到reduce.pl,它对所有记录执行SUM()操作,并按城市对它们进行分类

让我们洗牌input.txt看看我们是否得到想要的结果。

下面是修改后的input.txt:

Bangalore,Onion,60
NewDelhi,Onion,80
Bangalore,Pizza,120
Bangalore,Burger,80
Kolkata,Onion,90
Kolkata,Pizza,120
Kolkata,Chilli,20
NewDelhi,Chilli,30
NewDelhi,Burger,180
Kolkata,Burger,160
NewDelhi,Pizza,150
Bangalore,Chilli,10

MapReduce 操作的输出是:

[user@node-1 ~]$ cat input-shuffled.txt | perl map.pl | perl reduce.pl 
------------------------
City : Total Cost
------------------------
Bangalore : 270
Kolkata : 390
NewDelhi : 440

没有区别,因为映射和缩减操作都是一次性独立执行的。这里没有数据并行。整个过程可以在该图中可视化:

我们可以看到,贴图阶段后有一个输入数据的副本,还原阶段后的最终输出才是我们感兴趣的。

运行单线程进程是有用的,当我们不需要处理大量数据时,它是必需的。当输入大小是无界的,无法放入单个服务器时,我们需要开始考虑分布式/并行算法来解决手头的问题。

Hadoop MapReduce

Apache MapReduce 是一个框架,它使我们更容易在非常大的分布式数据集上运行 MapReduce 操作。Hadoop 的优势之一是分布式文件系统,它支持机架感知和可扩展。Hadoop 作业调度器足够智能,可以确保计算发生在数据所在的节点上。这也是一个非常重要的方面,因为它减少了网络 IO 的数量。

让我们看看这个框架是如何在这个图表的帮助下使大规模并行计算变得更加容易的:

这个图看起来比上一个图复杂一点,但是大部分事情都是 Hadoop MapReduce 框架自己为我们完成的。我们仍然编写映射和减少输入数据的代码。

让我们从上图中详细看看当我们使用 Hadoop MapReduce 框架处理数据时会发生什么:

  • 我们的输入数据被分解成几部分
  • 每一条数据都被输入到一个映射程序
  • 来自所有映射程序的输出被收集、混洗和排序
  • 每一个被分类的零件都被输入到减速器程序
  • 来自所有减速器的输出被组合以生成输出数据

流式 MapReduce

流式 MapReduce 是 Hadoop MapReduce 框架中可用的功能之一,我们可以使用任何外部程序来充当映射器和缩减器。只要这些程序可以由目标操作系统执行,它们就可以运行映射和缩减任务。

在编写这些程序时,需要记住以下几点:

  • 这些程序应该从STDIN读取输入
  • 他们应该能够处理无限量的数据(流),否则就会崩溃
  • 在将这些程序用于流式 MapReduce 之前,应该提前了解它们的内存需求,否则我们可能会看到不可预测的行为

在前一节中,我们已经编写了简单的 Perl 脚本来进行映射和简化。同样在当前场景中,我们将使用相同的程序来理解它们如何执行我们的任务。

If you observe carefully, map.pl can process infinite amounts of data and will not have any memory overhead. But the reduce.pl program uses the Perl Hash data structure to perform the reduction operation. Here, we might face some memory pressure with real-world data.

在本练习中,我们使用随机输入数据,如下所示:

[user@node-3 ~]$ cat ./input.txt
 Bangalore,Onion,60
 NewDelhi,Onion,80
 Bangalore,Pizza,120
 Bangalore,Burger,80
 Kolkata,Onion,90
 Kolkata,Pizza,120
 Kolkata,Chilli,20
 NewDelhi,Chilli,30
 NewDelhi,Burger,180
 Kolkata,Burger,160
 NewDelhi,Pizza,150
 Bangalore,Chilli,10

稍后,我们需要将映射器和缩减器脚本复制到所有 Hadoop 节点:

We are using the same Hadoop cluster that's built as part of Chapter 10, Production Hadoop Cluster Deployment for this exercise. If you remember, the nodes are master, node-1, node-2, and node-3.

[user@master ~]$ scp *.pl node-1:~
[user@master ~]$ scp *.pl node-2:~
[user@master ~]$ scp *.pl node-3:~

在这一步中,我们将输入复制到hadoop /tmp/ directory

Please use a sensible directory in your production environments as per your enterprise standards. Here the /tmp directory is used for illustration purposes only.

[user@node-3 ~]$ hadoop fs -put ./input.txt /tmp/

在这一步中,我们使用 Hadoop 流式 MapReduce 框架来使用我们的脚本来执行计算:

The contents of the map.pl and reduce.pl are exactly the same as we have used in the previous examples.

[user@node-3 ~]$ hadoop jar \
    /usr/hdp/current/hadoop-mapreduce-client/hadoop-streaming.jar \
    -input hdfs:///tmp/input.txt \
    -output hdfs:///tmp/output-7 \
    -mapper $(pwd)/map.pl \
    -reducer $(pwd)/reduce.pl

输出存储在 HDFS,我们可以这样查看:

[user@node-3 ~]$ hadoop fs -cat /tmp/output-7/part*
 NewDelhi, 440
 Kolkata, 390
 Bangalore, 270
[user@node-3 ~]$

如果我们仔细观察,结果与我们的传统程序完全匹配。

Java MapReduce

在前一节中,我们已经看到了如何使用任意编程语言在 Hadoop 上运行 MapReduce 操作。但是在大多数实际场景中,如果我们利用 Hadoop MapReduce 基础架构提供的库是很好的,因为它们功能强大,能够满足我们的许多需求。

让我们尝试使用 MapReduce 库编写一个简单的 Java 程序,看看我们是否能生成与前面练习中相同的输出。在这个例子中,我们将使用来自官方文档的官方 MapReduce 实现。

文档位于:https://Hadoop . Apache . org/docs/r 2 . 8 . 0/Hadoop-MapReduce-client/Hadoop-MapReduce-client-core/MapReduce tutorial . html

由于我们的输入与示例有很大不同,并且我们还想找到给定城市中所有产品的总价,因此我们必须根据我们的 CSV input.txt文件更改映射程序。reduce 函数与官方文档中的函数相同,我们的 mapper 函数会生成一个<City, Price>对。这很容易被现有的实现所消耗。

我们已经调用了我们的程序TotalPrice.java。让我们看看源代码的样子:

[user@node-3 ~]$ cat TotalPrice.java 
import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class TotalPrice {
  public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
    public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString(), ",");
      Text city = new Text(itr.nextToken());
      itr.nextToken();
      IntWritable price = new IntWritable(Integer.parseInt(itr.nextToken()));
      context.write(city, price);
    }
  }

  public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
  private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }

  public static void main(String[] args) throws Exception {
    Configuration conf = new Configuration();
    Job job = Job.getInstance(conf, "TotalPriceCalculator");
    job.setJarByClass(TotalPrice.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

一旦我们有了源代码,我们需要编译它来创建一个 Java Archive ( JAR )文件。它以下列方式完成:

 [user@node-3 ~]$ javac -cp `hadoop classpath` TotalPrice.java 
 [user@node-3 ~]$ jar cf tp.jar TotalPrice*.class

一旦我们创建了 JAR 文件,我们就可以使用 Hadoop 命令提交作业来处理input.txt,并在/tmp/output-12目录中产生输出:

As in the case of streaming MapReduce, we need not copy the source to all the Hadoop servers.

 [user@node-3 ~]$ hadoop jar tp.jar TotalPrice /tmp/input.txt /tmp/output-12

这个运行应该通过 fine,并将产生/tmp/output-12目录中的输出文件。我们可以使用以下命令查看输出内容:

[user@node-3 ~]$ hadoop fs -cat /tmp/output-12/part*
Bangalore       270
Kolkata 390
NewDelhi        440

这也与之前的运行完全一致。

正如我们所看到的,Hadoop Mapreduce 框架已经采取了所有必要的步骤来确保整个管道进度保持在其控制范围内,从而为我们提供了期望的结果。

即使我们使用了一个非常简单的数据集进行计算,Hadoop Mapreduce 也能确保无论我们处理的数据大小如何,我们之前编写的相同程序都会产生我们想要的结果。这使得它成为一个非常强大的批处理作业架构。

摘要

到目前为止,我们已经看到 Hadoop Mapreduce 是一个强大的框架,它提供了流式和批处理操作模式,以非常简单的指令处理大量数据。尽管 Mapreduce 最初是 Hadoop 中计算框架的选择,但它未能满足市场不断变化的需求,因此开发了新的架构来解决这些问题。在下一节中,我们将学习一个名为 Apache Spark 的框架。

ApacheSpark 2

Apache Spark 是一个通用的集群计算系统。它非常适合大规模数据处理。当完全在内存中运行时,它的性能比 Hadoop 高 100 倍,当完全从磁盘运行时,它的性能比 Hadoop 高 10 倍。它有一个复杂的有向无环图执行引擎,支持无环数据流模型。

Apache Spark 拥有用 Java、Scala、Python 和 R 编程语言编写程序的一流支持,以迎合更广泛的受众。它提供了 80 多种不同的运营商来构建并行应用,而不用担心底层基础设施。

Apache Spark 拥有迎合结构化查询语言的库,被称为 SparkSQL;这支持在使用 ANSI SQL 的程序中编写查询。它还支持计算流数据,这在当今的实时数据处理需求中非常需要,例如为交互式用户体验系统的仪表板供电。Apache Spark 也有机器学习库,比如 Mlib ,它迎合了运行科学程序的需求。然后它支持为遵循图形数据结构的数据编写程序,称为 GraphX 。这使得它成为一个真正强大的框架,支持最先进的计算方式。

Apache Spark 不仅运行在 Hadoop 平台上,还运行在各种系统上,如 Apache Mesos、Kubernetes、Standalone 或 Cloud。这使得今天的企业选择它想要利用这个系统的力量的方式成为一个完美的选择。

在接下来的部分中,我们将了解更多关于 Spark 及其生态系统的信息。我们在本练习中使用的是 Spark 2.2.0。

使用安巴里安装 Spark

从上一章开始,我们有一个正在运行的现有 Ambari 安装。我们将利用相同的安装来增加 Spark 支持。让我们看看如何实现这一点。

安巴里管理中的服务选择

一旦我们登录到安巴里管理界面,我们会看到创建的主集群。在这个页面上,我们点击左侧菜单上的操作按钮。它显示如下屏幕。从该菜单中,我们单击添加服务选项:

添加服务向导

一旦我们点击添加服务菜单项,我们会看到一个向导,我们必须从安巴里所有支持的服务列表中选择 Spark 2。屏幕如下所示:

服务选择完成后,单击下一步按钮。

服务器放置

一旦选择了 Spark 2 服务,其他相关服务也会自动为我们选择,我们可以选择主服务器的位置。我保留默认选择不变:

当更改看起来不错时,单击“下一步”按钮。

客户和奴隶选择

在这一步中,我们可以选择作为我们在上一步中选择的主节点的客户端的节点列表。我们还可以选择可以安装客户端实用程序的服务器列表。根据您的选择进行选择:

更改完成后,单击“下一步”按钮。

服务定制

由于 Hive 也作为 Spark 2 选择的一部分被安装,我们可以选择定制 Hive 数据源的细节。我在主节点上创建了数据库,用户名为hive,密码为hive,数据库也为hive。在生产中进行更改时,请选择一个强密码。

定制屏幕如下所示:

正确完成更改后,单击下一步。

软件部署

在此屏幕中,我们看到了迄今为止所做选择的摘要。单击部署开始在选定的服务器上部署 Spark 2 软件。如果我们觉得我们错过了任何自定义,我们可以随时取消向导并在此步骤中重新开始:

Spark 安装进度

在这一步中,我们将看到 Spark 软件安装的进度及其其他依赖关系。一旦部署了所有内容,我们会看到任何警告和错误的摘要。从下面的屏幕中我们可以看到,在安装过程中遇到了一些警告,这表明一旦向导完成,我们需要重新启动一些服务。别担心,看到这些错误是很正常的。我们将在接下来的步骤中纠正这些错误,以使 Spark 系统成功运行:

单击完成完成向导。

服务重新启动和清理

由于在安装过程中出现警告,我们必须重新启动所有受影响的组件。该屏幕显示重启过程:

一旦我们给出一个确认,所有相关的服务将重新启动,我们将有一个成功运行的系统。

这就完成了 Spark 2 在由 Ambari 管理的现有 Hadoop 集群上的安装。在接下来的部分中,我们将了解更多关于 Spark 中各种数据结构和库的信息。

Apache Spark 数据结构

尽管 Mapreduce 提供了一种处理大量数据的强大方法,但由于以下几个缺点,它受到了限制:

  • 缺乏对各种运营商的支持
  • 实时数据处理
  • 缓存数据结果以加快迭代速度

仅举几个例子。自从 Apache Spark 从头开始构建以来,它一直以非常通用的方式处理大数据计算问题,并为开发人员提供了数据结构,使其更容易表示任何类型的数据,并以更好的方式使用这些数据进行计算。

关系数据库、数据框架和数据集

Apache Spark 的核心是被称为 RDD 的分布式数据集,也被称为弹性分布式数据集。这些是集群中存在的不可变数据集,具有高可用性和容错性。RDD 中的元素可以并行运行,为 Spark 簇提供大量能量。

由于数据已经存在于存储系统中,如 HDFS、关系数据库管理系统、S3 等,因此可以从这些外部数据源轻松创建关系数据库。该应用编程接口还为我们提供了从现有内存数据元素创建关系数据库的能力。

这些关系数据库没有任何预定义的结构。因此,它们可以采取任何形式,并且通过利用 Spark 库中的不同运算符,我们可以编写强大的程序,为我们提供必要的结果,而不必太担心数据的复杂性。

为了满足关系数据库管理系统的需求,数据帧开始发挥作用,其中数据帧可以与关系数据库系统中的表进行比较。我们知道,表有行和列,数据的结构是提前知道的。通过了解数据的结构,可以在数据处理过程中进行一些优化。

Spark 数据集与数据框有些相似。但是它们通过用本地语言对象(Java 和 Scala)支持半结构化数据对象来扩展数据框架的功能。数据帧是具有关系模式语义的不可变对象集合。因为我们处理的是半结构化数据和本地语言对象,所以有一个编码器/解码器系统负责在类型之间自动转换。

下面是一个快速对比图:

| 功能 | RDDs | 数据帧 | 数据集 |
| 数据类型 | 非结构化数据 | 结构数据 | 半结构化数据 |
| 模式要求 | 完全自由形式 | 严格的数据类型 | 松散耦合 |
| Spark 提供的优化 | 不需要,因为数据是非结构化的 | 利用优化,因为数据类型是已知的 | 推断的数据类型提供了某种程度的优化 |
| 高级表达式/过滤器 | 因为数据形式本质上是复杂的,所以很难 | 我们可以利用这些,因为我们知道我们正在处理的数据 | 也可以在这里借力 |

Apache Spark 编程

Apache Spark 有非常好的编程语言支持。它为 Java、Scala、Python 和 R 编程语言提供了一流的支持。尽管编程语言中可用的数据结构和运算符本质上是相似的,但我们必须使用特定于编程语言的结构来实现所需的逻辑。在本章中,我们将使用 Python 作为首选编程语言。然而,Spark 本身对这些编程语言是不可知的,无论使用哪种编程语言,都会产生相同的结果。

使用 Python 的 Apache Spark 可以有两种不同的使用方式。第一种方式是启动pyspark交互外壳,帮助我们运行 Python 指令。体验类似于 Python 外壳实用程序。另一种方法是编写可以使用 spark-submit 命令调用的独立程序。为了使用独立的 Spark 程序,我们必须了解 Spark 程序的基本结构:

spark 程序的典型结构由一个主要函数组成,该函数在 RDDs 上执行不同的运算符来生成所需的结果。Spark 库中支持 80 多种不同类型的操作员。在高层次上,我们可以将这些操作符分为两种类型:转换和动作。转换运算符将数据从一种形式转换为另一种形式。操作从数据中生成结果。出于性能原因,为了优化集群中的资源,Apache Spark 实际上在检查点执行程序。每个检查点只有在有操作符时才会到达。这是一件需要记住的重要事情,尤其是如果你是使用 Spark 编程的新手。即使是最高级的程序员有时也会困惑为什么他们没有看到期望的结果,因为他们没有对数据使用任何操作符。

回到前面的图表,我们有一个驱动程序,它有一个主例程,对存储在像 HDFS 这样的文件系统中的数据执行几个动作/转换,并给出我们想要的结果。我们知道 RDDs 是 Spark 编程语言中的基本并行数据存储。Spark 足够智能,可以像 HDFS 一样从种子存储中创建这些 rdd,一旦创建,它就可以将这些 rdd 缓存在内存中,并通过使这些 rdd 容错来使其高度可用。即使 RDD 的拷贝由于节点崩溃而离线,相同 RDDs 上的未来访问也将从最初生成它的计算中快速生成。

用于分析的样本数据

为了理解 spark 的编程 API,我们应该有一个样本数据集,在这个数据集上我们可以执行一些操作来获得信心。为了生成这个数据集,我们将从上一章的 employees 数据库中导入示例表。

以下是我们生成该数据集的说明:

登录服务器并切换到 Hive 用户:

ssh user@node-3
[user@node-3 ~]$ sudo su - hive

这将把我们放在一个远程 shell 中,我们可以从 MySQL 数据库转储该表:

[hive@node-3 ~]$ mysql -usuperset -A -psuperset -h master employees -e "select * from vw_employee_salaries" > vw_employee_salaries.tsv
[hive@node-3 ~]$ wc -l vw_employee_salaries.tsv 
2844048 vw_employee_salaries.tsv
[hive@node-3 ~]$ 

接下来,我们应该使用以下命令将文件复制到 Hadoop:

[hive@node-3 ~]$ hadoop fs -put ./vw_employee_salaries.tsv /user/hive/employees.csv

现在,数据准备工作已经完成,我们已成功将其复制到 HDFS。我们可以用 Spark 开始使用这些数据。

使用 pyspark 进行交互式数据分析

Apache Spark 发行版附带一个名为 pyspark 的交互式外壳。由于我们处理的是像 Python 这样的解释编程语言,所以我们可以在学习的同时编写交互式程序。

如果你还记得,我们已经安装了带有 Apache Ambari 的 Spark。因此,我们必须遵循 Apache Ambari 的标准目录位置来访问 Spark 相关的二进制文件:

[hive@node-3 ~]$ cd /usr/hdp/current/spark2-client/
[hive@node-3 spark2-client]$ ./bin/pyspark 
Python 2.7.5 (default, Aug  4 2017, 00:39:18) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /__ / .__/\_,_/_/ /_/\_\   version 2.2.0.2.6.4.0-91
      /_/

Using Python version 2.7.5 (default, Aug  4 2017 00:39:18)
SparkSession available as 'spark'.
>>> 

前面的步骤启动了交互式 Spark 外壳。

作为理解 Spark 数据结构的第一步,我们将从 HDFS 加载employees.csv文件,并使用以下指令计算文件中的总行数:

>>> ds = spark.read.text("employees.csv")
>>> ds.count()
2844048                                                                         
>>> 

如我们所见,该计数与 Unix shell 上之前的加载操作相匹配。

现在,让我们尝试从文件中加载前五条记录,并尝试查看数据结构对象的模式:

>>> ds.first()
Row(value=u'emp_no\tbirth_date\tfirst_name\tlast_name\tgender\thire_date\tsalary\tfrom_date\tto_date')
>>> ds.head(5)
[Row(value=u'emp_no\tbirth_date\tfirst_name\tlast_name\tgender\thire_date\tsalary\tfrom_date\tto_date'), Row(value=u'10001\t1953-09-02\tGeorgi\tFacello\tM\t1986-06-26\t60117\t1986-06-26\t1987-06-26'), Row(value=u'10001\t1953-09-02\tGeorgi\tFacello\tM\t1986-06-26\t62102\t1987-06-26\t1988-06-25'), Row(value=u'10001\t1953-09-02\tGeorgi\tFacello\tM\t1986-06-26\t66074\t1988-06-25\t1989-06-25'), Row(value=u'10001\t1953-09-02\tGeorgi\tFacello\tM\t1986-06-26\t66596\t1989-06-25\t1990-06-25')]
>>> ds.printSchema()
root
 |-- value: string (nullable = true)

>>> 

正如我们所看到的,即使我们有一个 CSV(制表符分隔的文件),Spark 已经将该文件读取为由换行符分隔的普通文本文件,并且该模式只包含一个值,该值是字符串数据类型。

在这种操作模式下,我们将每条记录视为一行,我们只能执行几种类型的操作,例如计算给定名称的所有出现次数:

>>> ds.filter(ds.value.contains("Georgi")).count()
2323                                                                            
>>> 

这种操作模式有点类似于日志处理。但是 Spark 真正的力量来自于把数据当作一个有行有列的表,也就是 DataFrames 的力量:

>>> ds = spark.read.format("csv").option("header", "true").option("delimiter", "\t").load("employees.csv")
>>> ds.count()
2844047   
>>> ds.show(5)
+------+----------+----------+---------+------+----------+------+----------+----------+
|emp_no|birth_date|first_name|last_name|gender| hire_date|salary| from_date|   to_date|
+------+----------+----------+---------+------+----------+------+----------+----------+
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 60117|1986-06-26|1987-06-26|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 62102|1987-06-26|1988-06-25|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 66074|1988-06-25|1989-06-25|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 66596|1989-06-25|1990-06-25|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 66961|1990-06-25|1991-06-25|
+------+----------+----------+---------+------+----------+------+----------+----------+
only showing top 5 rows

>>> 
>>> ds.printSchema()
root
 |-- emp_no: string (nullable = true)
 |-- birth_date: string (nullable = true)
 |-- first_name: string (nullable = true)
 |-- last_name: string (nullable = true)
 |-- gender: string (nullable = true)
 |-- hire_date: string (nullable = true)
 |-- salary: string (nullable = true)
 |-- from_date: string (nullable = true)
 |-- to_date: string (nullable = true)

>>> 

现在,我们可以看到 Spark 已经自动将输入的 CSV 文本转换为数据帧。但所有字段都被视为字符串。

让我们尝试使用 spark 的模式推断功能来自动查找字段的数据类型:

>>> ds = spark.read.format("csv").option("header", "true").option("delimiter", "\t").option("inferSchema", "true").load("employees.csv")
18/03/25 19:21:15 WARN FileStreamSink: Error while looking for metadata directory.
18/03/25 19:21:15 WARN FileStreamSink: Error while looking for metadata directory.
>>> ds.count()                                                                  
2844047                                                                         
>>> ds.show(2)
+------+-------------------+----------+---------+------+-------------------+------+-------------------+-------------------+
|emp_no|         birth_date|first_name|last_name|gender|          hire_date|salary|          from_date|            to_date|
+------+-------------------+----------+---------+------+-------------------+------+-------------------+-------------------+
| 10001|1953-09-02 00:00:00|    Georgi|  Facello|     M|1986-06-26 00:00:00| 60117|1986-06-26 00:00:00|1987-06-26 00:00:00|
| 10001|1953-09-02 00:00:00|    Georgi|  Facello|     M|1986-06-26 00:00:00| 62102|1987-06-26 00:00:00|1988-06-25 00:00:00|
+------+-------------------+----------+---------+------+-------------------+------+-------------------+-------------------+
only showing top 2 rows

>>> ds.printSchema()
root
 |-- emp_no: integer (nullable = true)
 |-- birth_date: timestamp (nullable = true)
 |-- first_name: string (nullable = true)
 |-- last_name: string (nullable = true)
 |-- gender: string (nullable = true)
 |-- hire_date: timestamp (nullable = true)
 |-- salary: integer (nullable = true)
 |-- from_date: timestamp (nullable = true)
 |-- to_date: timestamp (nullable = true)

>>> 

现在我们可以看到,所有字段都有一个最接近 MySQL 表定义的正确数据类型。

我们可以对数据应用简单的操作来查看结果。让我们试着找出男性记录总数:

>>> ds.filter(ds.gender == "M").count()
1706321 

此外,尝试找到工资超过 10 万美元的男性记录:

>>> ds.filter(ds.gender == "M").filter(ds.salary > 100000).count()
57317   

就这么简单,对吧?在官方的 Spark 文档中,有更多的操作符可供探索。

带 Spark 的独立应用

在前一节中,我们已经看到了如何使用交互外壳pyspark来学习 Spark Python API。在本节中,我们将编写一个简单的 Python 程序,在 Spark 集群上运行。在现实场景中,这是我们在 Spark 集群上运行应用的方式。

为了做到这一点,我们将编写一个名为MyFirstApp.py的程序,内容如下:

[hive@node-3 ~]$ cat MyFirstApp.py 
from pyspark.sql import SparkSession

# Path to the file in HDFS
csvFile = "employees.csv"

# Create a session for this application
spark = SparkSession.builder.appName("MyFirstApp").getOrCreate()

# Read the CSV File
csvTable = spark.read.format("csv").option("header", "true").option("delimiter", "\t").load(csvFile)

# Print the total number of records in this file
print "Total records in the input : {}".format(csvTable.count())

# Stop the application
spark.stop()
[hive@node-3 ~]$ 

为了在 Spark 集群上运行这个程序,我们必须使用 spark-submit 命令,它在调度和协调整个应用生命周期方面做了必要的工作:

[hive@node-3 ~]$ /usr/hdp/current/spark2-client/bin/spark-submit ./MyFirstApp.py 2>&1 | grep -v -e INFO -e WARN
Total records in the input : 2844047

正如预期的那样,这些是我们的输入文件中的记录总数(不包括标题行)。

Spark 流应用

spark 的强大功能之一是构建处理实时流数据并产生实时结果的应用。为了更好地理解这一点,我们将编写一个简单的应用,试图在输入流中找到重复的消息,并打印所有唯一的消息。

当我们处理不可靠的数据流,并且我们只想提交唯一的数据时,这种应用很有帮助。

这里给出了这个应用的源代码:

[hive@node-3 ~]$ cat StreamingDedup.py 
from pyspark import SparkContext
from pyspark.streaming import StreamingContext

context = SparkContext(appName="StreamingDedup")
stream = StreamingContext(context, 5)

records = stream.socketTextStream("localhost", 5000)
records
    .map(lambda record: (record, 1))
    .reduceByKey(lambda x,y: x + y)
    .pprint()

ssc.start()
ssc.awaitTermination()

在这个应用中,我们连接到端口5000上的远程服务,该服务在其自己的页面上发送消息。程序每 5 秒钟总结一次操作结果,如StreamingContext参数所定义。

现在,让我们使用 UNIX netcat 命令(nc)和一个简单的循环来启动一个简单的 TCP 服务器:

for i in $(seq 1 10)
do
  for j in $(seq 1 5)
  do
   sleep 1
   tail -n+$(($i * 3)) /usr/share/dict/words | head -3
  done
done | nc -l 5000

之后,将我们的计划提交给 spark cluster:

[hive@node-3 ~]$ /usr/hdp/current/spark2-client/bin/spark-submit ./StreamingDedup.py 2>&1 | grep -v -e INFO -e WARN

程序启动后,我们会看到以下输出:

-------------------------------------------
Time: 2018-03-26 04:33:45
-------------------------------------------
(u'16-point', 5)
(u'18-point', 5)
(u'1st', 5)

-------------------------------------------
Time: 2018-03-26 04:33:50
-------------------------------------------
(u'2', 5)
(u'20-point', 5)
(u'2,4,5-t', 5)

我们看到每个单词都有正好 5 作为计数,这是预期的,因为我们在 Unix 命令循环中打印了五次。

借助这个图表,我们可以理解这一点:

输入流产生连续的数据流,由Spark 程序实时消耗。之后,通过消除重复项来打印结果

如果我们按照时间顺序来看,从时间零点到时间五秒( T0 - T5 )的数据被处理,结果可以在 T5 时间获得。其他时间段也一样。

在这个简单的例子中,我们刚刚学习了如何使用 Spark Streaming 构建实时应用的基础知识。

Spark SQL 应用

使用 Spark 编写应用时,开发人员可以选择在结构化数据上使用 SQL 来获得所需的结果。一个例子让我们更容易理解如何做到这一点:

[hive@node-3 ~]$ cat SQLApp.py 
from pyspark.sql import SparkSession

# Path to the file in HDFS
csvFile = "employees.csv"

# Create a session for this application
spark = SparkSession.builder.appName("SQLApp").getOrCreate()

# Read the CSV File
csvTable = spark.read.format("csv").option("header", "true").option("delimiter", "\t").load(csvFile)
csvTable.show(3)

# Create a temporary view
csvView = csvTable.createOrReplaceTempView("employees")

# Find the total salary of employees and print the highest salary makers
highPay = spark.sql("SELECT first_name, last_name, emp_no, SUM(salary) AS total FROM employees GROUP BY emp_no, first_name, last_name ORDER BY SUM(salary)")

# Generate list of records
results = highPay.rdd.map(lambda rec: "Total: {}, Emp No: {}, Full Name: {} {}".format(rec.total, rec.emp_no, rec.first_name, rec.last_name)).collect()

# Show the top 5 of them
for r in results[:5]:
    print(r)

# Stop the application
spark.stop()
[hive@node-3 ~]$ 

在这个例子中,我们从employees.csv构建一个数据框架,然后在内存中创建一个名为员工的视图。稍后,我们可以使用 ANSI SQL 来编写和执行查询,以生成必要的结果。

由于我们有兴趣找到收入最高的员工,结果如预期所示:

[hive@node-3 ~]$ /usr/hdp/current/spark2-client/bin/spark-submit ./SQLApp.py 2>&1 | grep -v -e INFO -e WARN
[rdd_10_0]
+------+----------+----------+---------+------+----------+------+----------+----------+
|emp_no|birth_date|first_name|last_name|gender| hire_date|salary| from_date|   to_date|
+------+----------+----------+---------+------+----------+------+----------+----------+
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 60117|1986-06-26|1987-06-26|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 62102|1987-06-26|1988-06-25|
| 10001|1953-09-02|    Georgi|  Facello|     M|1986-06-26| 66074|1988-06-25|1989-06-25|
+------+----------+----------+---------+------+----------+------+----------+----------+
only showing top 3 rows

Total: 40000.0, Emp No: 15084, Full Name: Aloke Birke
Total: 40000.0, Emp No: 24529, Full Name: Mario Antonakopoulos
Total: 40000.0, Emp No: 30311, Full Name: Tomofumi Coombs
Total: 40000.0, Emp No: 55527, Full Name: Kellyn Ouhyoung
Total: 40000.0, Emp No: 284677, Full Name: Richara Eastman

正如我们所看到的,Apache Spark 提供的简化的应用编程接口使得在 CSV 数据的基础上编写 SQL 查询变得更加容易(不需要关系数据库管理系统),以获得我们想要的东西。

摘要

在本章中,您了解了大规模数据处理框架的基本概念,并了解到 spark 的强大功能之一是构建处理实时流数据并产生实时结果的应用。

在接下来的几章中,我们将讨论如何使用 Elasticsearch 堆栈构建实时数据搜索管道。

八、构建企业搜索平台

在学习了数据摄取和数据持久化方法之后,让我们学习一下搜索数据。在本章中,我们将了解以下重要事项:

  • 数据搜索技术
  • 构建实时搜索引擎。
  • 搜索实时全文数据
  • 数据索引技术
  • 构建实时数据搜索管道

数据搜索概念

在我们的日常生活中,我们总是不断地寻找一些东西。早上,我们搜索牙刷、报纸,搜索股票价格、公交时刻表、办公包等等。这份名单越列越多。当我们一天结束睡觉时,搜索活动就停止了。我们使用很多工具和技术来搜索这些东西,以最大限度地减少实际搜索时间。我们用谷歌搜索大多数东西,比如新闻、股票价格、公共汽车时刻表,以及我们需要的任何东西。为了搜索一本书的特定页面,我们使用该书的索引。所以,重点是搜索是我们生活中非常重要的活动。有两个重要的概念可以浮出水面,那就是搜索工具和搜索时间。只要想一个你想知道某个公司的某个特定股票价格的情况,加载这个页面需要几分钟的时间。你肯定会很恼火。因为这种情况下的搜索时间是你不能接受的。那么接下来的问题就是,如何减少这个搜索时间?我们将在本章中了解这一点。

对企业搜索引擎的需求

就像我们都需要一个工具来搜索自己的东西一样,每个公司也需要一个搜索引擎来构建,这样内部和外部实体就可以找到他们想要的东西。

例如,员工必须搜索他/她的 PTO 余额、特定月份的工资单,等等。人力资源部可能会搜索财务组的员工。在电子商务公司中,产品目录是最容易搜索的对象。这是一个非常敏感的对象,因为它直接影响公司的收入。如果顾客想买一双鞋,他/她能做的第一件事就是搜索公司产品目录。如果搜索时间超过几秒钟,客户可能会对产品失去兴趣。也有可能是同一客户去另一个网站买一双鞋,导致收入损失。

看来,即使有世界上所有的技术和数据,没有两个关键的组成部分,我们也做不了什么:

  • 数据搜索
  • 数据索引

谷歌、亚马逊和苹果等公司改变了世界对搜索的期望。我们都希望他们可以随时随地使用任何工具搜索任何内容,例如网站、手机和语音激活工具,如谷歌回声、Alexa 和 HomePad。我们期待这些工具能回答我们所有的问题,从今天天气怎么样?给我一个我附近加油站的名单。

随着这些预期的增长,索引越来越多数据的需求也在增长。

构建企业搜索引擎的工具

以下是一些流行的工具/产品/技术:

  • Apache Lucene
  • 弹性搜索
  • Apache 人索尔
  • 定制(内部)搜索引擎

In this chapter, I will focus on Elasticsearch in detail. I will discuss Apache Solr on a conceptual level only.

弹性搜索

Elasticsearch 是一个开源搜索引擎。它基于 Apache Lucene。它是分布式的,支持多租户功能。它使用无模式的 JSON 文档,并有一个内置的基于 HTTP 的网络接口。它还支持分析性 RESTful 查询工作负载。它是一个基于 Java 的数据库服务器。它的主要协议是 HTTP/JSON。

为什么选择弹性搜索?

Elasticsearch 是目前最流行的数据索引工具。这是因为它具有以下特点:

  • 。数据以实时速度编制索引。
  • 它是可伸缩的。它水平缩放。
  • 灵活。它支持任何数据格式,结构化、半结构化或非结构化。
  • 分布式。如果一个节点出现故障,集群仍可用于业务。
  • 它支持任何语言的数据搜索查询:Java、Python Ruby、C#等等。
  • 它有一个 Hadoop 连接器,,这有助于弹性搜索和 Hadoop 之间的顺畅通信。
  • 它支持在庞大的数据集上进行稳健的数据聚合,以发现趋势和模式。
  • 弹性堆栈 (Beats、Logstash、Elasticsearch 和 Kibana)和 X-Pack 为数据摄取、数据索引、数据可视化、数据安全和监控提供了现成的支持。

弹性搜索组件

在我们深入探讨之前,让我们了解弹性搜索的几个重要组成部分。

索引

Elasticsearch 索引是 JSON 文档的集合。Elasticsearch 是一个可能包含多个索引的数据存储。每个索引可以分为一种或多种类型。类型是一组相似的文档。一个类型可能包含多个文档。就数据库类比而言,索引是一个数据库,它的每种类型都是一个表。每个 JSON 文档都是该表中的一行。

Indices created in Elasticsearch 6.0.0 or later may only contain a single mapping type.

在 Elasticsearch 7.0.0 中将完全删除映射类型。

文件

弹性搜索中的文档是指 JSON 文档。它是存储在索引中的基本数据单元。一个索引包含多个文档。在关系数据库管理系统世界中,文档只不过是表格中的一行。例如,客户文档可能如下所示:

{
"name": "Sam Taylor",
"birthdate": "1995-08-11",
"address":
{
"street": "155 rabbit Street",
"city": "San Francisco",
"state": "ca",
"postalCode": "94107"
},
"contactPhone":
[
{
"type": "home",
"number": "510-415-8929"
},
{
"type": "cell",
"number": "408-171-8187"
}
]
}

绘图

映射是索引的模式定义。就像数据库一样,我们必须定义一个表的数据结构。我们必须创建一个表、它的列和列数据类型。在弹性搜索中,我们已经定义了索引创建过程中的结构。我们可能需要定义哪些字段可以被索引、搜索和存储。

好消息是,Elasticsearch 支持动态映射。这意味着在索引创建时映射不是强制性的。无需映射即可创建索引。当文档发送到弹性搜索进行索引时,弹性搜索会自动定义每个字段的数据结构,并使每个字段成为可搜索字段。

Elasticsearch 是节点(服务器)的集合。每个节点可以将部分数据存储在索引中,并提供跨所有节点的联合索引和搜索功能。默认情况下,每个集群都有一个唯一的名称elasticsearch。集群分为多种类型的节点,即主节点和数据节点。但是,弹性搜索集群可以只使用一个节点来创建,该节点在同一个节点上同时安装了主节点和数据节点:

  • 主节点:控制整个集群。一个集群中可以有多个主节点(建议有三个)。它的主要功能是索引创建或删除以及将碎片(分区)分配给数据节点。
  • 数据节点:这将实际索引数据存储在碎片中。它们支持所有与数据相关的操作,如聚合、索引搜索等。

类型

文档分为各种逻辑类型,例如订单文档、产品文档、客户文档等。不是创建三个单独的订单、产品和客户索引,而是可以将单个索引逻辑上分为订单、产品和客户类型。在关系数据库管理系统的类比中,一个类型只不过是数据库中的一个表。因此,类型是索引的逻辑分区。

Type is deprecated in Elasticsearch version 6.0.

如何在 Elasticsearch 中索引文档?

让我们通过索引这三个示例文档来了解弹性搜索实际上是如何工作的。在学习这一点的同时,我们将触及弹性搜索的一些重要功能/概念。

以下是要索引的三个示例 JSON 文档:

{
"name": "Angela Martin",
"birthdate": "1997-11-02",
"street": "63542 Times Square",
"city": "New York",
"state": "NY",
"zip": "10036",
"homePhone": "212-415-8929",
"cellPhone": "212-171-8187"
} ,
{
"name": "Sam Taylor",
"birthdate": "1995-08-11",
"street": "155 rabbit Street",
"city": "San Francisco",
"state": "ca",
"zip": "94107",
"homePhone": "510-415-8929",
"cellPhone": "408-171-8187"
} ,
{
"name": "Dan Lee",
"birthdate": "1970-01-25",
"street": "76336 1st Street",
"city": "Los Angeles",
"state": "ca",
"zip": "90010",
"homePhone": "323-892-5363",
"cellPhone": "213-978-1320"
}

弹性搜索安装

首先是第一件事。让我们安装弹性搜索。

请执行以下步骤在您的服务器上安装弹性搜索。假设您正在服务器上使用 CentOS 7 安装弹性搜索。

最低硬件要求是什么?

  • RAM : 4 GB
  • CPU : 2

哪个 JDK 需要安装?我们需要 JDK 8 号。如果您的服务器上没有安装 JDK 8,请执行以下步骤安装 JDK 8:

  1. 更改到个人文件夹:
 $ cd ~
  1. 下载 JDK 转速:
$ wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u73-b02/jdk-8u73-linux-x64.rpm
  1. 使用 YUM 安装 RMP(假设你有sudo权限):
$ sudo yum -y localinstall jdk-8u73-linux-x64.rpm

既然我们已经成功地在我们的服务器上安装了 JDK 8,让我们开始安装弹性搜索。

弹性搜索的安装

有关详细的安装步骤,请参考以下网址:

https://www . elastic . co/guide/en/elastic search/reference/current/rpm . html

  1. Elasticsearch v6.2.3 的 RPM 可从网站下载并按如下方式安装:
$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.2.rpm
$ sudo rpm --install elasticsearch-6.1.2.rpm
  1. 要将弹性搜索配置为在系统启动时自动启动,请运行以下命令。
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable elasticsearch.service
  1. 弹性搜索可以按如下方式启动和停止:
sudo systemctl start elasticsearch.service
sudo systemctl stop elasticsearch.service

主配置文件位于名为elasticsearch.yml的配置文件夹中。

让我们在elasticsearch.yml中进行以下初始配置更改。查找并替换以下参数:

cluster.name: my-elaticsearch
path.data: /opt/data
path.logs: /opt/logs
network.host: 0.0.0.0
http.port: 9200

现在开始弹性搜索:

sudo systemctl start elasticsearch.service

使用以下网址检查弹性搜索是否正在运行:

http://localhost:9200

我们将得到以下回应:

// 20180320161034
// http://localhost:9200/
{
 "name": "o7NVnfX",
"cluster_name": "my-elasticsearch",
"cluster_uuid": "jmB-_FEuTb6N_OFokwxF1A",
"version": {
"number": "6.1.2",
"build_hash": "5b1fea5",
"build_date": "2017-01-10T02:35:59.208Z",
"build_snapshot": false,
"lucene_version": "7.1.0",
"minimum_wire_compatibility_version": "5.6.0",
"minimum_index_compatibility_version": "5.0.0"
},
"tagline": "You Know, for Search"
}

现在,我们的弹性研究进展顺利。让我们创建一个索引来存储我们的文档。

创建索引

我们将使用下面的curl命令来创建第一个名为my_index的索引:

curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
"settings" : {
"index" : {
"number_of_shards" : 2,
"number_of_replicas" : 1
}
}
}
'

我们会得到这样的回应:

{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "my_index"
}

在索引创建 URL 中,我们使用了设置、碎片和副本。让我们理解什么是碎片和复制品。

主碎片

我们已经用三个碎片创建了索引。这意味着 Elasticsearch 会将索引分成三个分区。每个分区称为一个碎片。每个碎片都是一个完整的、独立的 Lucene 索引。基本思想是弹性搜索将每个碎片存储在一个单独的数据节点上,以提高可扩展性。我们必须提到在创建索引时我们想要多少碎片。然后,弹性搜索会自动处理它。在文档搜索过程中,Elasticsearch 将聚合所有可用碎片中的所有文档,以合并结果,从而满足用户的搜索请求。它对用户是完全透明的。因此,概念是索引可以分成多个碎片,每个碎片可以托管在每个数据节点上。碎片的放置将由 Elasticsearch 本身负责。如果我们没有在索引创建网址中指定碎片的数量,默认情况下,弹性搜索将为每个索引创建五个碎片。

副本碎片

我们已经用一个副本创建了索引。这意味着 Elasticsearch 将为每个分片创建一个副本(复本),并将每个复本放置在不同的数据节点上,而不是复制它的分片上。因此,现在有两个碎片,主碎片(原始碎片)和副本碎片(主碎片的副本)。在大量搜索活动中,弹性搜索可以从主碎片或放置在不同数据节点上的副本碎片中提供查询结果。这就是 Elasticsearch 提高查询吞吐量的方式,因为每个搜索查询可能会到达不同的数据节点。

总之,主碎片和副本碎片都提供了水平可扩展性和吞吐量。它可以扩展您的搜索量/吞吐量,因为搜索可以在所有副本上并行执行。

Elasticsearch 是一个分布式数据存储。这意味着数据可以分为多个数据节点。例如,假设我们只有一个数据节点,并且我们在同一数据节点上继续接收和索引文档,那么在达到该节点的硬件容量后,我们可能无法接收文档。因此,为了容纳更多的文档,我们必须向现有的弹性搜索集群添加另一个数据节点。如果我们添加另一个数据节点,弹性搜索会将碎片重新平衡到新创建的数据节点。所以现在,用户搜索查询可以适应两个数据节点。如果我们创建了一个副本碎片,那么每个碎片将创建两个副本,并放置在这两个数据节点上。现在,如果其中一个数据节点发生故障,那么用户搜索查询将只使用一个数据节点来执行。

此图显示了如何从两个数据节点执行用户搜索查询:

下图显示,即使数据节点 A 关闭,用户查询仍然从数据节点 B 执行:

让我们验证新创建的索引:

curl -XGET 'localhost:9200/_cat/indices?v&amp;amp;amp;pretty'

我们将得到以下回应:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open my_index 2MXqDHedSUqoV8Zyo0l-Lw 5 1 1 0 6.9kb 6.9kb

让我们了解一下回应:

  • 健康:这表示集群整体健康状态为黄色。有三种状态:绿色、黄色和红色。状态Green表示集群功能齐全,一切正常。状态“黄色”表示群集完全可用,但某些副本尚未分配。在我们的示例中,由于我们只使用一个节点和 5 个碎片,每个碎片有一个副本,因此 Elasticsearch 不会将所有碎片的所有副本分配给一个数据节点。集群状态“红色”表示集群部分可用,某些数据集不可用。原因可能是数据节点关闭或其他原因。
  • 状态 : Open。这意味着集群对企业开放。
  • 指数:指数名称。在我们的例子中,索引名是my_index
  • Uuid :这是唯一的索引 id。
  • 优先级:主碎片数。
  • 代表:副本碎片的数量。
  • 文档数:一个索引中的文档总数。
  • docs.deleted :从一个索引中删除的文档总数。
  • 存储大小:主碎片和副本碎片占用的存储大小。
  • pri.store.size :仅由主碎片获取的存储大小。

将文档纳入索引

以下curl命令可用于摄取my_index索引中的单个文档:

curl -X PUT 'localhost:9200/my_index/customer/1' -H 'Content-Type: application/json' -d '
{
"name": "Angela Martin",
"birthdate": "1997-11-02",
"street": "63542 Times Square",
"city": "New York",
"state": "NY",
"zip": "10036",
"homePhone": "212-415-8929",
"cellPhone": "212-171-8187"
}'

在前面的命令中,我们使用了一个名为customer的类型,它是一个索引的逻辑分区。在关系数据库管理系统的类比中,一个类型就像弹性搜索中的一个表。

另外,我们在客户类型后使用了数字1。这是一个顾客的身份证。如果我们忽略它,那么弹性搜索将为文档生成一个任意的标识。

我们有多个文档要插入my_index索引。在命令行中逐个插入文档是非常繁琐和耗时的。因此,我们可以将所有文档包含在一个文件中,并批量插入my_index

创建一个sample.json文件,包括所有三个文档:

{"index":{"_id":"1"}}

{"name": "Sam Taylor","birthdate": "1995-08-11","address":{"street": "155 rabbit Street","city": "San Francisco","state": "CA","zip": "94107"},"contactPhone":[{"type": "home","number": "510-415-8929"},{"type": "cell","number": "408-171-8187"}]}

{"index":{"_id":"2"}}
{"name": "Dan Lee","birthdate": "1970-01-25","address":{"street": "76336 1st Street","city": "Los Angeles","state": "CA","zip": "90010"},"contactPhone":[{"type": "home","number": "323-892-5363"},{"type": "cell","number": "213-978-1320"}]}

{"index":{"_id":"3"}}

{"name": "Angela Martin","birthdate": "1997-11-02","address":{"street": "63542 Times Square","city": "New York","state": "NY","zip": "10036"},"contactPhone":[{"type": "home","number": "212-415-8929"},{"type": "cell","number": "212-171-8187"}]}

批量插入

让我们使用以下命令一次性摄取文件sample.json中的所有文档:

curl -H 'Content-Type: application/json' -XPUT 'localhost:9200/my_index/customer/_bulk?pretty&amp;amp;amp;refresh' --data-binary "@sample.json"

让我们使用我们最喜欢的浏览器来验证所有记录。它将显示所有三条记录:

http://localhost:9200/my_index/_search

文档搜索

由于我们的my_index索引中有文档,我们可以搜索这些文档:

找出city = " Los Angeles?所在的文档,查询如下:

curl -XGET 'http://localhost:9200/my_index2/_search?pretty' -H 'Content-Type: application/json' -d' {
"query": {
"match": {
"city": "Los Angeles" }
}
}'

回应:

{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 3,"successful" : 3,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.3862944,
"hits" : [
{
"_index" : "my_index",
"_type" : "customer",
"_id" : "3",
"_score" : 1.3862944,
"_source" : {
"name" : "Dan Lee",
"birthdate" : "1970-01-25",
"street" : "76336 1st Street",
"city" : "Los Angeles", "state" : "ca",
"postalCode" : "90010",
"homePhone" : "323-892-5363",
"cellPhone" : "213-978-1320"
}
}
]
}
}

如果我们分析响应,我们可以看到源部分返回了我们正在寻找的文档。文件在索引my_index"_type" : "customer""_id" : "3"中。Elasticsearch 成功搜索所有three _shards

hits部分下,有一个名为_score的字段。Elasticsearch 计算文档中每个字段的相关频率,并将其存储在索引中。它被称为文档的重量。该权重是基于四个重要因素计算的:术语频率、反向频率、文档频率和字段长度频率。这就引出了另一个问题,【Elasticsearch 如何对文档进行索引?

例如,我们在 Elasticsearch 中有以下四个要索引的文档:

  • 我爱 Elasticsearch
  • 弹性搜索是一个文档存储
  • 糖化血红蛋白是关键值数据存储
  • 我爱巴舍

| 期限 | 频率 | 文件编号 |
| a | Two | Two |
| 指数 | one | Two |
| 弹性搜索 | Two | 1,2 |
| 巴什 | Two | one |
| 我 | Two | 1,4 |
| 是 | Two | 2,3 |
| 钥匙 | one | three |
| 爱 | Two | 1,4 |
| 商店 | Two | 2,3 |
| 价值 | one | three |

当我们在 Elasticsearch 中摄取三个文档时,会创建一个倒排索引,如下所示。

现在,如果我们想要查询术语 Elasticsearch,那么只需要搜索两个文档:1 和 2。如果我们运行另一个查询来查找 love Elasticsearch ,那么在只发送第一个文档的结果之前,需要搜索三个文档(文档 1、2 和 4)。

此外,还有一个更重要的概念我们需要理解。

元字段

当我们将一个文档摄取到索引中时,Elasticsearch 会为每个索引文档添加一些元字段。以下是参照我们的示例my_index的元字段列表:

  • _index:索引的名称。my_index
  • _type:映射类型。“客户”(在 6.0 版中已弃用)。
  • _uid:_type + _id(6.0 版本中已弃用)。
  • _id : document_id (1)。
  • _all:这将一个索引的所有字段连接成一个可搜索的字符串(在 6.0 版本中不推荐使用)。
  • _ttl:在文档可以被自动删除之前,将其保存。
  • _timestamp:为文档提供时间戳。
  • _source:这是一个实际的文档,默认自动索引。

绘图

在关系数据库管理系统的类比中,映射意味着定义一个表模式。我们总是定义一个表结构,也就是列数据类型。在 Elasticsearch 中,我们还需要为每个字段定义数据类型。但随之而来的是另一个问题。为什么我们之前把三篇文献摄入my_index索引的时候没有定义?答案很简单。弹性搜索不在乎。据称弹性搜索是一个无模式的数据模型

如果我们没有定义映射,Elasticsearch 会通过将所有字段定义为文本来为我们动态创建映射。Elasticsearch 足够智能,可以找出日期字段并将date数据类型分配给它们。

让我们找到现有的索引my_index的动态映射:

curl -XGET 'localhost:9200/my_index2/_mapping/?pretty'

回应:

{
"my_index" : {
"mappings" : {
customer" : {
"properties" : {
"birthdate" : {
"type" : "date"
},
"cellPhone" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"homePhone" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
type" : "keyword",
"ignore_above" : 256
}
}
},
"postalCode" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"state" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"street" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
}

Elasticsearch 支持以下两种映射类型:

  • 静态映射
  • 动态映射

静态映射

在静态映射中,我们总是知道我们的数据,并为每个字段定义适当的数据类型。静态映射必须在创建索引时定义。

动态映射

在我们的示例中,我们已经对文档使用了动态映射。基本上,我们没有为任何字段定义任何数据类型。但是当我们使用_Bulk加载摄取文档时,弹性搜索透明地为每个字段定义了合适的textdate数据类型。Elasticsearch 智能地将我们的Birthdate作为日期字段,并为其分配了date数据类型。

支持弹性搜索的数据类型

以下电子表格总结了弹性搜索中可用的数据类型:

| 常用 | 综合体 | Geo | 专业化 |
| 线 | 排列 | 地理点 | ip |
| 关键字 | 对象(单个 Json) | 地理形状 | completion |
| 日期 | 嵌套(Json 数组) | | token_count |
| 长的 | | | join |
| 短的 | | | percolator |
| 字节 | | | murmur3 |
| 两倍 | | | |
| 浮动 | | | |
| 布尔代数学体系的 | | | |
| 二进制的 | | | |
| 整数范围 | | | |
| 浮动范围 | | | |
| 远程 | | | |
| 双范围 | | | |
| 日期范围 | | | |

大多数数据类型不需要解释。但是以下是对特定数据类型的一些解释:

  • 地理点 : 可以在这里定义经纬度点
  • 地理形状 : 这是用来定义形状的
  • 完成 : 该数据类型用于定义单词的自动完成。
  • 连接:定义父子关系
  • 渗滤器 : 这是查询-dsl
  • mur 3:在索引时间内,用于计算哈希值并将其存储到索引中

**# 映射示例

让我们重新创建另一个索引second_index,它类似于我们的静态映射的first_index,在这里我们将分别定义每个字段的数据类型:

curl -XPUT localhost:9200/second_index -d '{
"mappings": {
"customer": {
"_source": {
"enabled": false
},
"properties": {
"name": {"type": "string", "store": true},
"birthdate": {"type": "string"},
"street": {"type": "string"},
"city": {"type": "date"},
"state": {"type": "string", "index": "no", "store": true}
"zip": {"type": "string", "index": "no", "store": true}}
}
}
}

让我们理解前面的映射。我们禁用客户类型的_source字段。这意味着,我们摆脱了默认行为,默认情况下,弹性搜索存储和索引文档。现在,由于我们已经禁用了它,我们将分别处理每个字段,以决定该字段是应该索引存储还是两者都存储。

因此,在前面的示例中,我们只想存储三个字段:namestatezip。此外,我们不想索引statezip字段。这意味着statezip字段不可搜索。

分析者

我们已经了解了倒排索引。我们知道 Elasticsearch 将文档存储到一个倒排索引中。这种转变被称为分析。这是成功响应索引搜索查询所必需的。

同样,很多时候,我们需要在将文档发送到 Elasticsearch 索引之前使用某种转换。我们可能需要将文档更改为小写,从文档中剥离 HTML 标记(如果有),删除两个单词之间的空白,根据分隔符标记字段,等等。

Elasticsearch 提供以下内置分析器:

  • 标准分析仪 : 是默认分析仪。这使用标准标记器来划分文本。它规范了令牌,降低了令牌的级别,还删除了不需要的令牌。
  • 简单分析器 : 这个分析器由小写的 tokenizer 组成。
  • 空白分析器:这使用空白标记器在空格处划分文本。
  • 语言分析器 : Elasticsearch 提供了很多特定语言的分析器,比如英语等等。
  • 指纹分析仪 : 指纹分析仪是一款专业级分析仪。它创建了一个指纹,可用于重复检测。
  • 模式分析器 : 模式分析器使用正则表达式将文本拆分为术语。
  • 停止分析器 : 这使用字母标记器来划分文本。它从令牌流中移除停止字。例如,像 a、an、the、is 等所有停止字。
  • 关键词分析器 : 该分析器将整个流标记为单个标记。它可以用于邮政编码。
  • 字符过滤器:在字符串被标记之前准备一个字符串。示例:移除 html 标记。
  • 标记器:必须有一个标记器。它用于将字符串分解成单独的术语或标记。
  • 令牌过滤器:更改、添加或删除令牌。斯特梅尔是一个令牌过滤器,它用来获取单词的基数,例如:学会了,学习= >学会了

标准分析仪示例:

curl -XPOST 'localhost:9200/_analyze?pretty' -H 'Content-Type: application/json' -d'
{
"analyzer": "standard",
"text": " 1\. Today it's a Sunny-day, very Bright."
}'

回应:

[today, it's , a, sunny, day, very, bright ]

简单分析仪示例:

curl -XPOST 'localhost:9200/_analyze?pretty' -H 'Content-Type: application/json' -d'
{
"analyzer": "simple",
"text": " 1\. Today it's a Sunny-day, very Bright."
}'

回应:

[today, it's , a, sunny, day, very, bright ]

Elasticsearch 堆栈组件

弹性搜索堆栈包括以下内容

  • 搜索
  • logstash(日志记录)
  • 弹性搜索
  • 姆纳人

让我们简单地研究一下。

搜索

请参考以下网址了解更多节拍:https://www.elastic.co/products/beats

Beats 是轻量级数据托运人。Beats 作为代理安装到服务器上。他们的主要功能是收集数据并将其发送到 Logstash 或 Elasticsearch。我们还可以配置 beats 向卡夫卡主题发送数据。

有多个节拍。每个节拍都是为了收集特定的数据集和指标。以下是各种类型的节拍:

  • 文件节拍 : 用于收集日志文件。它们将常见日志格式的收集、解析和可视化简化为一个命令。Filebeat 自带内部模块(auditd、Apache、nginx、system 和 MySQL)。
  • Metricbeat : 用于收集指标。他们从任何系统和服务收集指标,例如内存、COU 和磁盘。Metricbeat 是一种发送系统和服务统计数据的轻量级方法。
  • Packetbeat :这是为了收集网络数据。Packetbeat 是一个轻量级的网络数据包分析器,它将数据发送到 Logstash 或 Elasticsearch。
  • Winlogbeat : 用于采集 Windows 事件数据。Winlogbeat 实时流窗口事件日志到弹性搜索和日志存储。
  • Auditbeat : 用于采集审计数据。Auditbeat 收集审核框架数据。
  • 心跳 : 用于采集正常运行时间监测数据。心跳将这些信息和响应时间发送给弹性搜索。

文件节拍的安装:

$wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.1.2-x86_64.rpm
$ sudo rpm --install filebeat-6.1.2-x86_64.rpm
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable filebeat.service

logstash(日志记录)

Logstash 是一个轻量级的开源数据处理管道。它允许从各种各样的来源收集数据,动态转换数据,并将其发送到任何想要的目的地。

它最常被用作流行的分析和搜索引擎 Elasticsearch 的数据管道。Logstash 是将数据加载到 Elasticsearch 的流行选择,因为它紧密集成,强大的日志处理功能,以及 200 多个预构建的开源插件,可以帮助您按照自己想要的方式对数据进行索引。

以下是Logstash.conf的结构:

input {
...
}
filter {
...
}
output {
..
}

Logstash 的安装:

$ wget https://artifacts.elastic.co/downloads/logstash/logstash-6.1.2.rpm
$ sudo rpm --install logstash-6.1.2.rpm
$ sudo /bin/systemctl daemon-reload
$ sudo systemctl start logstash.service

姆纳人

Kibana 是一个开源的数据可视化和探索工具,用于日志和时间序列分析、应用监控和运营智能用例。Kibana 与流行的分析和搜索引擎 Elasticsearch 紧密集成,这使得 Kibana 成为可视化存储在 Elasticsearch 中的数据的默认选择。Kibana 也因其强大且易于使用的功能而广受欢迎,例如直方图、折线图、饼图、热图以及内置的地理空间支持

基巴纳的安装:

$wget https://artifacts.elastic.co/downloads/kibana/kibana-6.1.2-x86_64.rpm
$ sudo rpm --install kibana-6.1.2-x86_64.rpm
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable kibana.service

用例

让我们假设在应用服务器上部署了一个应用。该应用正在登录访问日志。那么我们如何使用仪表板来分析这个访问日志呢?我们希望创建以下信息的实时可视化:

  • 各种响应代码的数量
  • 响应总数
  • 综合方案清单

建议的技术堆栈:

  • Filebeat :读取访问日志,写入卡夫卡主题
  • 卡夫卡:消息队列和 o 缓冲区消息
  • Logstash: 从 Kafka 提取消息并写入 Elasticsearch 索引
  • 弹性搜索:用于索引消息
  • 基巴纳:仪表盘可视化

为了解决这个问题,我们在 Appserver 上安装了 filebeat。Filebeat 将从访问日志中读取每一行,并实时写入 kafka 主题。信息将在卡夫卡缓冲。Logstash 将从卡夫卡主题中提取消息,并写入 Elasticsearch。

Kibana 将通过读取 Elasticsearch 索引中的消息来创建实时流式仪表板。下面是我们用例的架构:

下面是一步一步的代码示例,Acccss.log:

127.0.0.1 - - [21/Mar/2017:13:52:29 -0400] "GET /web-portal/performance/js/common-functions.js HTTP/1.1" 200 3558
127.0.0.1 - - [21/Mar/2017:13:52:30 -0400] "GET /web-portal/performance/js/sitespeed-functions.js HTTP/1.1" 200 13068
127.0.0.1 - - [21/Mar/2017:13:52:34 -0400] "GET /web-portal/img/app2-icon-dark.png HTTP/1.1" 200 4939
127.0.0.1 - - [21/Mar/2017:13:52:43 -0400] "GET /web-search-service/service/performanceTest/release/list HTTP/1.1" 200 186
127.0.0.1 - - [21/Mar/2017:13:52:44 -0400] "GET /web-portal/performance/fonts/opan-sans/OpenSans-Light-webfont.woff HTTP/1.1" 200 22248
127.0.0.1 - - [21/Mar/2017:13:52:44 -0400] "GET /web-portal/performance/img/icon/tile-actions.png HTTP/1.1" 200 100
127.0.0.1 - - [21/Mar/2017:13:52:44 -0400] "GET /web-portal/performance/fonts/fontawesome/fontawesome-webfont.woff?v=4.0.3 HTTP/1.1" 200 44432

以下是完整的Filebeat.ymal:

在卡夫卡输出部分,我们已经提到了卡夫卡经纪人的细节。output.kafka:

# initial brokers for reading cluster metadata
hosts: ["localhost:6667"]

以下是完整的Filebeat.ymal:

###################### Filebeat Configuration Example #########################
# This file is an example configuration file highlighting only the most common
# options. The filebeat.reference.yml file from the same directory contains all the
# supported options with more comments. You can use it as a reference.
#
# You can find the full configuration reference here:
# https://www.elastic.co/guide/en/beats/filebeat/index.html
# For more available modules and options, please see the filebeat.reference.yml sample
# configuration file.
#======================== Filebeat prospectors========================
filebeat.prospectors:
# Each - is a prospector. Most options can be set at the prospector level, so
# you can use different prospectors for various configurations.
# Below are the prospector specific configurations.
- type: log
# Change to true to enable this prospector configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /var/log/myapp/*.log
#- c:programdataelasticsearchlogs*
#json.keys_under_root: true
#json.add_error_key: true
# Exclude lines. A list of regular expressions to match. It drops the lines that are
# matching any regular expression from the list.
#exclude_lines: ['^DBG']
# Include lines. A list of regular expressions to match. It exports the lines that are
# matching any regular expression from the list.
#include_lines: ['^ERR', '^WARN']
# Exclude files. A list of regular expressions to match. Filebeat drops the files that
# are matching any regular expression from the list. By default, no files are dropped.
#exclude_files: ['.gz$']
# Optional additional fields. These fields can be freely picked
# to add additional information to the crawled log files for filtering
#fields:
# level: debug
# review: 1
fields:
app: myapp
env: dev
dc: gce
### Multiline options
# Mutiline can be used for log messages spanning multiple lines. This is common
# for Java Stack Traces or C-Line Continuation
# The regexp Pattern that has to be matched. The example pattern matches all lines starting with [#multiline.pattern: ^[
# Defines if the pattern set under pattern should be negated or not. Default is false.
#multiline.negate: false
# Match can be set to "after" or "before". It is used to define if lines should be append to a pattern
# that was (not) matched before or after or as long as a pattern is not matched based on negate.
# Note: After is the equivalent to previous and before is the equivalent to to next in Logstash
#multiline.match: after
#============================= Filebeat modules ===============================
filebeat.config.modules:
# Glob pattern for configuration loading
path: ${path.config}/modules.d/*.yml
# Set to true to enable config reloading
reload.enabled: false
# Period on which files under path should be checked for changes
#reload.period: 10s
#==================== Elasticsearch template setting ==========================
setup.template.settings:
index.number_of_shards: 3
#index.codec: best_compression
#_source.enabled: false
#================================ General =====================================
# The name of the shipper that publishes the network data. It can be used to group
# all the transactions sent by a single shipper in the web interface.
#name:
# The tags of the shipper are included in their own field with each
# transaction published.
#tags: ["service-X", "web-tier"]
# Optional fields that you can specify to add additional information to the
# output.
#fields:
# env: staging
#============================== Dashboards =====================================
# These settings control loading the sample dashboards to the Kibana index. Loading
# the dashboards is disabled by default and can be enabled either by setting the
# options here, or by using the `-setup` CLI flag or the `setup` command.
#setup.dashboards.enabled: false
# The URL from where to download the dashboards archive. By default this URL
# has a value which is computed based on the Beat name and version. For released
# versions, this URL points to the dashboard archive on the artifacts.elastic.co
# website.
#setup.dashboards.url:
#============================== Kibana =====================================
# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
# This requires a Kibana endpoint configuration.
setup.kibana:
# Kibana Host
# Scheme and port can be left out and will be set to the default (http and 5601)
# In case you specify and additional path, the scheme is required: http://localhost:5601/path
# IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
#host: "localhost:5601"
#============================= Elastic Cloud ==================================
# These settings simplify using filebeat with the Elastic Cloud (https://cloud.elastic.co/).
# The cloud.id setting overwrites the `output.elasticsearch.hosts` and
# `setup.kibana.host` options.
# You can find the `cloud.id` in the Elastic Cloud web UI.
#cloud.id:
# The cloud.auth setting overwrites the `output.elasticsearch.username` and
# `output.elasticsearch.password` settings. The format is `<user>:<pass>`.
#cloud.auth:
#================================ Outputs =====================================
# Configure what output to use when sending the data collected by the beat.
#-----------------------------------Kafka Output-------------------------------
output.kafka: # initial brokers for reading cluster metadata hosts: ["localhost:6667"] # message topic selection + partitioning
topic: logs-topic
partition.round_robin:
reachable_only: false
required_acks: 1
compression: gzip
max_message_bytes: 1000000
#-------------------------- Elasticsearch output ------------------------------
#output.elasticsearch:
# Array of hosts to connect to.
#hosts: ["localhost:9200"]
# Optional protocol and basic auth credentials.
#protocol: "https"
#username: "elastic"
#password: "changeme"
#----------------------------- Logstash output --------------------------------#output.logstash:
# The Logstash hosts
#hosts: ["localhost:5044"]
# Optional SSL. By default is off.
# List of root certificates for HTTPS server verifications
#ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]
# Certificate for SSL client authentication
#ssl.certificate: "/etc/pki/client/cert.pem"
# Client Certificate Key
#ssl.key: "/etc/pki/client/cert.key"
#================================ Logging =====================================
# Sets log level. The default log level is info.
# Available log levels are: error, warning, info, debug
logging.level: debug
# At debug level, you can selectively enable logging only for some components.
# To enable all selectors use ["*"]. Examples of other selectors are "beat",
# "publish", "service".
#logging.selectors: ["*"]
#============================== Xpack Monitoring ===============================
# filebeat can export internal metrics to a central Elasticsearch monitoring
# cluster. This requires xpack monitoring to be enabled in Elasticsearch. The
# reporting is disabled by default.
# Set to true to enable the monitoring reporter.
#xpack.monitoring.enabled: false
# Uncomment to send the metrics to Elasticsearch. Most settings from the
# Elasticsearch output are accepted here as well. Any setting that is not set is
# automatically inherited from the Elasticsearch output configuration, so if you
# have the Elasticsearch output configured, you can simply uncomment the
# the following line.
#xpack.monitoring.elasticsearch:

我们必须在卡夫卡中创造一个logs-topic主题,然后才能开始在其中吸收信息。假设我们已经在服务器上安装了卡夫卡。更多关于卡夫卡的内容,请参考第二章Hadoop 生命周期管理

创建日志-主题:

bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic logs-topic

以下是Logstash.conf(阅读卡夫卡的留言,并推至 Elasticseach):

input
{
kafka
{
bootstrap_servers => "127.0.0.1:6667"
group_id => "logstash_logs"
topics => ["logs-topic"]
consumer_threads => 1
type => "kafka_logs"
}
}
filter {
if [type] == "kafka_logs"
{
json {
source => "message"
}
grok {
match => { "message" => "%{IP:ip} - - [%{GREEDYDATA:log_timestamp}] %{GREEDYDATA:middle} %{NUMBER:status} %{NUMBER:bytes}" }
}
mutate {
add_field => {
"App" => "%{[fields][app]}"
}
}
}
}
output {
if [App] == "myapp"
{
elasticsearch
{
action => "index"
codec => "plain"
hosts => ["http://127.0.0.1:9200"]
index => "log_index-%{+YYYY-MM-dd}"
}
}
}

在卡夫卡部分,我们提到了以下事情:

Kafka bootstrap_servers => "127.0.0.1:6667"
Kafka topics => ["logs-topic"]

在过滤器部分我们将每条消息转换为 JSON 格式。之后,我们解析每条消息,并将其分成多个字段,如iptimestampstatus。此外,我们将应用名称myapp字段添加到每个消息中。

在输出部分,我们将每条消息写入弹性搜索。索引名为log_index-YYYY-MM-dd

摘要

在本章中,您了解了弹性搜索集群的基本概念和组件。

之后,我们讨论了 Elasticsearch 如何使用倒排索引对文档进行索引。我们还讨论了映射和分析技术。我们学习了如何在加入弹性搜索之前对事件进行反规格化。我们讨论了弹性搜索如何使用水平可伸缩性和吞吐量。在了解了诸如 Beats、Logstash 和 Kibana 等 Elasticstack 组件之后,我们处理了一个真实的用例,在这个用例中,我们演示了如何使用 Filebeat 将访问日志事件摄入 Kafka。我们开发了一个代码来从卡夫卡那里提取消息,并使用 Logstash 将其摄入弹性搜索。最后,我们使用 Kibana 学习了数据可视化。

在下一章中,我们将了解如何构建分析来设计推动业务决策的数据可视化解决方案。**

九、设计数据可视化解决方案

一旦我们拥有了 Hadoop 生态系统中的数据并对其进行了处理,下一个合乎逻辑的步骤就是构建驱动业务决策的分析。

在本章中,我们将学习以下主题:

  • 数据可视化
  • Apache 德鲁伊
  • Apache superset(Apache super set)

数据可视化

数据可视化是通过图形方式理解原始数据中各种实体之间关系的过程。这是一项非常强大的技术,因为它使最终用户能够以非常简单的形式获得消息,而无需了解底层数据。

数据可视化在大数据洞察的可视化交流中发挥着非常重要的作用。这既是一门艺术,也是一门科学,在理解数据方面需要一些努力;同时,我们也需要对目标受众有所了解。

到目前为止,我们已经看到任何类型的数据都可以存储在 Hadoop 文件系统 ( HDFS )中。为了将复杂的数据结构转换成可视化的形式,我们需要了解用于表示数据的标准技术。

在数据可视化中,信息以图形的形式传达给最终用户,这些图形可以是 1D、2D、三维甚至更高维度。这完全取决于我们试图传达的意思。

让我们看一下用于向用户传达视觉信息的标准图形:

  • 条形图/柱形图
  • 折线图/面积图
  • 圆形分格统计图表
  • 雷达图
  • 散点图/气泡图
  • 标签云
  • 泡泡图

条形图/柱形图

这是数据的 2D 图形表示,其中数据点显示为垂直/水平条。每个条代表一个数据点。当数据点不涉及时间维度时,这些点的显示顺序可能不会有任何不同。当我们处理用于表示条形图的时间序列数据时,我们通常遵循沿 X(水平)轴显示的时间顺序。

让我们看一下由四个数据点生成的示例图表。数据代表每个用户拥有的数量:

解读:图中既有行和列的文本数据,也有视觉效果。如果仔细观察,文本数据的大小更小,只有四条记录。但是视觉图形在不了解任何数据的情况下直接传达了信息。

图表传达的信息是:

  • 西塔比所有人都有钱
  • 吉塔的钱最少

其他解释也是可能的。它们留给读者。

折线图/面积图

这也是典型的 2D 图表,其中每个数据点都表示为画布上的一个点,并且属于同一数据集的所有这些点都用一条线连接起来。当从水平/垂直轴到直线的区域被完全覆盖时,该图表成为面积图。

同一图形中可以有多条线,表示同一实体的多个数据系列。

让我们根据与之前相同的数据来看一下这个面积图的示例:

这些是图表的属性:

  • x 轴有所有人的列表
  • y 轴表示从 0100 的量
  • 图表上四个地方画有点,对应于表格形式的值
  • 点是用直线连接的
  • 该区域填充在线下方,使其成为面积图

圆形分格统计图表

这也是一个 2D 图表,绘制成一个圆圈中的多个扇区。当我们想要突出所有数据点的相对重要性时,此图表非常有用。

让我们看一下使用与之前相同的数据集绘制的示例图表,以便更好地理解它:

如你所见,很容易理解使用这张图表的每个人所拥有的金额的相对重要性。

得出的结论与前面的图表相似。但是图是一个简单的圆,这里没有多个维度给用户增加负担。

雷达图

这也是一个 2D 图形,其中数据轴是等距扇区的边缘(像饼图的边缘)。当我们想要了解每个数据点的相对重要性的多维度时,此图非常有用。

为了更好地理解这个图表,让我们看一下这个样本数据和图表:

数据由八列组成:

  • 第一列:所有用户列表
  • 第二栏至第八栏:一周中的天数以及当天每人拥有的美元

我们想画一张图表,向我们展示以下内容:

  • 每天总美元数
  • 每个人每天拥有的美元

我们已经在雷达图中绘制了所有这些信息,其中轴是扇区(天),上限为 400 的最大值。每个用户的价值被一个接一个地画出来,这样我们就知道总价值,而不是相对价值(这类似于面积叠加)。

散点图/气泡图

散点图可以是多维图形。当我们沿着轴渲染画布上对应于数值的每个数据点时,这是一个更容易理解的图形。该图有助于理解轴上每个点的相对重要性。

气泡图是散点图的变体,画布上的点将数值显示为大气泡(表示其重要性)。

让我们用这个例子来看看这两个图形:

左侧的图形是气泡图,右侧的图形是散点图。

让我们看看生成的数据和图表。

输入数据:

  • 由五行组成,而我们在列中有销售产品数量

用气泡图:

  • y 轴显示产品数量
  • x 轴只是位置轴,并不反映输入数据的值
  • 画布上的每个点都显示了与产品数量相对应的销售额

使用散点图:

  • y 轴显示完成的销售额
  • x 轴显示销售的产品
  • 画布上的每个点显示输入中的每一行

其他图表

还有许多其他类型的图形可能在本节中没有涉及,但值得在https://d3js.org网站上探索。这将使您了解如何表示数据,以便向用户传达非常好的信息。

Hadoop 中的实用数据可视化

Hadoop 拥有丰富的数据源和应用生态系统,可以帮助我们构建丰富的可视化。在接下来的章节中,我们将了解两个这样的应用:

  • Apache 德鲁伊
  • Apache superset(Apache super set)

我们还将学习如何使用 Apache 超集处理关系数据库(如 MySQL)中的数据。

Apache 德鲁伊

Apache Druid 是一个分布式、高性能的柱状商店。其官网为 https://druid.io

Druid 允许我们存储实时和历史数据,本质上是时间序列。它还提供了快速的数据聚合和灵活的数据探索。该架构支持以千兆字节大小存储数万亿个数据点。

为了更好的了解德鲁伊建筑,请参考http://static.druid.io/docs/druid.pdf的这份白皮书。

德鲁伊组件

让我们快速了解一下德鲁伊集群的不同组件:

| 组件 | 描述 |
| 德鲁伊经纪人 | 这些节点知道数据在集群中的位置。这些节点由应用/客户端联系,以获取德鲁伊内部的数据。 |
| 德鲁伊协调员 | 这些节点管理历史节点上的数据(加载、删除和负载平衡数据)。 |
| 德鲁伊霸主 | 该组件负责接受任务并返回任务的状态。 |
| 德鲁伊路由器 | 当数据量在万亿字节或更高范围时,需要这些节点。这些节点将请求路由到代理。 |
| 德鲁伊历史 | 这些节点存储不可变的段,是德鲁伊集群的骨干。它们服务于加载段、删除段,并服务于对段请求的查询。 |

其他所需组件

下表列出了其他几个必需的组件:

| 组件 | 描述 |
| 动物园管理员 | Apache Zookeeper 是一个高度可靠的分布式协调服务 |
| 元数据存储 | MySQL 和 PostgreSQL 是流行的关系数据库系统,用于跟踪所有部门、主管、任务和配置 |

Apache Druid 安装

Apache Druid 可以独立安装,也可以作为 Hadoop 集群的一部分安装。在本节中,我们将看到如何通过 Apache Ambari 安装 Druid。

adservice

首先,我们调用 Hadoop 集群中服务列表下方的操作下拉列表。

屏幕如下所示:

选择德鲁伊和超集

在这个设置中,我们将同时安装德鲁伊和超集。超集是可视化应用,我们将在下一步中了解它。

选择屏幕如下所示:

选择两种服务后,单击下一步。

服务器上的服务放置

在这一步中,我们将可以选择安装应用的服务器。为此,我选择了节点 3。您可以选择任何想要的节点。

屏幕看起来像这样:

更改完成后,单击下一步。

选择奴隶和客户

在这里,我们可以选择为安装的组件选择需要从属节点和客户端的节点。我留下了已经为我选择的选项:

服务配置

在这一步中,我们需要为德鲁伊和超集应用使用的元数据存储选择数据库、用户名和密码。随意选择默认的。我给了 MySQL 作为他们两个的后端存储。

屏幕如下所示:

一旦更改看起来不错,点击屏幕底部的“下一步”按钮。

服务安装

在此步骤中,应用将自动安装,状态将在计划结束时显示。

安装完成后,单击下一步。对当前屏幕的更改如下所示:

安装摘要

一旦一切顺利完成,我们会看到一个已经完成的总结。完成后,单击完成:

将样本数据摄入 Druid

一旦我们的 Hadoop 集群中运行了所有与 Druid 相关的应用,我们就需要一个样本数据集,为了运行一些分析任务,我们必须加载该数据集。

让我们看看如何加载样本数据。从网上下载德鲁伊档案:

[druid@node-3 ~$ curl -O http://static.druid.io/artifacts/releases/druid-0.12.0-bin.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
                               Dload Upload Total Spent Left Speed
100 222M 100 222M 0 0 1500k 0 0:02:32 0:02:32 --:--:-- 594k

提取档案:

[druid@node-3 ~$ tar -xzf druid-0.12.0-bin.tar.gz

将示例维基百科数据复制到 Hadoop:

[druid@node-3 ~]$ cd druid-0.12.0
[druid@node-3 ~/druid-0.12.0]$ hadoop fs -mkdir /user/druid/quickstart
[druid@node-3 ~/druid-0.12.0]$ hadoop fs -put quickstart/wikiticker-2015-09-12-sampled.json.gz /user/druid/quickstart/

提交导入请求:

[druid@node-3 druid-0.12.0]$ curl -X 'POST' -H 'Content-Type:application/json' -d @quickstart/wikiticker-index.json localhost:8090/druid/indexer/v1/task;echo
{"task":"index_hadoop_wikiticker_2018-03-16T04:54:38.979Z"}

这一步之后,德鲁伊会自动将数据导入到德鲁伊集群中,进度可以在霸王控制台看到。

该界面可通过http://<overlord-ip>:8090/console.html访问。屏幕如下所示:

一旦摄取完成,我们将看到作业状态为成功。

In case of FAILED imports, please make sure that the backend that is configured to store the Metadata for the Druid cluster is up and running. Even though Druid works well with the OpenJDK installation, I have faced a problem with a few classes not being available at runtime. In order to overcome this, I have had to use Oracle Java version 1.8 to run all Druid applications.

现在我们准备开始使用德鲁伊来完成可视化任务。

MySQL 数据库

Apache 超集还允许我们读取关系数据库管理系统(如 MySQL)中的数据。我们还将在本节中创建一个示例数据库,稍后我们可以将它与超集一起使用来创建可视化。

样本数据库

雇员数据库是一个标准数据集,其中包含示例组织及其雇员、工资和部门数据。我们将看看如何为我们的任务设置它。

本节假设 MySQL 数据库已经配置并运行。

下载样本数据集

在任何可以访问 MySQL 数据库的服务器上,使用以下命令从 GitHub 下载示例数据集:

[user@master ~]$ sudo yum install git -y
[user@master ~]$ git clone https://github.com/datacharmer/test_db
Cloning into 'test_db'...
remote: Counting objects: 98, done.
remote: Total 98 (delta 0), reused 0 (delta 0), pack-reused 98
Unpacking objects: 100% (98/98), done.

将数据复制到 MySQL

在这一步中,我们将把文件中的数据内容导入到 MySQL 数据库中:

[user@master test_db]$ mysql -u root < employees.sql
INFO
CREATING DATABASE STRUCTURE
INFO
storage engine: InnoDB
INFO
LOADING departments
INFO
LOADING employees
INFO
LOADING dept_emp
INFO
LOADING dept_manager
INFO
LOADING titles
INFO
LOADING salaries
data_load_time_diff
NULL

验证表的完整性

这是一个重要的步骤,只是为了确保我们导入的所有数据都正确地存储在数据库中。验证发生时会显示完整性检查的摘要:

[user@master test_db]$ mysql -u root -t < test_employees_sha.sql
+----------------------+
| INFO                 |
+----------------------+
| TESTING INSTALLATION |
+----------------------+
+--------------+------------------+------------------------------------------+
| table_name   | expected_records | expected_crc                             |
+--------------+------------------+------------------------------------------+
| employees    | 300024 | 4d4aa689914d8fd41db7e45c2168e7dcb9697359 |
| departments  |  9 | 4b315afa0e35ca6649df897b958345bcb3d2b764 |
| dept_manager |               24 | 9687a7d6f93ca8847388a42a6d8d93982a841c6c |
| dept_emp     | 331603 | d95ab9fe07df0865f592574b3b33b9c741d9fd1b |
| titles       | 443308 | d12d5f746b88f07e69b9e36675b6067abb01b60e |
| salaries     | 2844047 | b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f |
+--------------+------------------+------------------------------------------+
+--------------+------------------+------------------------------------------+
| table_name   | found_records    | found_crc                        |
+--------------+------------------+------------------------------------------+
| employees    | 300024 | 4d4aa689914d8fd41db7e45c2168e7dcb9697359 |
| departments  |  9 | 4b315afa0e35ca6649df897b958345bcb3d2b764 |
| dept_manager |               24 | 9687a7d6f93ca8847388a42a6d8d93982a841c6c |
| dept_emp     | 331603 | d95ab9fe07df0865f592574b3b33b9c741d9fd1b |
| titles       | 443308 | d12d5f746b88f07e69b9e36675b6067abb01b60e |
| salaries     | 2844047 | b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f |
+--------------+------------------+------------------------------------------+
+--------------+---------------+-----------+
| table_name   | records_match | crc_match |
+--------------+---------------+-----------+
| employees    | OK | ok        |
| departments  | OK | ok        |
| dept_manager | OK            | ok |
| dept_emp     | OK | ok        |
| titles       | OK | ok        |
| salaries     | OK | ok        |
+--------------+---------------+-----------+
+------------------+
| computation_time |
+------------------+
| 00:00:11         |
+------------------+
+---------+--------+
| summary | result |
+---------+--------+
| CRC     | OK |
| count   | OK |
+---------+--------+

现在数据被正确加载到名为员工的 MySQL 数据库中。

单一标准化表格

在数据仓库中,与许多小型相关表相比,标准化表是一种标准做法。让我们创建一个包含员工、工资、部门

MariaDB [employees]> create table employee_norm as select e.emp_no, e.birth_date, CONCAT_WS(' ', e.first_name, e.last_name) full_name , e.gender, e.hire_date, s.salary, s.from_date, s.to_date, d.dept_name, t.title from employees e, salaries s, departments d, dept_emp de, titles t where e.emp_no = t.emp_no and e.emp_no = s.emp_no and d.dept_no = de.dept_no and e.emp_no = de.emp_no and s.to_date < de.to_date and s.to_date < t.to_date order by emp_no, s.from_date;
Query OK, 3721923 rows affected (1 min 7.14 sec)
Records: 3721923  Duplicates: 0  Warnings: 0

MariaDB [employees]> select * from employee_norm limit 1\G
*************************** 1\. row ***************************
    emp_no: 10001
birth_date: 1953-09-02
 full_name: Georgi Facello
    gender: M
 hire_date: 1986-06-26
    salary: 60117
 from_date: 1986-06-26
   to_date: 1987-06-26
 dept_name: Development
     title: Senior Engineer
1 row in set (0.00 sec)

MariaDB [employees]> 

一旦我们有了规范化的数据,我们将看到如何使用这个表中的数据来生成丰富的可视化。

Apache superset(Apache super set)

超集是一个现代化的企业级商业智能应用。这个应用的重要特性是,我们可以直接从浏览器运行所有分析。没有必要为此安装任何特殊软件。

如果你还记得,我们已经在前面的章节中安装了超集和德鲁伊。现在我们需要学习如何使用超集来构建丰富的可视化。

访问超集应用

在网络浏览器中打开http://<SERVER-IP>:9088/。如果一切运行正常,我们将看到如下登录屏幕:

输入admin作为安装过程中选择的用户名和密码。

超集仪表板

仪表板是超集应用的重要部分。他们让我们以图形形式展示分析计算的结果。仪表板是从切片创建的,而切片又是从超集应用中配置的各种数据源构建的。

成功登录后,不会有任何自动为我们创建的仪表盘。我们将看到一个空白的仪表板列表,如下所示:

为了构建仪表板,我们首先需要配置数据源。因此,让我们点击顶部导航中的源菜单,然后点击刷新德鲁伊元数据:

完成这一步后,我们将进入数据源页面,一个新的数据源将自动出现在这里。还记得我们之前把这个数据集上传到德鲁伊吗?

现在我们可以点击数据源名称(绿色),这将把我们带到数据源探索页面:

正如我们所看到的,这个页面被分成多个部分。

  • 左侧界面:
    • 数据源和图表类型:在这个栏目中,我们可以选择我们需要使用的数据源,也可以选择右边我们想要看到的图形类型。
    • 时间:这是我们可以将数据源的数据限制在给定时间范围内的栏目。初学者往往会对该列出错,因为他们在右侧看不到任何数据。因此,选择一个开始时间值(为了获得更好的结果,建议选择 100 年前这样的相对值)。
    • 分组依据:该列用于根据输入数据的维度对数据进行分组。
    • 其他选项:分组依据下面还有其他选项,我们将在接下来的步骤中探讨。
  • 右侧界面:
    • 这个用户界面包含我们在左侧选择的选项的结果。

理解维基百科编辑数据

在我们开始构建可视化之前。让我们更仔细地看看我们摄入到 Druid 中的数据,以及我们可以从这些数据中呈现什么类型的图形:

| 公制/尺寸 | 数据类型 | 描述 |
| delta | LONG | 以数字形式表示的变化 |
| deleted | LONG | 从文章中删除了数字形式的数据 |
| added | LONG | 添加数据,以数字形式测量 |
| isMinor | STRING | 布尔值,指示这是否是次要编辑 |
| page | STRING | 维基百科中发生变化的页面 |
| isRobot | STRING | 变化是由机器人(不是人类,而是某种形式的程序)完成的吗 |
| channel | STRING | 维基百科频道发生了变化 |
| regionName | STRING | 进行更改的地理区域名称 |
| cityName | STRING | 进行更改的城市名称 |
| countryIsoCode | STRING | 进行变更的国家的国际标准化组织代码 |
| user | STRING | 做出更改的维基百科用户或 IP 地址 |
| countryName | STRING | 进行更改的国家的名称 |
| isAnonymous | STRING | 更改是否由匿名用户完成(未登录状态)? |
| regionIsoCode | STRING | 进行变更的地理区域的国际标准化组织代码 |
| metroCode | STRING | 这类似于美国的邮政编码(见http://www . nlsinfo . org/usersvc/NLSY97/NLSY97 rnd 9 geocodecodebooksupplement/GATT 101 . html) |
| namespace | STRING | 维基百科文章/页面命名空间 |
| comment | STRING | 为此更改添加的注释 |
| isNew | STRING | true如果这是新的一页(见https://en.wikipedia.org/wiki/Wikipedia:Glossary#N |
| isUnpatrolled | STRING | true如果变更不是巡逻变更(见https://en.wikipedia.org/wiki/Wikipedia:New_pages_patrol |

因此,我们列出了数据的所有属性。让我们看一下示例一,以更好地理解我们正在谈论的内容:

{
  "time": "2015-09-12T00:47:21.578Z",
  "channel": "#en.wikipedia",
  "cityName": null,
  "comment": "Copying assessment table to wiki",
  "countryIsoCode": null,
  "countryName": null,
  "isAnonymous": false,
  "isMinor": false,
  "isNew": false,
  "isRobot": true,
  "isUnpatrolled": false,
  "metroCode": null,
  "namespace": "User",
  "page": "User:WP 1.0 bot/Tables/Project/Pubs",
  "regionIsoCode": null,
  "regionName": null,
  "user": "WP 1.0 bot",
  "delta": 121,
  "added": 121,
  "deleted": 0
}

一旦我们对数据维度有了一些了解,我们就需要看看我们可以从这些数据中回答什么类型的问题。这些问题是我们容易获得的见解。稍后,我们可以用最适合我们的图形形式来表示这些。

让我们看看从这些数据中我们可以回答的一些问题。

一维洞见:

  • 哪些城市是产生变化的地方?
  • 哪些页面被更改了?
  • 哪些国家做出了改变?
  • 创建了多少新页面?

沿尺寸计数:

  • 每个城市有多少变化?
  • 哪些是做出改变的顶级城市?
  • 哪些是促成这些变化的顶级用户?
  • 经常更改的名称空间有哪些?

多维洞察:

  • 所有国家在上午 9 点到 10 点之间发生了多少变化?
  • 机器人编辑的挂钟时间是什么时候?
  • 哪个国家的变化起源最多,是在什么时候被机器人瞄准的?

看起来很有趣,对吧?为什么我们不尝试使用 Apache 超集来创建一个具有这些见解的仪表板呢?

为此,我们需要在超集应用中遵循这个简单的工作流程:

  1. 数据来源:
    • 从支持的数据库中定义新的数据源
    • 刷新 Apache 德鲁伊数据源
  2. 创建切片
  3. 使用切片制作仪表板

如果我们回想一下,我们已经在前面的章节中完成了步骤 1 。所以,我们可以马上进入第二步和第三步。

使用维基百科数据创建超集切片

让我们看看使用超集应用中的切片功能可以生成什么类型的图形。

唯一用户数

在此切片中,我们将看到如何生成图形来查找对数据集中的编辑做出贡献的唯一用户。

首先,我们需要从顶部导航进入切片页面。之后,屏幕看起来是这样的:

在此页面中,单击加号图标(+)添加新切片:

之后,我们会看到系统中配置的数据源列表。我们必须点击数据源名称:

在我们点击 wikiticker 之后,我们会进入可视化页面,在那里我们定义想要呈现为图形的维度。

对于当前的用例,让我们从用户界面中选择以下选项:

| 用户界面位置 | 图形 | 解释 |
| 补充报道 | | 选择数据源作为【德鲁伊-安巴里】。【维基百科】和图形类型为大数字在时间部分,选择自 5 年前开始的值,并将其余值保留为默认值。在公制部分。从自动完成中选择 COUNT(DISTINCT user_unique)。在子标题部分,添加屏幕上显示的唯一用户数。之后,点击顶部的查询按钮。 |
| 图形输出 | | 我们在这张图中看到了查询的结果。 |
| 保存切片 | | 点击顶部的另存为按钮会显示一个类似这样的弹出窗口,我们需要在其中添加相应的值。将切片保存为Unique Users并将其添加到名为My Dashboard 1的新仪表板中。 |

听起来很简单,对吧?我们先不要急着看仪表盘。在接下来的部分中,让我们根据数据创建更多分析。

美国顶级地区的词云

在这一节中,我们将学习如何为美国顶级地区构建一个单词云,这些地区为我们在 Druid 中的数据源中的维基百科编辑做出了贡献。我们可以继续编辑上一节中的相同切片,或者转到一个空白切片,如前一节所述。

让我们专注于生成单词云时需要选择的值:

| 用户界面位置 | 图形 | 解释 |
| 补充报道 | | 选择数据源为[德鲁伊-安巴里]。[维基百科]图形类型为 Word Cloud。在时间部分,选择自 5 年前的值,并将其余值保留为默认值。 |
| | | 在“系列”部分。从下拉列表中选择地区名称。在度量中,选择计数(*),这是总编辑计数。 |
| | | 在“过滤器”部分,选择“国家/地区代码”;它应该在美国。添加另一个过滤器以仅选择有效区域(跳过空代码)。如图所示,添加这些值。 |
| 图形输出 | | 点击查询后,我们看到这个美丽的字云。 |
| 保存切片 | | 点击顶部的另存为按钮会显示一个类似这样的弹出窗口,我们需要在其中添加相应的值。将切片另存为Word Cloud - Top US Regions并将其添加到名为My Dashboard 1 的新仪表板中。 |

单词云的意义在于我们可以根据单词的相对大小来看到最上面的单词。当我们想要看到相对意义的单词较少时,这种可视化是有帮助的。

让我们尝试从数据中生成另一个图形。

太阳爆发图——十大城市

在本节中,我们将了解到一种不同类型的图表,这种图表我们在本章中还没有见过。但是首先,让我们提出用例。

我们希望在所有三个级别找到每个频道、城市名称和命名空间的唯一用户;也就是说,图形应该能够向我们展示:

  • 每个频道的唯一用户
  • 每个频道/城市名称的唯一用户
  • 每个频道/城市名称/命名空间的唯一用户

为了显示这种层次数据,我们可以使用太阳爆发图。

让我们看看需要选择什么类型的值来呈现这种类型的图表:

| 用户界面位置 | 图形 | 解释 |
| 补充报道 | | 选择数据源为[德鲁伊-安巴里]。[维基百科]图形类型为 Sunburst。在时间部分,选择自 5 年前的值,并将其余值保留为默认值。 |
| | | 在层次部分,从下拉列表中选择channelcityNamenamespace。在主要指标和次要指标中,选择计数(DISTINCT user_unique),这是用户总数。 |
| | | 在过滤器部分,选择城市名称,并使用正则表达式匹配添加非空条件 |
| | | 点击顶部的“另存为”按钮将显示如下弹出窗口。我们需要在这里添加相应的值。将切片另存为Sunburst - Top 10 Cities并将其添加到名为My Dashboard 1 的新仪表板中。 |
| 图形输出 | | 点击查询后,我们看到这个美丽的图形。 |

如我们所见,图中有三个同心环:

  • 最内环是channel维度
  • 中环显示cityName尺寸
  • 最外环是namespace维度

当我们悬停在最内环上时,我们可以看到它是如何扩展到最外环的。同样的事情也发生在其他戒指上。

当我们想要对数据进行漏斗分析时,这种图形非常有用。让我们在下一节中看看另一种类型的分析。

通过定向强制布局的前 50 个通道和名称空间

定向力布局 ( DFL )是一个点与点相互连接的网络布局。由于是力布局,我们可以看到d3.js应用物理引擎时,屏幕上的点在移动。

在这个网络图中,我们希望通过唯一的用户数度量来了解名称空间和通道之间的连通性。由于这是一个网络图,我们将看到节点在不同的路径上重复。

让我们看看如何得出这个图表:

| 用户界面位置 | 图形 | 解释 |
| 补充报道 | | 选择数据源为[德鲁伊-安巴里]。[维基百科]和图形类型为定向力布局在时间部分,选择自 5 年前开始的值,并将其余值保留为默认值。 |
| | | 在源/目标部分,从下拉菜单中选择channelnamespace。在度量部分,选择计数(DISTINCT user_unique),这是用户总数。我们将世界排名限制在 50 名,这样我们就只能看到前 50 名 |
| | | 点击顶部的另存为按钮会显示一个类似这样的弹出窗口,我们需要在其中添加相应的值。将切片保存为DFL - Top 50 Channels & Namespaces。将其添加到名为My Dashboard 1的新仪表板中。 |
| 图形输出 | | 点击查询后,我们看到这个美丽的图形。 |

请随意拖动图形中的节点,以了解更多关于它们如何相互连接的信息。节点的大小表示唯一的用户数及其细分(类似于阳光爆发图)。

让我们在下一节花一些时间学习另一个可视化和业务用例。

前 25 名国家/渠道分布

现在我们将学习桑基图,这是一种类似瀑布的方式来表示数据之间的分解和互连。在这种情况下,我们希望了解在唯一用户指标方面,通道名称和国家名称维度是如何关联的:

| 用户界面位置 | 图形 | 解释 |
| 补充报道 | | 选择数据源作为【德鲁伊-安巴里】。【维基百科】和作为桑基的图形类型。在时间部分,选择 5 年前的“自”值,其余值默认。 |
| | | 在源/目标部分,从下拉菜单中选择channelcountryName。在指标中,选择总编辑计数()。将行限制保持在 25;所以我们只会看到前 25 项。* |
| | | 在过滤器部分,选择国家名称并启用正则表达式过滤器,以便只选择那些具有有效国家名称的记录。 |
| | | 点击顶部的另存为按钮将显示一个弹出窗口。我们需要在这里添加相应的值。将切片另存为Top 25 Countries/Channels Distribution并将其添加到名为My Dashboard 1的新仪表板中。 |
| 图形输出 | | 点击查询后,我们看到这个美丽的图形。 |

至此,我们可以生成的所有分析列表都已完成。现在在下一节中,我们将看到如何在仪表板中使用它(这是我们最初的目标)。

创建维基百科编辑来自切片的仪表板

到目前为止,我们已经看到了如何在 Apache 超集应用中为维基百科编辑存储在 Apache Druid 数据库中的数据创建切片。现在是时候让我们看看如何创建一个仪表板,以便我们可以与业务团队或任何其他我们想要分享见解的团队共享它。

在这个过程中,第一步是单击顶部导航栏上的仪表板菜单。这将带我们进入添加新仪表板页面,我们需要填写以下详细信息。

| 元素 | 描述 | |
| 标题 | 这是我们要创建的仪表板的名称 | 我的仪表板 1 |
| 鼻涕虫 | 仪表板的简称 | dash1 |
| 部分 | 我们要添加到仪表板的切片列表。 |

  1. Sunburst-Top 10 Cities
  2. DFL-Top 50 Channels & Namespace
  3. Top 25 countries/channels contribution
  4. Cloud-the top region of the United States
  5. Unique user

|
| 其他字段 | 我们可以将其他字段留空,因为它们不是创建仪表板所必需的 | |

这是本页的图片:

一旦更改看起来不错,点击屏幕底部的保存按钮。

这将带我们进入下一步,我们可以看到仪表板已成功创建:

我们可以在仪表板列表中看到我的仪表板 1。要访问此仪表板,请单击它,我们将进入仪表板屏幕:

如我们所见,我们有一种非常强大的方式来表示所有原始数据。这肯定会对最终用户产生影响,确保信息得到传达。

到目前为止,我们已经学习了如何从存储在 Apache Druid 柱状数据库中的数据创建切片和仪表板。在下一节中,我们将看到如何连接到关系数据库管理系统,并根据这些数据生成切片和仪表板。

带有 RDBMS 的 Apache 超集

Apache 超集是使用 Python 编程语言构建的,并且支持许多关系数据库,因为它使用 SQLAlchemy 作为数据库驱动程序。这些驱动程序的安装超出了本节的范围。但是,安装这些应该非常容易。大多数时候,操作系统供应商会为我们打包它们。所以,我们不用担心手动安装这些。

支持的数据库

以下是 Apache 超集支持的一些数据库:

| 数据库名称 | Python 包名 | 驾驶员 URI 前缀 | 详情 |
| 关系型数据库 | mysqlclient | mysql:// | 甲骨文数据库 |
| 一种数据库系统 | psycopg2 | postgresql+psycopg2:// | 世界上最先进的开源数据库 |
| 很快 | pyhive | presto:// | 开源分布式查询引擎 |
| 神谕 | cx_Oracle | oracle:// | 甲骨文公司创建的多模型数据库管理系统。 |
| Sqlite | | sqlite:// | 快速、可扩展的嵌入式数据库库 |
| 红移 | sqlalchemy-redshift | postgresql+psycopg2:// | 亚马逊红移是建立在 PostgreSQL 上的柱状数据库 |
| MSSQL | pymssql | mssql:// | 微软 SQL 服务器 |
| 黑斑羚 | impyla | impala:// | Apache Impala 是运行在 Hadoop 上的大规模并行处理 SQL 引擎 |
| SparkSQL | pyhive | jdbc+hive:// | Apache Spark 模块,用于在 Spark 程序中编写 SQL。 |
| 青梅 | psycopg2 | postgresql+psycopg2:// | Greenplum 是一个先进的、功能齐全的开源数据平台 |
| 雅典娜(智慧与技艺的女神) | PyAthenaJDBC | awsathena+jdbc:// | 亚马逊雅典娜是无服务器交互式查询服务 |
| 垂直的 | sqlalchemy-vertica-python | vertica+vertica_python:// | Vertica 是大数据分析软件 |
| 钟罩 | sqlalchemy-clickhouse | clickhouse:// | 开源分布式柱状数据存储 |

Portions of the above table is extracted from the official documentation of Apache Superset (https://superset.incubator.apache.org/installation.html#database-dependencies)

了解员工数据库

如果您还记得,在前面的部分中,我们已经导入了一个名为 Employees 的示例数据库,并将其加载到 MySQL 数据库中。我们将深入研究这个示例数据存储,以便了解我们可以从中生成哪些类型的分析。

雇员表

employees表包含员工的详细信息(随机生成的数据),具有以下属性

| | 数据类型 | 描述 |
| emp_no | INTEGER | 员工编号 |
| birth_date | DATE | 员工出生日期 |
| first_name | STRING | 员工的名字 |
| last_name | STRING | 员工的姓氏 |
| gender | STRING | 员工性别,男性为男,女性为女 |
| hire_date | STRING | 员工的最新加入日期 |

部门表

departments表由组织中每个部门的基本细节组成。这张表进一步说明了这一点:

| 表列 | 数据类型 | 描述 |
| dept_no | STRING | 部门编号 |
| dept_name | STRING | 部门名称 |

部门经理表

dept_manager表记录了员工担任给定部门经理的情况。更多详情见下表:

| 表列 | 数据类型 | 描述 |
| emp_no | INT | 担任该部门经理的员工 ID |
| dept_no | STRING | 部门标识 |
| from_date | DATE | 员工担任该部门经理的起始日期。 |
| to_date | DATE | 员工担任该部门经理的截止日期。 |

部门员工表

dept_emp表由显示每个员工属于一个部门的时间的所有记录组成。

| 表列 | 数据类型 | 描述 |
| emp_no | INT | 员工编号 |
| dept_no | STRING | 部门标识 |
| from_date | DATE | 员工所属部门的起始日期 |
| to_date | DATE | 该部门员工的最后日期 |

标题表

标题表包含从给定日期到结束日期的所有员工角色。更多细节如下所示:

| 表列 | 数据类型 | 描述 |
| emp_no | INT | 员工 Id |
| title | STRING | 员工的指定 |
| from_date | DATE | 员工担任此角色的开始日期 |
| to_date | DATE | 员工履行此职责的最后日期 |

薪金表

salaries表由给定员工的薪资历史记录组成。下表解释了更多详细信息:

| 表列 | 数据类型 | 描述 |
| emp_no | INT | 员工 Id |
| salary | INT | 员工工资 |
| from_date | DATE | 计算薪资的起始日 |
| to_date | DATE | 计算薪资的最后一天。 |

标准化员工表

employee_norm表由员工、工资、部门、dept_emp和职称表的数据组成。让我们详细看看这张表:

| 表列 | 数据类型 | 描述 |
| emp_no | INT | 员工编号 |
| birth_date | DATE | 员工出生日期 |
| full_name | STRING | 员工全名 |
| gender | STRING | 员工的性别 |
| hire_date | DATE | 员工加入日期 |
| salary | INT | 该期间员工的工资 |
| from_date | DATE | 薪资期间开始 |
| to_date | DATE | 薪资期间结束 |
| dept_name | STRING | 员工在此薪资期间工作的部门 |
| title | STRING | 在此期间指定员工 |

了解了员工数据库中的各种表格后,我们现在对目前掌握的数据有了一些了解。现在,下一个任务是找出我们可以从这些数据中生成什么类型的分析。我们将在下一节中了解这一点。

员工数据库的超集切片

一旦我们对存储在 MySQL 数据库中的数据类型有了一些基本的了解。我们现在将看到我们可以从这些数据中回答什么类型的问题。

一维洞见:

  • 组织中有多少员工?
  • 组织中所有员工的工资总额是多少?
  • 有多少个部门?

多维洞察

  • 每年支付的工资总额是多少?
  • 每个部门的总工资是多少?
  • 谁是每年收入最高的员工?

如果我们按照这些思路思考,我们应该能够回答关于数据的非常重要的问题,并且应该能够生成漂亮的图形。

让我们举几个例子来说明在接下来的部分中我们可以生成什么类型的可视化。

注册 MySQL 数据库/表

在我们开始为员工表生成切片之前,我们应该首先注册它。注册过程包括以下步骤。

通过单击顶部导航栏中“源”菜单中的“数据库”下拉列表打开数据库,如下所示:

之后,我们需要点击页面上的加号(+)图标:

这将把我们带到一个页面,在那里我们可以注册新的数据库。屏幕如下所示:

我们将填写如下所示的详细信息。

| 字段名 | | 描述 | |
| 数据库ˌ资料库 | employees | 我们要注册的数据库的名称。(在 MySQL 数据库中输入与其相同的名称) | |
| SQL anywhere uri | mysql+pymysql://superset:superset@master:3306/employees | URI 可以编程访问这个数据库。这将包括协议/驱动程序、用户名、密码、主机名和数据库名 | |
| 其他字段 | | 将它们保留为默认值 | |

在此之后,点击保存按钮,这将使用 Apache 超集保存数据库详细信息。我们将进入表格列表页面,如下所示:

如我们所见,我们已经在 MySQL 后端注册了员工数据库。

在下一步中,我们需要从顶部菜单中选择表格:

因为我们没有注册任何表,所以我们将看到一个像这样的空页面:

为了注册一个新的表,我们必须点击用户界面中的加号(图标),这将我们带到以下页面:

输入如下所示的字段值,完成后单击保存:

| 字段名 | | 描述 |
| 表名 | employee_norm | 我们要注册的表的名称。 |
| 数据库ˌ资料库 | employees | 选择已经向超集注册的数据库。 |

现在我们可以看到该表已成功注册,如下图屏幕所示:

超集的一个重要特性是,它将根据数据类型自动选择我们可以对表的列执行的不同类型的操作。这决定了我们在用户界面的其余部分显示哪些类型的维度和指标。

为了选择这些选项,我们需要通过单击编辑图标来编辑表格,我们将看到此页面:

正如我们所看到的,Apache 超集已经自动识别了每个字段的数据类型,它还为我们提供了一个选项来为各种活动选择这些维度。下表列出了这些活动:

| 活动 | 描述 |
| 可分组 | 如果选中该复选框,则该字段可用作分组操作的一部分(在 SQL 中为GROUP BY)。 |
| 可滤过的 | 如果选中该复选框,则该字段可用作条件操作(WHERE子句)的一部分。 |
| 区分计数 | 如果选中该复选框,则该字段可用作字段计数(DISTINCT)操作的一部分。 |
| 总和 | 如果选择了复选框,则该字段可用作SUM()功能的一部分。 |
| 最小/最大 | 指示该字段可用作查找最小值和最大值的一部分。 |
| 是暂时的 | 指示该字段是时间维度。 |

如上所示进行更改,然后单击保存按钮。

现在,我们准备在接下来的步骤中开始创建切片和仪表板。

切片和仪表板创建

正如我们在前面几节中看到的,为了创建仪表板,我们首先需要创建切片。在本节中,我们将学习创建几个切片。

部门薪资拆分

在这一部分中,我们将学习如何创建可视化,显示每个部门的薪资细分百分比:

| 用户界面位置 | 图形 | 描述 |
| 补充报道 | | 数据源&图表类型:选择【员工】。数据源为【员工 _ 定额】,图表类型为“分布-国家职业类别 3 -饼图”在时间部分,选择出生日期作为时间栏,选择 100 年前作为起栏。在指标部分,从下拉列表中选择 sum_salary 作为值,部门名称作为分组依据。 |
| 图形输出 | | 点击“查询”按钮将会显示这张喜欢的图表。用部门薪资分手这个名字保存。 |

就像上一节一样,看看在没有任何编程知识的情况下创建好看的图形有多容易。

在下一节中,我们将从同一员工数据库中了解另一种类型的图形。

薪酬多样性

这是一个重要的图表,在这里我们确定了在整个组织历史中,不同性别之间的工资差异。这里我们用平均工资作为分析的基础。

| 用户界面位置 | 图形 | 描述 |
| 补充报道 | | Datasource & Chart Type: select [employees].[employee_norm] as the datasource and Time Series - line chart as chart type 在时间部分,选择出生日期作为时间列& 100 年前作为自列。在指标部分,选择平均工资作为指标,性别作为Group By。 |
| 输出 | | 显示每个性别每年平均工资的图形。用标题工资差异保存 |

从图中我们可以看出,男女之间的工资差距很大,而且非常接近。同期平均工资也有类似的增长。

在下一节中,我们将学习生成另一种类型的图形,这将使我们对数据有不同的见解。

每年每个角色的薪资变化

这是一个重要的统计数据,我们想知道组织中不同职位的工资变化有多大。

| 用户界面位置 | 图形 | 描述 |
| 补充报道 | | Datasource & Chart Type: select [employees].[employee_norm] as the datasource and Time Series - Percentage Change as chart type
In the Time section, Select from_date as Time column , Year as Time Granularity & 100 years ago as Since column.在指标部分,选择 sum_salary 作为指标,标题为 By。 |
| 输出 | | 点击查询,我们会得到下面的图形。用名称每年每个角色的薪资变化保存。 |

从这张图中我们可以发现,很少有角色在组织内的总薪酬中有非常大的差异。

到目前为止,我们已经创建了三个切片,我们将使用到目前为止创建的切片创建一个新的仪表板。

仪表板创建

在这一步中,我们将通过转到仪表板页面并单击添加仪表板图标(如前几节所示)来创建新的仪表板。

我们将看到下面的屏幕,在这里我们选择到目前为止创建的三个切片,然后单击保存:

仪表板保存成功后,我们可以看到它是这样的:

正如我们所看到的,仪表板是以简单的方式表达大量数据的非常强大的方式。

摘要

在本章中,我们学习了数据可视化,以及它如何帮助用户在不了解底层数据的情况下接收所需的消息。然后,我们看到了以图形方式可视化数据的不同方法。

我们浏览了用于可视化数据的 Hadoop 应用,如 Apache Druid 和 Apache 超集,并学习了如何将它们与 MySQL 等关系数据库一起使用。我们还看到了一个示例数据库,以帮助我们更好地理解应用。

在下一章中,我们将学习如何在云上构建我们的 Hadoop 集群。

十、使用云开发应用

在计算的早期,CPU 能力和存储非常匮乏,因此购买相关设备的成本非常高。随着 80 年代初苹果和微软在个人计算发展方面的进步,越来越多的个人和组织获得了这些计算设备。随着行业发展芯片制造方式,数十亿(如果不是数万亿)个晶体管被放在单个芯片上,这些计算设备的尺寸已经大幅缩小,从占据整个房间到在数据中心包含单个机架单元。当计算速度和存储设备容量开始增加时,个人和企业开始意识到高效管理其计算资源正在成为一项挑战。

互联网的广泛使用也对个人如何获取资源做出了重大贡献。

在本章中,我们将涵盖以下主题:

  • 什么是云?
  • 云中可用的技术
  • 规划云基础设施
    • 云中的高可用性
    • 云中的业务连续性规划
    • 云中的安全性
  • 在云中构建 Hadoop 集群
  • 云和内部应用
  • 云中的数据访问

什么是云?

云计算,简称 Cloud,是一种在互联网上租用和使用电子存储空间、计算能力、网络带宽、IP 地址、数据库、网络服务器等资源的简单方式。云推动了按使用付费模式,即客户只为这些资源的使用付费,就像电网为其客户的电力消耗付费一样。

云计算已经改变了个人和组织在互联网上访问和管理其服务器和应用的方式。在云计算出现之前,每个人都习惯在自己的场所或专用数据中心管理自己的服务器和应用。单个芯片上多核计算的原始计算能力(中央处理器和图形处理器)的增加以及存储空间(硬盘和固态硬盘)的增加给有效利用可用计算资源带来了挑战。

云中可用的技术

随着云计算应用的增加,企业已经开始构建各种技术,并将其提供给消费者。我们将浏览率先推出云产品的组织列表,以及它们提供的不同类型的技术。

以下是提供云服务的公司列表:

  • Microsoft Azure (Azure)
  • 亚马逊网络服务
  • 谷歌云平台
  • 国际商用机器公司
  • 销售队伍
  • 精力
  • 神谕
  • VMware

正在以下列形式向消费者提供各种类型的资源:

  • 平台即服务
  • 基础设施即服务
  • 软件即服务
  • 后端即服务
  • 网络即服务

随着此类产品的增加,许多组织无需关注基础设施,如房地产、服务器、防火墙、负载平衡器、交换机、电源等。但是他们可以从云提供商那里购买这些服务,然后专注于他们正在构建的应用。

现在,让我们看看顶级提供商微软、亚马逊和谷歌提供了哪些技术:

| 技术 | 蔚蓝 | 亚马逊网络服务 | 谷歌云 | 描述 |
| 服务器 | Azure 计算 | 亚马逊 EC2 | 谷歌计算引擎 ( GCE ) | 这项技术旨在提供随需应变的服务器,这些服务器可以是虚拟化的,也可以是专用/裸机的。 |
| 储存;储备 | 天蓝色存储 | 亚马逊 EBS | 谷歌存储 | 这是按需存储,可以根据需要连接到计算节点。一些供应商提供了按需扩展这些存储设备大小的能力。 |
| 网络 | Azure 网络 | 是 | 谷歌网络服务 | 提供商根据应用的网络要求提供 100 Mbps 至 10 Gbps 的网络带宽。 |
| 数据库 | Azure 数据库 | 亚马逊无线电数据系统 | 谷歌云 SQL | 对于托管数据库,我们不需要担心数据库服务器的维护,因为供应商会自动处理对这些服务器的支持。请记住,在某些情况下,我们需要为自己规划高可用性。 |
| 内容交付 | 天蓝色 CDN | 亚马逊云前线 | 谷歌云 CDN | 如果我们想通过利用交付网络将静态资产推给用户,这将非常有帮助,因为它可以显著降低延迟。我们还可以将它用作私有存储来存储所有文件,如备份、会议记录等。 |
| 域名系统 ( 域名系统) | DNA 蓝色 | 亚马逊 S3 路线 | 谷歌云域名系统 | 域名系统对于在互联网上运行我们的应用至关重要。这项服务让我们的生活变得更加轻松,因为它让我们的服务器可以被基础设施的其他部分访问,而不必运行我们自己的 DNS 服务器。 |
| 业务邮件 | 微软 o365 | 亚马逊工作邮件 | 谷歌邮件 | 对于要求以安全和可扩展的方式访问电子邮件和日历的组织来说,这是必须的。 |
| 机器学习 | Azure AI +机器学习 | 亚马逊机器学习 | 谷歌 ML 引擎 | 机器学习技术已经成为这些天的流行语。供应商提供了几种与机器学习相关的技术,因为我们只需关注我们需要做什么,而不必担心运行这些算法所需的基础设施。 |
| 分布式拒绝服务 ( 分布式拒绝服务)保护 | 天蓝色分布式拒绝服务保护 | 自动气象站防护罩 | – | 对于那些无法承受服务宕机的组织来说,这是一件非常重要的事情,当大规模拒绝服务攻击发生时,会影响网站的常规访问者。 |
| 监视 | 天蓝色监视器 | 亚马逊云观测 | 谷歌监控 | 如果不监控我们的应用和基础架构,我们将无法看到我们的表现。这些服务有助于我们保持业务正常运行,并对触发我们在云上运行的应用和基础架构宕机的事件做出响应。 |
| 容器 | 蔚蓝集装箱服务 ( AKS ) | 亚马逊库本内特弹性集装箱服务 ( 亚马逊 EKS ) | 谷歌库比厄引擎 | 这是一种基础架构,允许您将应用作为容器运行,而不是拥有完整的计算环境来运行它们。 |

规划云基础架构

传统组织有自己的 IT/基础架构团队来管理其专用服务器和网络。在规划向云的迁移时,我们必须牢记以下事项,以提高基础架构的可操作性。

规划云基础架构涉及:

  • 专用或共享服务器
  • 高可用性
  • 业务连续性规划
  • 安全
  • 网络体系结构

专用服务器与共享服务器

云提供商让您可以选择租赁完全拥有物理硬件的服务器,或者与我们这样的其他云用户共享物理硬件。为了对此做出决定,我们需要了解这些模型的优缺点。

专用服务器

这些类型的服务器的所有权属于单个用户或组织,不与任何其他用户共享。这种设置有以下几个优点:

  • 我们完全拥有物理服务器,我们分配的任何其他服务器都将在相同的硬件上进行配置
  • 对于这种设置,我们可能会收取更多的费用
  • 有了幽灵和熔毁,我们得到了更好的保护,因为硬件不与任何人共享
  • 我们不受邻居的影响,因为我们完全拥有硬件

共享服务器

对于简单的实验来说,拥有一台完整的服务器是昂贵的。在这种情况下,我们可以采用共享设置,在给定的物理硬件中租用一些资源。共享服务器的一些优势如下:

  • 我们只对按需租用的虚拟服务器收费。
  • 尽管云供应商提供了绝对的隔离,但有了 Spectre 和 flush,我们需要小心一点。
  • 比专用服务器更容易调配。

高可用性

根据我们计划运行的应用类型,我们必须了解供应商就正常运行时间为这些应用提供的服务级别协议 ( SLA ),并且我们需要相应地计划我们的应用。

让我们看一下使用 DNS 实现应用高可用性的简单方法:

在此设计中,会发生以下情况:

  • 当用户试图使用谷歌浏览器或火狐浏览器连接到我们的网站时,它首先会尝试联系域名系统服务器
  • DNS 服务器知道我们的前端服务器,并返回所有服务器的列表
  • 浏览器将直接连接到前端服务器
  • 前端服务器连接到数据库并返回请求的资源

在这个设计中,我们需要记住以下几点:

  • 前端服务器直接暴露在互联网中,因此我们应该采取适当的安全措施,例如防火墙或 DDos 保护来保护我们的服务器
  • 这些前端服务器也应该安装最新的操作系统软件,以便防止任何攻击
  • 数据库服务器不应该对外界可见,因此应该有一个适当的防火墙来允许来自前端服务器的请求

Cloud providers provide a private IP address. In order to minimize the risk of DB servers being accidentally exposed to the internet, we should block the public internet access to these servers.

让我们来看看另一种设计,它也能保护我们的网络服务器免受互联网攻击:

在本设计中,与之前的设计相比,我们做了以下更改:

  • 浏览器联系 DNS 服务器连接到我们的网站时, DNS 服务器提供负载均衡器(LB)/代理服务器的 IP 地址
  • 浏览器连接到这个 LB
  • LB 跟踪哪些后端服务器可用,然后将请求转发给服务器:
    • 服务器与数据库 ( 数据库)对话,并完成响应的构建
    • 响应被发送回 LB
  • LB浏览器发送响应

如果我们仔细观察这个设计,我们会发现这些是优于前一个设计的优点:

  • LB 隐藏了我们的基础设施,因此外人无法轻易知道我们的基础设施中有多少台服务器
  • LB 保护我们的网络服务器免受多种攻击
  • LB 可以进行 SSL 卸载,所有的流量加密/解密都发生在 LB 级别,我们的服务器可以免除 SSL 开销

Depending on the security policy of the organization, you might need to enable SSL on the web servers as well.

业务连续性规划

业务连续性规划 ( BCP )是组织处于成长阶段时需要考虑的一件非常重要的事情。网络、服务器或数据库或任何其他云基础架构组件的任何宕机都可能导致整个业务瘫痪。

在规划 BCP 时,有几件关键的事情要记住:

  • 基础设施不可用
  • 天灾
  • 商业数据

基础设施不可用

如果云提供商提供的服务出现计划外停机,我们所有的服务都会随之中断。为了最大限度地提高我们业务的可用性,我们需要在另一个地理区域建立备份设置。对于一些组织来说,这可能是昂贵的,因为整个设置将被复制,但是为了业务连续性,这是规划云基础架构时要考虑的一个重要特性。

天灾

地震、洪水、火灾等事件很难预测。因此,我们需要制定必要的计划来保持我们的业务运行,这取决于我们的服务器在云上的位置,以及供应商在构建数据中心时遵循的技术标准。

商业数据

业务数据以多种形式存在,并以文件、数据库服务器和大数据系统的形式存储。对于 BCP,我们需要仔细分析我们可以计划在哪些其他远程位置保留我们的数据副本,并进行测试运行,看看我们的应用是否可以通过单击一个按钮从任何一个地理位置无缝运行。

当我们在这里处理多个地理位置时,我们需要了解,当数据量巨大时,将数据从一个数据中心复制到另一个数据中心需要时间。我们的应用也必须重新设计,以防我们在最初的设计中没有考虑 BCP。

BCP 设计范例

此图试图解释我们如何通过在多个数据中心设置相同的应用来实现 BCP:

该系统可以是:

  • 霍特
  • 冷热

热-热系统

在 Hot-Hot 系统中,两个数据中心同时处于活动状态,为用户的流量提供服务。这里,我们采用了几种 CDN 和地理定位技术来将用户路由到给定的数据中心。

我们在这样做时面临的挑战是,如果一个区域完全空白,另一个区域应该有足够的空间来确保另一个区域的流量被吸收

使用该系统的优点是用户体验良好,因为在该设计中,用户被路由到最近的系统

冷热系统

在此系统/设计中,任何时候都只有一个区域处于活动状态,只有在 BCP(业务连续性规划)的情况下,我们才会回到另一个区域。

我们在使用该系统时面临的挑战如下:

  • 在问题出现之前,很容易忘记另一个地区;持续同步使用两个区域以及数据和软件是非常重要的。
  • 由于只有一个区域处于活动状态,因此必须考虑好用户到另一个数据中心的正确故障转移

使用该系统的优点是所有的写操作都发生在一个区域,这使得数据库设计变得简单。

安全

当您考虑迁移到云时,安全性非常重要。以下是需要记住的事情:

  • 服务器安全性
  • 应用安全
  • 网络安全性
  • 单点登录
  • AAA 要求

服务器安全性

当我们谈论云时,我们将永远无法物理访问服务器(除非我们获得云供应商的许可)。在这种情况下,我们必须了解云提供商遵循什么级别的策略和实践来确保我们的应用将在其上运行的服务器的物理安全。

例如,政府在考虑迁移到云时可能需要一整套不同的物理安全限制。同样,有几个标准,如 PCI 和 HIPAA,在这个模型上执行更强的规则。

如果我们的业务需要遵守这些标准,我们需要选择支持所有这些标准的云变体。

应用安全

在云上,我们可以选择自己托管应用,也可以使用作为服务提供的应用软件即服务 ( SaaS )。如果我们在自己调配的服务器(专用或共享)上托管应用,我们需要在服务器级别实施正确的防火墙规则,以及正确的用户访问规则,以确保我们的软件只允许经过授权和正确身份验证的用户。

如果应用是内部的,我们应该确保我们的员工获得 2FA 或 3FA 方法来登录这些服务。

网络安全性

为了保护我们在云上的服务器,我们需要实施适当的防火墙规则、域名系统区域,甚至拥有自己的虚拟专用网络,以确保我们的所有资产不会受到损害并暴露在互联网上。

云是互联网的代名词,对我们的数据和基础设施构成持续威胁。除非我们实施适当的安全措施,否则任何人都可以从我们的系统中随意获取任何东西。

单点登录

单点登录 ( 单点登录)已经受到在云上为各个部门使用多个应用的组织的欢迎。最近,组织已经停止构建自己的应用来运行业务,而是开始采用其他服务。当此类应用的数量增加时,这些应用的用户不断面临在所有这些网站中输入用户名和密码的挑战。

为了提供无缝的浏览体验,同时遵守企业安全标准,许多提供商实现了 OAuth 和 SAML,因为它们是行业公认的。

这些单点登录/身份提供者与公司员工数据库集成,进一步吸收企业的云应用,如下图所示:

本设计试图解释组织如何利用身份提供者来利用单点登录:

  • 组织与身份提供者共享员工和组织的详细信息:
    • 密码可以共享,也可以不共享,因为如果出现漏洞,可能会危及整个组织
    • 单点登录系统可以对员工强制使用自己的密码
  • 当用户尝试打开组织中的任何应用时,它会将用户重定向到单点登录提供程序
  • 单点登录提供程序完成身份验证,并与应用共享必要的凭据
  • 应用基于来自单点登录的反馈授权用户
  • 应用打开用户特定的细节,然后用户可以与应用交互

现在,这些单点登录的最大优势是,一旦用户与系统建立了会话,他们就可以登录到其他公司批准的应用,而无需进一步登录。

与单点登录提供商互动时,保密性是最大的挑战,因此组织应该仔细评估并选择符合其安全要求的正确解决方案。

AAA 要求

谈到安全性,重要的是要理解遵循 AAA 标准的应用将解决企业面临的许多挑战。

美国汽车协会标准涉及:

  • 证明
  • 批准
  • 审计

认证确保用户的身份被正确验证。

授权根据公司政策,进一步控制是否允许给定用户访问某些资源。

审核确保所有访问和使用资源的尝试都得到跟踪,这也可以用于任何调查,并提供适当的会计和计费(如果需要)。

通过遵循这些最佳实践,我们可以确保事情在大范围内顺利进行。

在云中构建 Hadoop 集群

我们之前看到,云提供了一种灵活且简单的方法来租用资源,如服务器、存储、网络等。云让消费者可以很容易地使用现收现付模式,但是云的许多复杂性被提供商隐藏了起来。

为了更好地理解 Hadoop 是否非常适合在云上,让我们尝试进一步挖掘,看看云是如何在内部组织的。

云的核心是以下机制:

  • 大量具有各种硬件配置的服务器
  • 通过 IP 网络连接并可用的服务器
  • 托管这些设备的大型数据中心
  • 跨地理位置的数据中心,具有演进的网络和数据中心设计

如果我们密切关注,我们正在谈论以下内容:

  • 大量不同的中央处理器架构
  • 速度和性能各不相同的大量存储设备
  • 具有不同速度和互连性的网络

让我们看一下这样一个云上数据中心的简单设计:

在上图中,我们有以下设备:

  • S1****S2:机架式交换机
  • U1-U6 :机架式服务器
  • R1 :路由器
  • 存储区域网络
  • 网络附加存储

正如我们所看到的,云提供商拥有大量这样的体系结构,以使它们具有可扩展性和灵活性。

您应该已经正确地猜到,当这种服务器的数量增加时,当我们请求新的服务器时,提供商可以将服务器分配到该区域的任何地方。

这使得计算和存储在一起有点困难,但也提供了弹性。

为了解决这一同地问题,一些云提供商提供了创建虚拟网络和使用专用服务器的选项,然后在这些服务器上分配他们的所有虚拟节点。这在某种程度上更接近数据中心设计,但足够灵活,可以在不需要时返还资源。

让我们回到 Hadoop,并提醒自己,为了从 Hadoop 系统中获得最佳效果,我们应该让 CPU 功率更接近存储。这意味着中央处理器和存储器之间的物理距离应该更小,因为总线速度与处理要求相匹配。

中央处理器和存储之间的输入/输出速度越慢(例如,iSCSI、存储区域网络、网络连接存储等),我们从 Hadoop 系统获得的性能就越差,因为数据是通过网络获取的,保存在内存中,然后馈送给中央处理器进行进一步处理。

这是在云上设计 Hadoop 系统时需要记住的重要事情之一。

除了性能原因,还有其他需要考虑的因素:

  • 扩展 Hadoop
  • 管理 Hadoop
  • 保护 Hadoop

现在,让我们尝试了解如何在云环境中处理这些问题。

在前几章中,我们看到可以通过以下方法安装 Hadoop:

  • 单独的
  • 半分布式
  • 全分布式

当我们想要在云上部署 Hadoop 时,我们可以使用以下方式进行部署:

  • 自定义外壳脚本
  • 云自动化工具(Chef、Ansible 等)
  • Apache 人 Ambari
  • 云供应商提供的方法
    • 谷歌云数据平台
    • 亚马逊 EMR
    • Microsoft HDInsight
  • 第三方管理的 Hadoop
    • Cloudera
  • 与云无关的部署
    • Apache 呼尔

谷歌云数据平台

在本节中,我们将学习如何使用谷歌云数据块来设置单节点 Hadoop 集群。

这些步骤可以分为以下几个部分:

  1. 获得谷歌云帐户。
  2. 激活谷歌云数据鹏服务。
  3. 创建新的 Hadoop 集群。
  4. 登录到 Hadoop 集群。
  5. 正在删除 Hadoop 集群。

获得谷歌云账户

本节假设您已经有一个谷歌云帐户。

激活谷歌云数据鹏服务

登录谷歌云控制台后,您需要访问云数据鹏服务。激活屏幕如下所示:

创建新的 Hadoop 集群

一旦在项目中启用了数据块,我们就可以点击创建来创建一个新的 Hadoop 集群。

之后,我们会看到另一个屏幕,需要在其中配置集群参数:

我把大部分东西都保留了默认值。稍后,我们可以点击创建按钮,为我们创建一个新的集群。

登录到群集

成功创建集群后,我们将自动进入集群列表页面。从那里,我们可以启动一个 SSH 窗口来登录我们创建的单节点集群。

SSH 窗口如下所示:

如您所见,Hadoop 命令对我们来说是现成的,我们可以运行任何标准的 Hadoop 命令来与系统交互。

删除群集

要删除集群,请单击删除按钮,它将显示一个确认窗口,如下图所示。此后,群集将被删除:

看起来很简单,对吧?是的。云提供商让用户使用云变得非常简单,并且只为使用付费。

云中的数据访问

云已经成为存储个人数据和业务数据的重要目的地。根据数据的重要性和保密要求,组织已经开始使用云来存储他们的重要数据集。

下图试图总结典型企业的各种访问模式,以及它们如何利用云来存储数据:

云提供商提供不同种类的存储。让我们看看这些类型是什么:

  • 块存储器
  • 基于文件的存储
  • 加密存储
  • 离线存储

块存储器

当我们希望与计算服务器一起使用这种存储,并希望通过主机操作系统管理存储时,这种类型的存储非常有用。

为了更好地理解这一点,这种类型的存储相当于我们购买笔记本电脑/MacBook 时附带的硬盘/固态硬盘。就笔记本电脑存储而言,如果我们决定增加容量,我们需要用另一个磁盘替换现有磁盘。

说到云,如果我们想增加更多容量,我们可以购买另一个更大容量的存储,并将其连接到我们的服务器。这是云变得流行的原因之一,因为它可以非常容易地添加或缩减我们需要的存储。

记住这一点很好,因为我们的应用有许多不同类型的访问模式,云供应商还提供具有不同存储/速度要求的数据块存储,以他们自己的容量/IOPS 等衡量。

让我们举一个容量升级要求的例子,看看我们如何在云上利用这种数据块存储。

为了理解这一点,让我们看一下这个图中的例子:

想象一下由管理员创建的名为 DB1 的服务器,原始容量为 100 GB 。后来,由于客户的意外需求,一个应用开始消耗所有的 100 GB 存储,因此管理员决定将容量增加到 1 TB (1,024 GB)。

在这种情况下,工作流是这样的:

  1. 在云上创建一个新的 1 TB 磁盘
  2. 将磁盘连接到服务器并安装
  3. 备份数据库
  4. 将数据从现有磁盘复制到新磁盘
  5. 启动数据库
  6. 验证数据库
  7. 销毁旧磁盘上的数据并归还磁盘

该过程得到了简化,但在生产中,这可能需要一些时间,具体取决于管理员执行的维护类型。但是,从云的角度来看,获取新的数据块存储非常快。

文件存储器

文件是计算的基础。如果你熟悉 UNIX/Linux 环境,你已经知道,一切都是文件在 UNIX 世界里。但是不要与此混淆,因为每个操作系统都有自己处理硬件资源的方式。在这种情况下,我们不担心操作系统如何处理硬件资源,但我们谈论的是用户作为日常业务的一部分存储的重要文档。

这些文件可以是:

  • 电影/会议录音
  • 图片
  • Excel 工作表
  • Word 文档

尽管它们在我们的计算机中看起来很简单,但它们可能具有重要的业务意义,当我们考虑将它们存储在云上时,应该小心处理。

大多数云提供商都提供了一种在云上存储这些简单文件的简单方法,并且在安全性方面也提供了灵活性。

获取此表单存储的典型工作流程如下:

  1. 创建一个唯一标识的新存储桶
  2. 向该桶添加私有/公共可见性
  3. 向存储在此存储桶中的数据添加多地域复制要求

一些云提供商根据客户在创建存储桶时选择的功能数量向其收费。

Please choose a hard-to-discover name for buckets that contain confidential data, and also make them private.

加密存储

这是业务关键数据的一个非常重要的要求,因为我们不希望信息泄露到组织范围之外。云提供商为我们提供静态加密设施。一些供应商选择自动执行此操作,一些供应商还提供了灵活性,允许我们为自己拥有的数据选择加密密钥和加密/解密方法。根据组织策略,我们应该遵循最佳实践来处理云上的这种情况。

随着存储设备性能的提高,加密不会在解密/加密文件时增加大量开销。如下图所示:

继续之前的相同示例,当我们选择加密 1 TB 的底层数据块存储时,我们可以利用云提供的加密,在那里他们会自动为我们加密和解密数据。因此,我们不必在主机操作系统上使用特殊的软件来进行加密和解密。

请记住,加密可以是供应商提供的数据块存储和基于文件的存储中的一项功能。

冷藏

这种存储对于在云中存储很少访问的重要备份非常有用。由于我们在这里处理的是一种特殊类型的数据,我们还应该意识到,云供应商可能会对来自该存储的数据访问收取很高的费用,因为它应该被写入一次并被遗忘(直到需要为止)。这种机制的优势在于,我们只需支付更少的费用就可以存储甚至千兆字节的数据。

摘要

在这一章中,我们了解了云计算的含义,并看到了云如何彻底改变了我们在互联网上访问和管理服务器和应用的方式。然后,我们浏览了不同提供商在云上提供的不同技术的列表。

我们还学习了如何规划我们的云基础架构,并研究了在云上构建我们自己的 Hadoop 集群所涉及的不同步骤。最后,我们看到了在云上存储和访问数据的不同方式。

在下一章中,我们将了解一些部署 Hadoop 集群的策略和最佳实践。

十一、面向生产的 Hadoop 集群部署

Hadoop 本身以强大的核心和文件系统开始,旨在应对大数据挑战。后来,在此基础上开发了许多应用,创建了一个相互配合的大型应用生态系统。随着应用数量开始增加,创建和管理 Hadoop 环境的挑战也随之增加。

在本章中,我们将了解以下内容:

  • Apache 人 Ambari
  • 一个带有 Ambari 的 Hadoop 集群

Apache Ambari 架构

Apache Ambari 遵循主/从架构,其中主节点指示从节点执行某些操作,并报告每个操作的状态。主节点负责跟踪基础设施的状态。为此,主节点使用数据库服务器,该服务器可以在设置期间进行配置。

为了更好地理解安巴里是如何工作的,让我们来看看安巴里的高层架构,如下图所示:

核心是,我们有以下应用:

  • Ambari 服务器
  • 安巴里特工
  • 安巴里网络用户界面
  • 数据库ˌ资料库

安巴里服务器

Ambari 服务器(ambari-server)是一个 shell 脚本,它是主服务器上所有管理活动的入口点。这个脚本在内部使用 Python 代码ambari-server.py,,并将所有请求路由到它。

安巴里服务器具有以下入口点,当向ambari-server程序传递不同参数时,这些入口点可用:

  • 守护进程管理
  • 软件升级
  • 软件设置
  • LDAP/PAM/Kerberos 管理
  • Ambari 备份和恢复
  • 其他选项

守护进程管理

当从命令行用startstopresetrestart参数调用脚本时,守护程序管理模式被激活。

例如,如果我们想要启动 Ambari 后台服务器,我们可以运行以下命令:

Example: ambari-server start

软件升级

一旦安装了 Ambari,我们就可以使用这种模式来升级 Ambari 服务器本身。当我们用upgrade标志调用ambari-server程序时,就会触发这一点。如果我们想升级整个安巴里堆栈,我们可以传递upgradestack标志:

Example: ambari-server upgrade

软件设置

一旦安巴里从互联网上下载(或通过 YUM 和 APT 安装),我们需要做一个软件的初步设置。当我们将setup标志传递给程序时,可以触发该模式。这个模式会问我们几个需要回答的问题。除非我们完成这一步,否则 Ambari 不能用于任何类型的服务器管理:

Example: ambari-server setup

LDAP/PAM/Kerberos 管理

T he 轻量级目录访问协议 ( LDAP )用于企业中的身份管理。为了使用基于 LDAP 的身份验证,我们需要使用以下标志:setup-ldap(用于使用ambari设置ldap属性)和sync-ldap(执行来自ldap服务器的数据同步):

Example: ambari-server setup-ldap
Example: ambari-server sync-ldap

可插拔身份验证模块 ( PAM )是任何 UNIX 或 Linux 操作系统中身份验证和授权的核心。如果我们想为安巴里利用基于 PAM 的访问,那么我们需要使用setup-pam选项来运行它。如果我们想从 LDAP 转向基于 PAM 的身份验证,我们需要使用migrate-ldap-pam运行它:

Example: ambari-server setup-pam
Example: ambari-server migrate-ldap-pam

Kerberos 是另一种先进的身份验证和授权机制,在网络环境中非常有用。这简化了大型服务器上的真实性、授权和审核 ( AAA )。如果我们想对 Ambari 使用 Kerberos,我们可以使用setup-kerberos标志:

Example: ambari-server setup-kerberos

Ambari 备份和恢复

如果我们想拍摄当前安装的 Ambari 的快照(不包括数据库),我们可以进入这个模式。这支持通过backuprestore标志调用的备份和恢复方法:

Example: ambari-server backup
Example: ambari-server restore

其他选项

除了这些选项之外,安巴里服务器程序还提供了其他选项,您可以使用-h(帮助)标志来调用这些选项。

安巴里特工

安巴里代理是一个程序,它运行在我们希望由安巴里管理的所有节点上。该程序定期向主节点发送心跳信号。使用该代理,ambari-server执行服务器上的许多任务。

Ambari 网络界面

这是 Ambari 应用的强大功能之一。该 web 应用由运行在主主机上的 Ambari 服务器程序公开;我们可以在端口8080上访问这个应用,它受到身份验证的保护。

一旦我们登录到这个门户网站,我们就可以控制和查看 Hadoop 集群的所有方面。

数据库ˌ资料库

Ambari 支持多个关系数据库管理系统来跟踪整个 Hadoop 基础设施的状态。在第一次安装 Ambari 服务器的过程中,我们可以选择我们想要使用的数据库。

在撰写本文时,安巴里支持以下数据库:

  • 一种数据库系统
  • 神谕
  • MySQL 或 MariaDB
  • 嵌入式 PostgreSQL
  • 微软 SQL 服务器
  • SQL Anywhere
  • 伯克利数据库

使用 Ambari 设置 Hadoop 集群

在本节中,我们将学习如何使用 Ambari 从头开始设置一个全新的 Hadoop 集群。为了做到这一点,我们需要四个服务器——一个服务器用于运行 Ambari 服务器,另外三个节点用于运行 Hadoop 组件。

服务器配置

下表显示了我们在本练习中使用的服务器配置:

| 服务器类型 | 名称 | CPU | ram | 磁盘 |
| Ambari 服务器节点 | 掌握 | one | 3.7 GB | 100 GB |
| Hadoop 节点 1 | 节点 1 | Two | 13 GB | 250 GB |
| Hadoop 节点 2 | 节点 2 | Two | 13 GB | 250 GB |
| Hadoop 节点 3 | 节点 3 | Two | 13 GB | 250 GB |

由于这是一个示例设置,我们对这种配置很满意。对于现实场景,请根据您的需求选择配置。

准备服务器

本部分和所有其他部分都假设您在所有服务器上都有一个工作正常的互联网连接,并且安全地用防火墙来防止任何入侵。

所有服务器都运行 CentOS 7 操作系统,因为它是一个使用 RPM/YUM 进行包管理的系统。当您在以下部分看到yum时,不要感到困惑。

在我们开始使用服务器之前,我们需要运行基本的实用程序来帮助我们解决服务器的各种问题。它们是作为下一个命令的一部分安装的。如果你不确定它们是什么,不要担心。除了mysql-connector-javawget以外,所有其他公用设施都不是强制性的:

sudo yum install mysql-connector-java wget iftop iotop smartctl -y

安装 Ambari 服务器

创建 Hadoop 集群的第一步是启动和运行我们的 Ambari 服务器应用。因此,使用 SSH 登录到主节点,并按顺序执行以下步骤:

  1. 使用以下命令下载 CentOS 7 的 Ambari YUM 存储库:
[user@master ~]$ wget http://public-repo-1.hortonworks.com/ambari/centos7/2.x/updates/2.6.1.5/ambari.repo

  1. 在这一步之后,我们需要使用以下命令将ambari.repo文件移动到/etc/yum.repos.d目录:
[user@master ~]$ sudo mv ambari.repo /etc/yum.repos.d
  1. 下一步是借助这个命令安装ambari-server包:
[user@master ~]$ sudo yum install ambari-server -y
  1. 我们将使用 MySQL 服务器作为我们的 Ambari 服务器。因此,让我们也安装所需的软件包:
[user@master ~]$ sudo yum install mariadb-server -y
  1. 让我们先配置 MySQL 服务器(或 MariaDB),然后再接触 Ambari 设置过程。这是通过以下命令完成的:
[user@master ~]$ sudo service mariadb start
Redirecting to /bin/systemctl start mariadb.service
  1. 然后,用密码ambari,创建一个名为ambari的数据库和一个名为ambari的用户,这样安巴里服务器的配置就很容易在下面的步骤中设置了。这可以通过以下 SQL 查询来完成:
CREATE DATABASE ambari;
GRANT ALL PRIVILEGES ON ambari.* to ambari@localhost identified by 'ambari';
GRANT ALL PRIVILEGES ON ambari.* to ambari@'%' identified by 'ambari';
FLUSH PRIVILEGES;
  1. 将这四行存储到名为ambari.sql的文本文件中,并使用以下命令执行:
[user@master ~] mysql -uroot < ambari.sql
  1. 这将创建一个数据库,并给予用户必要的特权。

Please use a strong password for production setup, otherwise your system will be vulnerable to any attacks.

现在我们已经完成了基础工作,让我们运行安巴里服务器设置。请注意,我们需要回答以下几个突出显示的问题:

[user@master ~]$ sudo ambari-server setup
Using python /usr/bin/python
Setup ambari-server
Checking SELinux...
SELinux status is 'enabled'
SELinux mode is 'enforcing'
Temporarily disabling SELinux
WARNING: SELinux is set to 'permissive' mode and temporarily disabled.
OK to continue [y/n] (y)? <ENTER>
Customize user account for ambari-server daemon [y/n] (n)? <ENTER>
Adjusting ambari-server permissions and ownership...
Checking firewall status...
WARNING: iptables is running. Confirm the necessary Ambari ports are accessible. Refer to the Ambari documentation for more details on ports.
OK to continue [y/n] (y)? <ENTER>
Checking JDK...
[1] Oracle JDK 1.8 + Java Cryptography Extension (JCE) Policy Files 8
[2] Oracle JDK 1.7 + Java Cryptography Extension (JCE) Policy Files 7
[3] Custom JDK
==============================================================================
Enter choice (1): <ENTER>
To download the Oracle JDK and the Java Cryptography Extension (JCE) Policy Files you must accept the license terms found at http://www.oracle.com/technetwork/java/javase/terms/license/index.html and not accepting will cancel the Ambari Server setup and you must install the JDK and JCE files manually.
Do you accept the Oracle Binary Code License Agreement [y/n] (y)? <ENTER>
Downloading JDK from http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-8u112-linux-x64.tar.gz to /var/lib/ambari-server/resources/jdk-8u112-linux-x64.tar.gz
jdk-8u112-linux-x64.tar.gz... 100% (174.7 MB of 174.7 MB)
Successfully downloaded JDK distribution to /var/lib/ambari-server/resources/jdk-8u112-linux-x64.tar.gz
Installing JDK to /usr/jdk64/
Successfully installed JDK to /usr/jdk64/
Downloading JCE Policy archive from http://public-repo-1.hortonworks.com/ARTIFACTS/jce_policy-8.zip to /var/lib/ambari-server/resources/jce_policy-8.zip

Successfully downloaded JCE Policy archive to /var/lib/ambari-server/resources/jce_policy-8.zip
Installing JCE policy...
Checking GPL software agreement...
GPL License for LZO: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
Enable Ambari Server to download and install GPL Licensed LZO packages [y/n] (n)? y <ENTER>
Completing setup...
Configuring database...
Enter advanced database configuration [y/n] (n)? y <ENTER>
Configuring database...
==============================================================================
Choose one of the following options:
[1] - PostgreSQL (Embedded)
[2] - Oracle
[3] - MySQL / MariaDB
[4] - PostgreSQL
[5] - Microsoft SQL Server (Tech Preview)
[6] - SQL Anywhere
[7] - BDB
==============================================================================
Enter choice (1): 3 <ENTER>
Hostname (localhost): 
Port (3306): 
Database name (ambari): 
Username (ambari): 
Enter Database Password (bigdata): ambari <ENTER>
Re-enter password: ambari <ENTER>
Configuring ambari database...
Configuring remote database connection properties...
WARNING: Before starting Ambari Server, you must run the following DDL against the database to create the schema: /var/lib/ambari-server/resources/Ambari-DDL-MySQL-CREATE.sql
Proceed with configuring remote database connection properties [y/n] (y)? <ENTER>
Extracting system views...
ambari-admin-2.6.1.5.3.jar
...........
Adjusting ambari-server permissions and ownership...
Ambari Server 'setup' completed successfully.
  1. 安装完成后,我们需要使用安装过程中生成的前一个文件在 Ambari 数据库中创建表。这可以通过以下命令完成:
[user@master ~] mysql -u ambari -pambari ambari < /var/lib/ambari-server/resources/Ambari-DDL-MySQL-CREATE.sql
  1. 下一步是我们启动ambari-server守护进程。这将启动 web 界面,我们将在以下步骤中使用该界面来创建 Hadoop 集群:
[user@master ~]$ sudo ambari-server start
Using python /usr/bin/python
Starting ambari-server
Ambari Server running with administrator privileges.
Organizing resource files at /var/lib/ambari-server/resources...
Ambari database consistency check started...
Server PID at: /var/run/ambari-server/ambari-server.pid
Server out at: /var/log/ambari-server/ambari-server.out
Server log at: /var/log/ambari-server/ambari-server.log
Waiting for server start...............................
Server started listening on 8080
DB configs consistency check: no errors and warnings were found.
Ambari Server 'start' completed successfully.
  1. 服务器设置完成后,配置 JDBC 驱动程序(这对所有其他节点也很有帮助):
[user@master ~] sudo ambari-server setup --jdbc-db=mysql --jdbc-driver=/usr/share/java/mysql-connector-java.jar

准备 Hadoop 集群

在创建 Hadoop 集群之前,我们还需要做几个步骤。

由于我们已经启动并运行了 Ambari 服务器,因此让我们生成一个 RSA 密钥对,用于 Ambari 服务器和 Ambari 代理节点之间的通信。

该密钥对允许 Ambari 服务器节点登录到所有 Hadoop 节点,并以自动方式执行安装。

如果您已经在采购服务器和基础架构的过程中这样做了,则此步骤是可选的:

[user@master ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): <ENTER>
Enter same passphrase again: <ENTER>
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:JWBbGdAnRHM0JFj35iSAcQk+rC0MhyHlrFawr+d2cZ0 user@master
The key's randomart image is:
+---[RSA 2048]----+
|.oo   *@@**    |
| +oo +o==*.o     |
| .=.. = .oo +    |
| .o+ o . o =     |
|.. .+ . S . .    |
|. .  o . E      |
| . .  o    |
|  o. .           |
|  ...            |
+----[SHA256]-----+

这将在/home/user/.ssh目录中生成两个文件:

  • ~/.ssh/id_rsa:这是需要保密的私钥文件
  • ~/.ssh/id_rsa.pub:这是允许任何 SSH 登录使用私钥文件的公钥文件

这个id_rsa.pub文件的内容应该放在所有 Hadoop 节点上的~/.ssh/authorized_keys中。在这种情况下,它们是节点服务器(1–3)。

This step of propagating all the public SSH keys can be done during the server provisioning itself, so a manual step is avoided every time we acquire new servers.

现在,我们将只使用安巴里网络界面完成所有工作。

创建 Hadoop 集群

在本节中,我们将使用 Ambari 网络界面构建一个 Hadoop 集群。本节假设以下情况:

  • 节点(1–3)可通过 SSH 从主服务器访问
  • 管理员可以使用主服务器的id-rsa私钥登录到节点(1–3)
  • UNIX 用户可以运行sudo并在节点(1–3)服务器上执行所有管理操作
  • 安巴里服务器安装完成
  • 浏览器可以访问安巴里网络界面,没有任何防火墙限制

Ambari 网络界面

让我们打开一个网页浏览器,使用http://<server-ip>:8080连接到安巴里服务器网页界面。我们会看到这样的登录屏幕。请输入admin作为用户名,admin作为密码继续:

登录成功后,我们将进入主页。

安巴里主页

这是主页面,用户界面上有多个选项。由于这是全新的安装,目前还没有可用的集群数据。

让我们看看主页的截图:

从这个地方,我们可以开展以下活动:

创建集群

正如您可能已经猜到的,本节用于启动一个向导,该向导将帮助我们从浏览器创建一个 Hadoop 集群。

管理用户和组

本节有助于管理可以使用和管理 Ambari web 应用的用户和组。

部署视图

该界面有助于为不同类型的用户创建视图,以及他们可以通过 Ambari 网络界面执行哪些操作。

由于我们的目标是创建一个新的 Hadoop 集群,我们将单击启动安装向导按钮并开始创建 Hadoop 集群的过程。

群集安装向导

Hadoop 集群创建分为多个步骤。我们将在下面的部分中完成所有这些步骤。首先,我们会看到一个屏幕,我们需要在其中命名我们的 Hadoop 集群。

命名您的集群

我选择了packt作为 Hadoop 集群名称。当在屏幕中输入 Hadoop 名称时,单击下一步。屏幕如下所示:

选择 Hadoop 版本

一旦我们命名了 Hadoop 集群,我们会看到一个屏幕来选择我们想要运行的 Hadoop 版本。

在撰写本文时,Ambari 支持以下 Hadoop 版本:

  • Hadoop 2.3
  • Hadoop 2.4
  • Hadoop 2.5
  • Hadoop 2.6(最高 2.6.3.0)

您可以选择安装的任何版本。我选择了默认选项 2.6.3.0 版本,可以在这个截图中看到:

单击屏幕底部的下一步继续下一步。

选择服务器

下一个逻辑步骤是选择我们将在其上安装 Hadoop-2.6.3.0 版本的服务器列表。如果您还记得原始表,我们将节点服务器命名为(1–3)。我们将在用户界面中输入这些。

由于安装将完全自动化,我们还需要提供我们在用户界面上一节中生成的 RSA 私钥。这将确保主节点可以通过 SSH 登录到服务器,而无需任何密码。

此外,我们需要提供一个已经在所有节点(1–3)服务器上创建的 UNIX 用户名,这些服务器也可以接受 RSA 密钥进行身份验证。

Add id_rsa.pub to ~/.ssh/authorized_keys on the node (1–3) servers.

请记住,这些主机名应该在域名系统 ( 域名系统)服务器中有正确的条目,否则安装将无法从这一步继续。

我给出的名字可以在下面的截图中看到:

输入数据后,点击注册并确认。

设置节点

在此步骤中,如果详细信息准确,Ambari 代理会自动安装在给定的节点上。成功确认如下所示:

如果我们想删除任何节点,这是我们可以在其中进行操作的屏幕。当我们准备好进入下一步时,请单击“下一步”。

选择服务

现在,我们需要选择要安装在我们选择的三台服务器上的应用/服务列表。

在撰写本报告时,安巴里支持以下服务:

| 应用/服务 | 应用描述 |
| HDFS | Hadoop 分布式文件系统 |
| Yarn+地图还原 2 | 下一代地图简化框架 |
| Tez | Hadoop 查询处理框架建立在 YARN 之上 |
| 储备 | 用于特定查询的数据仓库系统 |
| 巴什 | 非关系分布式数据库 |
| PIG | 用于分析 HDFS 数据集的脚本平台 |
| Sqoop | 在 Hadoop 和 RDBMS 之间传输数据的工具 |
| 驭象者 | 使用网络用户界面协调 Hadoop 作业的工作流 |
| 动物园管理员 | 分布式系统协调提供服务 |
| 猎鹰 | 数据处理和管理平台 |
| 暴风雨 | 流处理框架 |
| 水道 | 分布式系统,用于收集、聚合流式数据并将其移动到 HDFS |
| 累积性 | 分布式密钥/值存储 |
| 下面两个例子 | Amari 组件使用的共享服务 |
| 安巴里度量 | 基于 Grafana 的公制收集和存储系统 |
| 阿特拉斯 | 元数据和治理平台 |
| 卡夫卡 | 分布式流媒体平台 |
| 诺克斯 | 所有 Hadoop 组件的单点身份验证提供程序 |
| 日志搜索 | Ambari 管理的服务日志聚合器和查看器 |
| 看守人 | Hadoop 数据安全应用 |
| 游骑兵 KMS | 密钥管理服务器 |
| SmartSense | Hortonworks 智能感知工具,用于诊断应用 |
| Spark | 大规模数据处理框架 |
| 齐柏林笔记本 | 用于数据分析的网络笔记本 |
| 德鲁伊特 | 面向列的数据存储 |
| 象夫 | 机器学习算法 |
| 滑块 | 监控 Yarn 上应用的框架 |
| 超集 | 基于浏览器的关系数据库管理系统和德鲁伊数据探索平台 |

作为当前步骤的一部分,我们只选择了 HDFS 及其附属地区。屏幕显示如下:

做出选择后,单击用户界面底部的“下一步”按钮。

节点上的服务放置

在这一步中,我们将看到在我们选择安装的三个节点上自动选择服务。如果我们想定制服务在节点上的位置,我们可以这样做。位置如下所示:

当更改看起来不错时,单击下一步。

选择从节点和客户端节点

一些应用支持从属和客户端实用程序。在此屏幕中,我们需要选择要在其上安装这些应用的节点。如果您不确定,请单击“下一步”。屏幕如下所示:

定制服务

尽管 Ambari 会自动选择应用之间的大多数属性和链接,但它为我们提供了一些灵活性来选择某些功能的值,例如:

  • 数据库
  • 用户名
  • 密码

和其他有助于应用平稳运行的属性。这些在当前屏幕中以红色突出显示。

为了自定义这些,我们需要转到突出显示属性的选项卡,并根据我们的需要选择值。屏幕如下所示:

所有服务属性配置正确后,我们在 UI 中看不到任何红色内容,可以点击页面底部的“下一步”按钮。

审查服务

在这一步中,我们将看到到目前为止所做更改的摘要。我们可以选择打印更改,这样我们就不会忘记它们(别担心,所有这些都可以在以后的用户界面上找到)。现在,我们可以单击部署。此时将对节点进行实际更改。

如果我们取消此过程,将不会对服务器进行任何更改。向导的当前状态如下所示:

在节点上安装服务

在上一步中单击“部署”后,安巴里服务器将生成一个部署计划,应用将使用运行在所有节点上的安巴里代理并行部署在所有节点上。

在这一步中,我们可以看到实时部署的进度。

安装完所有组件后,它们将自动启动,我们可以在此屏幕中看到成功完成:

一切完成后,单击下一步。在任何失败的情况下,我们会看到失败的地方,并且会给我们一个重试安装的选项。如果有任何失败,我们需要挖掘错误并修复潜在的问题。

如果你已经按照本节开始时给出的说明去做了,你应该可以让一切顺利进行。

安装摘要

在这一步中,我们将看到已安装内容的摘要。屏幕如下所示:

单击“完成”按钮,这标志着 Hadoop 集群设置的结束。接下来,我们将进入集群仪表板。

集群仪表板

这是我们刚刚创建的 Hadoop 集群的主页,在这里我们可以看到已经安装的所有服务和健康传感器的列表。

我们可以在这个界面中管理 Hadoop 集群的所有方面。请随意探索该界面并使用它来了解更多信息:

这标志着使用 Ambari 创建 Hadoop 集群的结束。

Hadoop 集群

到目前为止,我们已经看到了如何使用 Ambari 创建单个 Hadoop 集群。但是,有没有对多个 Hadoop 集群的需求?

答案取决于业务需求。单个 Hadoop 集群和多个 Hadoop 集群各有利弊。

在我们讨论这两者的优缺点之前,让我们看看在什么场景下我们可以使用其中的任何一个。

面向整个企业的单一集群

这是最直接的方法,至少每个企业都从一个集群开始。随着业务多样性的增加,组织倾向于为每个部门或业务单位选择一个集群。

以下是一些优点:

  • 易操作性:由于只有一个 Hadoop 集群,管理起来非常容易,管理的时候团队规模也会是最优的。
  • 一站式商店:由于所有的公司数据都在一个地方,所以很容易想出创新的方法来使用数据并在数据之上生成分析。
  • 集成成本:企业内部的团队和部门可以非常轻松地与这个单一的系统进行集成。他们在管理应用时需要处理的配置不太复杂。
  • 服务成本:企业可以更好地了解其整个大数据使用情况,还可以以不那么严格的方式规划其系统的扩展。

采用这种方法的一些缺点如下:

  • 规模成为挑战:即使 Hadoop 可以在成百上千的服务器上运行,管理如此大的集群也成为一个挑战,尤其是在升级和其他变化期间。
  • 单点故障 : Hadoop 内部在 HDFS 文件系统中内置了复制功能。当更多节点出现故障时,很可能会出现数据丢失,并且很难从中恢复。
  • 治理是一项挑战:随着数据、应用和用户规模的增加,如果没有适当的规划和实施,跟踪数据是一项挑战。
    • 安全和机密数据管理:企业处理的数据种类繁多,从高度敏感的数据到短暂的数据都有。当各种数据被放入大数据解决方案中时,我们必须采用非常强大的身份验证和授权规则,以便数据只对正确的受众可见。

带着这些想法,让我们看看企业中拥有 Hadoop 集群的另一种可能性。

多个 Hadoop 集群

即使在一个组织中拥有单个 Hadoop 集群更容易维护,但有时拥有多个 Hadoop 集群以保持业务平稳运行并减少对单点故障系统的依赖也很重要。

这些多个 Hadoop 集群的使用有几个原因:

  • 裁员
  • 冷备份
  • 高可用性
  • 业务连续性
  • 应用环境

裁员

当我们想到冗余的 Hadoop 集群时,我们应该考虑我们可以保留多少冗余。我们已经知道, Hadoop 分布式文件系统 ( HDFS )内置了内部数据冗余。

考虑到 Hadoop 集群有许多围绕它构建的生态系统(服务,如 Yarn、卡夫卡等),我们应该仔细考虑和计划是让整个生态系统冗余,还是通过将数据保留在不同的集群中来仅使数据冗余。

由于有工具可以将数据从一个 HDFS 复制到另一个 HDFS,因此更容易使 Hadoop 的 HDFS 部分冗余。

让我们通过这张图来看看实现这一点的可能方法:

正如我们在这里看到的,主 Hadoop 集群运行其所有应用的完整堆栈,数据通过多个来源提供给它。

我们定义了两种类型的冗余集群:

完全冗余的 Hadoop 集群

该集群运行与主集群完全相同的一组应用,数据定期从主 Hadoop 集群中复制。由于这是从主群集到第二个群集的单向拷贝,因此当我们对此完全冗余的群集进行任何更改时,我们可以 100%确定主群集不会受到影响。

需要理解的一件重要的事情是,我们正在这个集群中运行应用的所有其他实例。由于每个应用都在自己的预定义位置维护其状态,因此应用状态不会从主 Hadoop 集群复制到该集群,这意味着在主 Hadoop 集群中创建的作业在该集群中不可见。这同样适用于卡夫卡的主题,动物园管理员节点,以及更多。

这种类型的集群有助于运行不同的环境,如质量保证、转移等。

数据冗余 Hadoop 集群

在这种类型的集群设置中,我们创建一个新的 Hadoop 集群,并从主集群复制数据,就像前面的例子一样;但是在这里,我们并不担心在这个集群中运行的其他应用。

这种类型的设置适用于:

  • 在不同的地理位置为 Hadoop 进行数据备份
  • 与其他企业/组织共享大数据

冷备份

随着数据的老化,冷备份对企业来说非常重要。尽管 Hadoop 旨在存储无限量的数据,但并不总是需要保持所有数据可供处理。

出于审计目的和历史原因,有时需要保存数据。在这种情况下,我们可以创建一个只有 HDFS(文件系统)组件的专用 Hadoop 集群,并定期将所有数据同步到该集群中。

该系统的设计类似于数据冗余 Hadoop 集群。

高可用性

尽管 Hadoop 在体系结构中有多个组件,但由于内部设计,并非所有组件都高度可用。

Hadoop 的核心组件是分布式、容错的文件系统 HDFS。HDS 有多个组件,其中之一是名称节点,它是文件在 HDFS 的位置的注册表。在早期版本的 HDS 中,名称节点是单点故障,在最新版本中,添加了辅助名称节点,以帮助满足 Hadoop 集群的高可用性要求。

为了使 Hadoop 生态系统的每个组件都成为一个高可用性系统,我们需要添加多个冗余节点(它们都有自己的成本),作为一个集群一起工作。

还有一点需要注意的是,Hadoop 的高可用性在单个地理区域内是可能的,因为应用的数据位置是 Hadoop 的关键因素之一。当我们有多个数据中心时,我们需要交替考虑,以实现数据中心的高可用性。

业务连续性

这是业务连续性规划 ( BCP )的一部分,如果规划不正确,自然灾害可能会终结 Hadoop 系统。

这里的策略是使用多个地理区域作为提供商来运行大数据系统。当我们谈论多个数据中心时,明显的挑战是网络和管理这两个系统的相关成本。最大的挑战之一是如何保持多个区域同步。

一个可能的解决方案是在其他地理区域构建一个完全冗余的 Hadoop 集群,并定期保持数据同步。在一个地区发生任何灾难/故障的情况下,我们的业务不会停止,因为我们可以顺利运营。

应用环境

许多企业内部采用不同的方式将软件发布到生产环境中。作为其中的一部分,他们遵循几种连续集成方法,以便更好地控制 Hadoop 环境的稳定性。用主生产环境中 X%的数据构建多个较小的 Hadoop 集群并在此运行所有应用是很好的。

应用可以在这些专用环境(质量保证、试运行等)上构建它们的集成测试,并且一旦一切正常,就可以将其软件发布到生产环境中。

我遇到的一个实践是,组织倾向于直接将代码运送到生产中,并最终因为未经测试的工作流或错误而面临应用的中断。拥有专用的 Hadoop 应用环境来彻底测试软件并实现更高的正常运行时间和更快乐的客户是一个很好的做法。

Hadoop 数据拷贝

我们在前面的章节中已经看到,拥有高度可用的数据对于企业取得成功并跟上竞争对手的步伐非常重要。

在本节中,我们将探讨实现高可用性数据设置的可能方法。

HDFS 数据拷贝

Hadoop 以 HDFS 为核心存储文件。HDFS 具有机架感知能力,并且足够智能,当应用在数据节点上运行时,可以减少网络数据传输。

在 HDFS 环境中,数据复制的首选方式之一是使用 DistCp。这方面的官方文件可在以下网址获得http://hadoop.apache.org/docs/r1.2.1/distcp.html

我们将看到几个将数据从一个 Hadoop 集群复制到另一个 Hadoop 集群的例子。但在此之前,让我们看看数据是如何布局的:

为了将数据从生产 Hadoop 集群复制到备份 Hadoop 集群,我们可以使用distcp。让我们看看怎么做:

hadoop distcp hdfs://NameNode1:8020/projects hdfs://NameNode2:8020/projects
hadoop distcp hdfs://NameNode1:8020/users hdfs://NameNode2:8020/users
hadoop distcp hdfs://NameNode1:8020/streams hdfs://NameNode2:8020/streams
hadoop distcp hdfs://NameNode1:8020/marketing hdfs://NameNode2:8020/marketing
hadoop distcp hdfs://NameNode1:8020/sales hdfs://NameNode2:8020/sales

当我们运行distcp命令时,会创建一个 MapReduce 作业来自动找出文件列表,然后将它们复制到目的地。

完整的命令语法如下所示:

Distcp [OPTIONS] <source path …> <destination path>
  • OPTIONS:这些是命令用来控制执行行为的多个选项。
  • source path:源路径可以是 Hadoop 支持的任何有效的 URI 文件系统。DistCp 支持一次获取多个源路径。
  • destination path:这是单个路径,需要复制所有的源路径。

让我们仔细看看几个重要的选项:

| 标志/选项 | 描述 |
| append | 如果目标文件已经存在,则将数据增量写入目标文件(仅执行append,不执行数据块级检查来进行增量复制)。 |
| async | 以非阻塞方式执行复制。 |
| atomic | 执行所有文件复制或中止,即使一个失败。 |
| Tmp <path> | 用于原子提交的路径。 |
| delete | 如果文件不在源树中,则从目标中删除文件。 |
| Bandwidth <arg> | 限制复制过程中要使用的网络带宽。 |
| f <file-path> | 文件名由需要复制的所有路径的列表组成。 |
| i | 忽略文件复制过程中的任何错误。 |
| Log <file-path> | 保存执行日志的位置。 |
| M <number> | 用于复制的最大并发映射数。 |
| overwrite | 覆盖文件,即使它们存在于目标上。 |
| update | 仅复制丢失的文件和目录。 |
| skipcrccheck | 如果通过,传输过程中将跳过循环冗余校验。 |

摘要

在这一章中,我们了解了 Apache Ambari,并详细研究了它的体系结构。然后,我们了解了如何使用 Ambari 准备和创建我们自己的 Hadoop 集群。为了做到这一点,我们还研究了在准备集群之前按照要求配置 Ambari 服务器。我们还了解了单个和多个 Hadoop 集群,以及如何根据业务需求使用它们。

posted @ 2025-10-01 11:29  绝不原创的飞龙  阅读(8)  评论(0)    收藏  举报