下一代大数据教程-一-

130 阅读1小时+

下一代大数据教程(一)

原文:Next-Generation Big Data

协议:CC BY-NC-SA 4.0

一、下一代大数据

尽管大数据令人兴奋,但大多数任务关键型数据仍存储在关系数据库管理系统中。这一事实得到了最近在线研究的支持,并得到了我自己在众多大数据和商业智能项目中工作的专业经验的证实。尽管人们对非结构化和半结构化数据有着广泛的兴趣,但对于大多数组织来说,从最大的公司和政府机构到小型企业和技术初创企业,结构化数据仍然占管理数据的很大比例。处理非结构化和半结构化数据的用例虽然有价值且有趣,但却少之又少。除非你在一家处理大量非结构化数据的公司工作,比如谷歌、脸书或苹果,否则你最有可能处理结构化数据。

自 10 多年前 Hadoop 问世以来,大数据已经发展成熟。除去所有的宣传,结构化数据处理和分析显然已经成为大数据的下一代杀手级用例。大多数大数据、商业智能和高级分析用例都处理结构化数据。事实上,Apache Impala、Apache Phoenix 和 Apache Kudu 等大数据领域的一些最受欢迎的进步,以及 Apache Spark 最近对 Spark SQL 和 DataFrames API 的强调,都是为了提供结构化数据处理和分析的能力。这在很大程度上是因为大数据最终被接受为企业的一部分。随着大数据平台的改进和获得新的功能,它们已经成为昂贵的数据仓库平台和关系数据库管理系统的合适替代品,用于存储、处理和分析任务关键型结构化数据。

关于这本书

本书面向商业智能和数据仓库专业人员,他们有兴趣使用 Apache Kudu、Apache Impala 和 Apache Spark 获得关于下一代大数据处理和分析的实用和真实的见解。有经验的大数据专业人士,如果希望了解更多关于 Kudu 和其他高级企业主题的信息,如实时数据摄取和复杂事件处理、物联网(IoT)、分布式内存计算、云中的大数据、大数据治理和管理、实时数据可视化、数据整理、数据仓库优化和大数据仓储,也将从本书中受益。

我假设读者对 Hadoop 的各种组件有基本的了解。一些关系数据库管理系统、商业智能和数据仓库方面的知识也很有帮助。如果您想运行提供的示例代码,需要一些编程经验。我主要关注三个主要的 Hadoop 组件:Apache Spark、Apache Impala 和 Apache Kudu。

阿帕奇 Spark

Apache Spark 是下一代数据处理框架,具有高级内存功能和有向无环图(DAG)引擎。它可以通过内置的机器学习、图形处理、流和 SQL 支持来处理交互式、实时和批处理工作负载。Spark 是为了解决 MapReduce 的局限性而开发的。在大多数数据处理任务中,Spark 比 MapReduce 快 10-100 倍。Spark 是最受欢迎的 Apache 项目之一,目前被世界上一些最大的创新公司使用。我在第五章和第六章讨论 Apache Spark 和 Spark 与 Kudu 的集成。

阿帕奇黑斑羚

Apache Impala 是一个大规模并行处理(MPP) SQL 引擎,旨在运行在 Hadoop 平台上。该项目由 Cloudera 启动,最终捐赠给了 Apache 软件基金会。在性能和可扩展性方面,Impala 可以与传统的数据仓库平台相媲美,它是为商业智能和 OLAP 工作负载而设计的。Impala 兼容一些最流行的 BI 和数据可视化工具,例如 Tableau、Qlik、Zoomdata、Power BI 和 MicroStrategy 等等。我在第三章和第四章介绍了 Apache Impala 和 Impala 与 Kudu 的集成。

Apache Kudu

Apache Kudu 是一个新的可变列存储引擎,旨在处理快速数据插入和更新以及高效的表扫描,实现实时数据处理和分析工作负载。当与 Impala 一起使用时,Kudu 是大数据仓库、EDW 现代化、物联网(IoT)、实时可视化、复杂事件处理和机器学习的特征存储的理想选择。作为一个存储引擎,Kudu 的性能和可扩展性可以与其他列存储格式(如 Parquet 和 ORC)相媲美。它的执行速度也明显快于使用 HBase 的 Apache Phoenix。我在第二章讨论 Kudu。

浏览本书

这本书由易于理解的章节组成,每次只关注一两个关键概念。第 1 到 9 章被设计成按顺序阅读,每一章都建立在前一章的基础上。第 10 到 13 章可以根据你的兴趣以任何顺序阅读。这些章节充满了实际的例子和一步一步的指示。在此过程中,您将发现大量关于最佳实践和建议的实用信息,这些信息将引导您在大数据之旅中朝着正确的方向前进。

第一章——新一代大数据简要介绍了本书的内容。

第二章-Kudu 简介提供了对 Apache Kudu 的介绍,从讨论 Kudu 的架构开始。我谈论各种话题,比如如何使用客户端 API 从 Impala、Spark、Python、C++和 Java 访问 Kudu。我提供了关于如何管理、配置和监控 Kudu 的细节,包括 Kudu 的备份和恢复以及高可用性选项。我还讨论了 Kudu 的优势和局限性,包括实用的变通方法和建议。

第三章——黑斑羚简介提供阿帕奇黑斑羚的简介。我用简单易懂的例子讨论了 Impala 的技术架构和功能。我将详细介绍如何执行系统管理、监控和性能调优。

第四章–使用 Impala 和 Kudu 进行高性能数据分析涵盖了 Impala 和 Kudu 的集成,并提供了关于如何利用这两种组件来提供高性能数据分析环境的实际示例和现实建议。我讨论了 Impala 和 Kudu 的优势和局限性,包括实用的解决方法和建议。

第五章——Spark 简介提供了 Apache Spark 的简介。我介绍了 Spark 的架构和功能,用实用的解释和易于理解的例子来帮助您立即开始 Spark 开发。

第六章–使用 Spark 和 Kudu 进行高性能数据处理涵盖了 Spark 和 Kudu 集成,并提供了关于如何使用这两种组件进行大规模数据处理和分析的实际示例和现实建议。

第七章–批处理和实时数据接收和处理包括使用本地和第三方商业工具(如 Flume、Kafka、Spark Streaming、StreamSets、Talend、Pentaho 和 such)进行批处理和实时数据接收和处理。我提供了如何实现复杂事件处理和物联网(IoT)的分步示例。

第章第 8——大数据仓库包括使用 Impala 和 Kudu 设计和实现星形和雪花形维度模型。我将讨论如何利用 Impala 和 Kudu 进行数据仓库存储,包括它的优点和局限性。我还讨论了 EDW 现代化用例,如数据整合、数据归档、分析和 ETL 卸载。

第九章——大数据可视化和数据整理讨论了为超大数据集设计的实时数据可视化和争论工具,并提供了易于理解的示例和建议。

第十章——分布式内存大数据计算涵盖了 Alluxio,以前称为 Tachyon。我讨论了它的架构和功能。我还讨论了 Apache Ignite 和 Geode。

第十一章—大数据治理和管理涵盖了大数据治理和管理。我使用 Cloudera Navigator 讨论了数据沿袭、元数据管理、审计和策略实现。我还研究了其他流行的数据治理和元数据管理应用程序。

第十二章——云中的大数据包含了在云中部署和使用 Apache Kudu、Spark 和 Impala 的分步说明和示例。

第十三章—大数据案例研究提供了六个创新的大数据案例研究,包括挑战、实现细节、解决方案和成果的详细信息。案例研究是在 Cloudera 的许可下提供的。

摘要

如果您想遵循本书中的示例,我建议您建立自己的 Cloudera 集群作为开发环境。您还可以使用最新版本的 Cloudera Quickstart VM,可以从 Cloudera 的网站上免费下载。我不建议使用不同的数据平台,如 Hortonworks、MapR、EMR 或 Databricks,因为它们与本书中讨论的其他组件不兼容,如 Impala 和 Kudu。

二、Kudu 简介

Kudu 是一个 Apache 许可的开源列存储引擎,为 Apache Hadoop 平台构建。它支持快速顺序和随机读写,支持实时流处理和分析工作负载。 i 它与 Impala 集成,允许您使用 SQL 插入、删除、更新、向上插入和检索数据。Kudu 还与 Spark(和 MapReduce)集成,用于快速和可扩展的数据处理和分析。像 Apache Hadoop 生态系统中的其他项目一样,Kudu 运行在商用硬件上,并被设计为高度可伸缩和高度可用。

Apache Kudu 项目由 Cloudera 的软件工程师 Todd Lipcon 于 2012 年创立,他是 Hadoop、HBase 和 Thrift 项目的 PMC 成员和委员。 ii Kudu 的开发是为了解决 HDFS 和 HBase 的局限性,同时结合两者的优势。虽然 HDFS 支持快速分析和大型表扫描,但存储在 HDFS 的文件是不可变的,只能在创建后追加。 iii HBase 使更新和随机访问数据成为可能,但对于分析工作负载来说速度较慢。Kudu 可以处理高速数据和实时分析,允许您同时更新 Kudu 表和运行分析工作负载。在某些情况下,HDFS 上的批处理和分析仍然略快于 Kudu,HBase 在随机读写性能方面优于 Kudu。Kudu 在中间的某个地方。如图 2-1 所示,在随机读取和写入方面,Kudu 的性能与 HDFS 的 Parquet (Kudu 在某些情况下更快)和 HBase 非常接近,因此大多数时候性能差异可以忽略不计。

A456459_1_En_2_Fig1_HTML.jpg

图 2-1

High-level performance comparison of HDFS, Kudu, and HBase

在 Kudu 之前,一些数据工程师使用一种称为 Lambda 架构的数据处理架构来解决 HDFS 和 HBase 的局限性。Lambda 架构有一个速度和批处理层(从技术上讲,还有一个服务层)。交易数据转到速度层(通常是 HBase ),用户在这里可以立即访问最新数据。速度层的数据以拼花格式定期(每小时或每天)复制到批处理层(通常为 HDFS ),用于报告和分析。正如您在图 2-2 中看到的,数据被复制了两次,数据流水线比 Lambda 架构所需的更复杂。这有点类似于典型的企业数据仓库环境,其中 OLTP 数据库代表“速度层”,数据仓库充当“批处理层”

A456459_1_En_2_Fig2_HTML.jpg

图 2-2

Lambda Architecture

Kudu 使 Lambda 架构过时,因为它能够同时处理随机读写和分析工作负载。如图 2-3 所示,有了 Kudu,就不会有数据重复,数据管道也简单多了。

A456459_1_En_2_Fig3_HTML.jpg

图 2-3

Modern data ingest pipeline using Kudu

Kudu 代表结构化数据

Kudu 被设计用来存储类似于关系数据库的结构化数据。事实上,Kudu(与 Impala 一起使用时)通常用于关系数据管理和分析。Kudu 在功能、性能和可伸缩性方面与商业数据仓库平台不相上下。我们将在本章后面讨论 Impala 和 Kudu 的集成,并在第四章中更详细地讨论。

用例

在开始之前,先说一下 Kudu 不是什么。库都并不是要取代 HBase 或者 HDFS。HBase 是一个无模式的 NoSQL 风格的数据存储,这使得它适用于稀疏数据或需要可变模式的应用程序。HBase 专为需要随机读写的 OLTP 类型工作负载而设计。有关 HBase 的更多信息,请参见 HBase 在线文档。

HDFS 旨在存储所有类型的数据:结构化、半结构化和非结构化。如果您需要将数据存储在高度可伸缩的文件系统中,HDFS 是一个很好的选择。如前所述,在运行分析工作负载时,HDFS(使用 Parquet)在某些情况下仍然比 Kudu 快。有关 HDFS 的更多信息,请参阅 HDFS 在线文档。

如前所述,Kudu 擅长存储结构化数据。它没有 SQL 接口,因此您需要将 Kudu 与 Impala 配对。通常认为应该存储在关系数据库或时间序列数据库中的数据也很可能存储在 Kudu 中。下面是一些可以利用 Kudu 的用例。 iv

关系数据管理和分析

Kudu(与 Impala 一起使用时)展示了关系数据库的大部分特征。它在行和列中存储数据,并在数据库和表中组织它们。Impala 提供了一个高度可伸缩的 MPP SQL 引擎,允许您使用 ANSI SQL 命令与 Kudu 表进行交互,就像使用关系数据库一样。关系数据库用例可以分为两大类,联机事务处理(OLTP)和决策支持系统(DSS ),或者用现代术语来说就是数据仓库。Kudu 不是为 OLTP 设计的,但是它可以用于数据仓库和其他企业数据仓库(EDW)现代化用例。

数据库

Kudu 可用于维度建模——现代数据仓库和在线分析处理(OLAP)的基础。Kudu 缺少外键约束、自动递增列和其他一些您通常会在传统数据仓库平台中发现的特性;但是,这些限制并不妨碍您在事实和维度表中组织数据。通过 ODBC/JDBC,可以使用您最喜欢的 BI 和 OLAP 工具访问 Impala。我在第八章中讨论了使用 Impala 和 Kudu 的数据仓库。

ETL 卸载

ETL 卸载是您可以使用 Kudu 的许多 EDW 优化用例之一。由于 ETL 流程的运行时间远远超出了其处理窗口,并且进入了工作时间,因此关键报告对整个组织都不可用。通过将耗时的 ETL 处理卸载到廉价的 Kudu 集群,ETL 作业可以在工作时间之前完成,从而在业务用户需要时为他们提供关键的报告和分析。我在第八章中讨论了使用 Impala 和 Kudu 的 ETL 卸载。

分析卸载和活动归档

Impala 是一个非常快速和可伸缩的 MPP SQL 引擎。通过将一些特别的查询和报告重定向到 Impala 和 Kudu,可以减轻企业数据仓库的负载。与花费数百万美元升级您的数据仓库相比,分析卸载和活动归档是优化您的 EDW 环境的更智能、更具成本效益的方式。我在第八章中讨论了使用 Impala 和 Kudu 的分析卸载和主动归档。

数据整合

对于大型组织来说,将数百或数千个遗留数据库分散在整个企业中并不罕见,这需要支付数百万美元的许可、管理和基础架构成本。通过将这些数据库整合到一个 Kudu 集群中,并使用 Impala 提供 SQL 访问,您可以显著降低成本,同时提高性能和可伸缩性。我在第八章中讨论了使用 Impala 和 Kudu 的数据整合。

物联网与时间序列

Kudu 非常适合物联网和时间序列应用,在这些应用中,传感器数据的实时数据摄取、可视化和复杂事件处理至关重要。小米、 v 和澳洲国防部 vi 等几家大公司和政府机构都在成功使用 Kudu 进行物联网用例。我在第七章使用 Impala、Kudu 和 StreamSets 讨论物联网、实时数据摄取和复杂事件处理。我在第九章中讨论了使用 Zoomdata 的实时数据可视化。

机器学习平台的特征存储

数据科学团队通常会创建一个集中的功能库,他们可以在其中发布并与其他团队共享精选的权威功能集,以创建机器学习模型。使用不可变的数据格式(如 ORC 和 Parquet)创建和维护特征存储非常耗时、麻烦,并且需要太多不必要的艰苦工作,尤其是对于大型数据集。使用 Kudu 作为快速和高度可扩展的可变特征库,数据科学家和工程师可以使用熟悉的 SQL 语句轻松更新和添加特征。在数据科学家不断迭代构建、测试和提高预测模型准确性的敏捷环境中,在数秒或数分钟内更新要素库的能力至关重要。在第六章中,我们使用 Kudu 作为使用 Spark MLlib 构建预测机器学习模型的特征库。

Note

Kudu 允许每个表最多 300 列。如果需要存储 300 个以上的特性,HBase 是比较合适的存储引擎。HBase 表可以包含数千或数百万列。使用 HBase 的缺点是,与 Kudu 相比,它在处理全表扫描时效率不高。Apache Kudu 社区正在讨论在 Kudu 的未来版本中解决 300 列的限制。

严格来说,可以通过设置不安全标志来绕过 Kudu 的 300 列限制。例如,如果您需要创建一个包含 1000 列的 Kudu 表,您可以使用以下标志启动 Kudu master:-unlock-unsafe-flags-max-num-columns = 1000。这还没有经过 Kudu 开发团队的彻底测试,因此不建议用于生产。

关键概念

Kudu 引入了几个概念来描述其架构的不同部分。

表 A 表是 Kudu 中存储数据的地方。每个 Kudu 表都有一个主键,并被分成称为 tablets 的段。

平板电脑平板电脑或隔板是桌子的一部分。

平板电脑服务器平板电脑服务器存储平板电脑并向客户端提供平板电脑。

主设备主设备跟踪所有集群元数据并协调元数据操作。

所有集群元数据的目录表中央存储。目录表存储有关桌子和平板电脑的位置、它们的当前状态以及副本数量的信息。目录表存储在主数据库中。

体系结构

类似于其他 Hadoop 组件的设计,如 HDFS 和 HBase(以及他们的谷歌同行 BigTable 和 GFS),Kudu 有一个主从架构。如图 2-4 所示,Kudu 由一个或多个主服务器组成,负责集群协调和元数据管理。Kudu 还拥有一台或多台平板电脑服务器,用于存储数据并提供给客户端应用程序。 vii 对于一个平板来说,任何时候都只能有一个代理大师,领袖。如果领导变得不可用,则另一个主设备被选举成为新的领导。类似于主,一个平板服务器充当领导者,其余的都是跟随者。所有写请求都发送给领导者,而读请求则发送给领导者或副本。存储在 Kudu 中的数据使用 Raft Consensus 算法进行复制,只要副本总数中的大部分仍然可用,就可以保证数据的可用性在一些副本丢失后仍然存在。只要有可能,Kudu 就会复制逻辑操作而不是实际的物理数据,从而限制跨集群的数据移动量。

Note

Raft 共识算法在 Diego Ongaro 和 John Ousterhout 所著的《Raft 论文:寻找可理解的共识算法(扩展版)》中有详细描述。 viii 迭戈·翁加罗(Diego Ongaro)的博士论文《共识:桥接理论与实践》(Consensus:Bridging Theory and Practice),由斯坦福大学于 2014 年发表,对论文内容进行了更详细的阐述。IX

A456459_1_En_2_Fig4_HTML.jpg

图 2-4

Kudu Architecture

多版本并发控制(MVCC)

大多数现代数据库使用某种形式的并发控制来确保读取一致性,而不是传统的锁定机制。Oracle 从 6.0 版开始就有了多版本一致性模型。 x Oracle 使用回滚段中维护的数据来提供读取一致性。回滚段包含已被未提交或最近提交的事务修改的先前数据。 xi MemSQL 和 SAP HANA 也使用 MVCC 管理并发。最初,SQL Server 只支持悲观并发模型,使用锁定来强制并发。结果,读者屏蔽作家,作家屏蔽读者。随着并发用户和操作数量的增加,阻塞问题和锁争用的可能性也会增加,从而导致性能和可伸缩性问题。SQL Server 领域的情况变得如此糟糕,以至于开发人员和 DBA 不得不在查询中使用 NOLOCK 提示,或者设置 READ UNCOMITTED 隔离级别,容忍脏读以换取较小的性能提升。从 SQL Server 2005 开始,微软引入了自己的多版本并发控制版本,称为行级版本控制。 xii SQL Server 没有相当于回滚段的功能,所以它使用 tempdb 来存储以前提交的数据。Teradata 没有多版本一致性模型,依赖事务和锁来实现并发控制。 十三

与 Oracle、MemSQL 和 SAP HANA 类似,Kudu 使用多版本并发控制来确保读取一致性。 xiv 读者不屏蔽作家,作家不屏蔽读者。Kudu 的乐观并发模型意味着在大型全表扫描期间不需要操作来获取锁,从而大大提高了查询性能和可伸缩性。

黑斑羚和库杜

Impala 是 Kudu 默认的 MPP SQL 引擎。Impala 允许您使用 SQL 与 Kudu 进行交互。如果您有使用 SQL 和存储引擎紧密集成的传统关系数据库的经验,您可能会发现 Kudu 和 Impala 相互解耦并不常见。Impala 旨在与其他存储引擎(如 HDFS、HBase 和 S3)一起工作,而不仅仅是 Kudu。将其他 SQL 引擎如 Apache Drill (DRILL-4241)和 Hive (HIVE-12971)与 Kudu 集成的工作也在进行中。解耦存储、SQL 和处理引擎是开源社区的常见做法。

Impala-Kudu 的整合非常成功,但仍有工作要做。虽然它在性能和可伸缩性方面与传统的数据仓库平台相当或者超过了传统的数据仓库平台,但是 Impala-Kudu 缺乏大多数传统数据仓库平台中的一些企业特性。我们将在本章后面讨论其中的一些限制。

主关键字

每个 Kudu 表都需要有一个主键。Kudu 的主键是作为聚集索引实现的。对于聚集索引,行以与索引相同的顺序物理存储在 tablet 中。还要注意,Kudu 没有自动递增特性,所以在向 Kudu 表中插入行时,必须包含唯一的主键值。如果没有主键值,可以使用 Impala 内置的 uuid()函数或另一种方法生成唯一值。

数据类型

和其他关系数据库一样,Kudu 支持各种数据类型(表 2-1 )。

表 2-1

List of Data Types, with Available and Default Encoding

| 数据类型 | 编码 | 默认 | | :-- | :-- | :-- | | 布尔 | 普通,游程长度 | 运行长度 | | 8 位有符号整数 | 普通、位洗牌、游程长度 | 比特洗牌 | | 16 位有符号整数 | 普通、位洗牌、游程长度 | 比特洗牌 | | 32 位有符号整数 | 普通、位洗牌、游程长度 | 比特洗牌 | | 64 位有符号整数 | 普通、位洗牌、游程长度 | 比特洗牌 | | unixtime_micros(自 Unix 纪元以来的 64 位微秒) | 普通、位洗牌、游程长度 | 比特洗牌 | | 单精度(32 位)IEEE-754 浮点数 | 普通,位图 | 比特洗牌 | | 双精度(64 位)IEEE-754 浮点数 | 普通,位图 | 比特洗牌 | | UTF-8 编码字符串(未压缩时最大 64KB) | 普通,前缀,字典 | 词典 | | 二进制(最多 64KB 未压缩) | 普通,前缀,字典 | 词典 |

您可能注意到 Kudu 目前不支持 decimal 数据类型。这是 Kudu 的一个关键限制。float 和 double 数据类型仅存储非常接近的值,而不是 IEEE 754 规范中定义的精确值。 xv 由于这种行为,float 和 double 都不适合存储财务数据。在撰写本文时,对十进制数据类型的支持仍在开发中(Apache Kudu 1.5 / CDH 5.13)。十进制支持在 Kudu 1.7 中到来。更多详情请查看 KUDU-721。有各种解决方法。您可以将财务数据存储为字符串,然后在每次需要读取数据时使用 Impala 将值转换为十进制。因为 Parquet 支持小数,所以另一个解决方法是对事实表使用 Parquet,对维度表使用 Kudu。

如表 2-1 所示,根据列的类型,Kudu 列可以使用不同的编码类型。支持的编码类型包括普通、位混洗、游程、字典和前缀。默认情况下,Kudu 列是未压缩的。Kudu 支持使用 Snappy、zlib 或 LZ4 压缩编解码器进行列压缩。有关 Kudu 编码和压缩支持的更多细节,请参考 Kudu 的文档。

Note

在 Kudu 的早期版本中,日期和时间被表示为 BIGINT。从 Impala 2.9/CDH 5.12 开始,可以在 Kudu 表中使用时间戳数据类型。然而,有几件事要记住。Kudu 使用 64 位值表示日期和时间列,而 Impala 使用 96 位值表示日期和时间。存储在 Kudu 中时,Impala 生成的纳秒值四舍五入。在读写时间戳列时,Kudu 的 64 位表示和 Impala 的 96 位表示之间存在转换开销。有两种解决方法:使用 Kudu 客户端 API 或 Spark 来插入数据,或者继续使用 BIGINT 来表示日期和时间。XVI

分割

表分区是增强 Kudu 表的性能、可用性和可管理性的常用方法。分区允许将表细分成更小的部分,即片。分区使 Kudu 能够以更精细的粒度访问表,从而利用分区修剪。所有 Kudu 表都需要进行表分区,表分区对应用程序是完全透明的。Kudu 支持散列、范围、复合散列-范围和散列-散列分区。下面是 Kudu 中分区的几个例子。

哈希分区

有时,为了避免 IO 瓶颈,需要在分区之间随机均匀地分布数据。使用哈希分区,数据根据应用于分区键的哈希函数放入分区。并不是说不允许在散列分区表上添加分区。如果希望添加更多的分区,就必须重建整个散列分区表。

CREATE TABLE myTable (
 id BIGINT NOT NULL,
 name STRING,
 PRIMARY KEY(id)
)
PARTITION BY HASH PARTITIONS 4
STORED AS KUDU;

范围划分

范围分区根据每个分区的分区键值的预定义范围将数据存储在分区中。范围分区允许向表中添加新的分区,从而增强了分区的可管理性。它还通过分区修剪提高了读取操作的性能。一个缺点是:如果按分区键顺序插入数据,范围分区会导致热点。

CREATE TABLE myTable (
  year INT,
  deviceid INT,
  totalamt INT,
  PRIMARY KEY (deviceid, year)
)
PARTITION BY RANGE (year) (
  PARTITION VALUE = 2016,
  PARTITION VALUE = 2017,
  PARTITION VALUE = 2018
)
STORED AS KUDU;

哈希范围分区

散列范围分区结合了散列和范围分区的优点,同时最大限度地减少了它们的限制。使用哈希分区可确保写入 IO 均匀分布在平板电脑服务器上,而使用范围分区可确保可以添加新的平板电脑来适应未来的增长。

CREATE TABLE myTable (
 id BIGINT NOT NULL,
 sensortimestamp BIGINT NOT NULL,
 sensorid INTEGER,
 temperature INTEGER,
 pressure INTEGER,
 PRIMARY KEY(rowid,sensortimestamp)
)
PARTITION BY HASH (id) PARTITIONS 16,
RANGE (sensortimestamp)
(

PARTITION unix_timestamp('2017-01-01') <= VALUES < unix_timestamp('2018-01-01'),
PARTITION unix_timestamp('2018-01-01') <= VALUES < unix_timestamp('2019-01-01'),
PARTITION unix_timestamp('2019-01-01') <= VALUES < unix_timestamp('2020-01-01')
)
STORED AS KUDU;

我将在第四章中更详细地讨论表分区。

Spark 和酷都

Spark 是 Kudu 理想的数据处理和摄取工具。Spark SQL 和 DataFrame API 使得与 Kudu 的交互变得很容易。我将在第六章更详细地讨论 Spark 和 Kudu 集成。

您可以通过 DataFrame API 将 Spark 与 Kudu 结合使用。您可以使用 spark-shell 或 spark-submit 中的- packages 选项来包含 kudu-spark 依赖项。您还可以从 central.maven.org 手动下载 jar 文件,并将其包含在您的- jars 选项中。如果您在 Scala 2.11 中使用 spark2,请使用 kudu-spark2_2.11 工件。例如:

spark-shell –-packages org.apache.kudu:kudu-spark2_2.11:1.1.0
spark-shell --jars kudu-spark2_2.11-1.1.0.jar

酷都语境

您使用 Kudu 上下文来对 Kudu 表执行 DML 语句。 xvii 例如,如果我们需要将数据插入一个 Kudu 表中:

import org.apache.kudu.spark.kudu._
val kuduContext = new KuduContext("kudumaster01:7051")
case class CustomerData(id: Long, name: String, age: Short)


val data = Array(CustomerData(101,"Lisa Kim",60), CustomerData(102,"Casey Fernandez",45))

val insertRDD = sc.parallelize(data)
val insertDF = sqlContext.createDataFrame(insertRDD)

insertDF.show

+----------+---------------+---+
|customerid|           name|age|
+----------+---------------+---+
|       101|       Lisa Kim| 60|
|       102|Casey Fernandez| 45|
+----------+---------------+---+

将数据帧插入 Kudu 表。我假设该表已经存在。

kuduContext.insertRows(insertDF, "impala::default.customers")

确认数据已成功插入。

val df = sqlContext.read.options(Map("kudu.master" -> "kuducluster:7051","kudu.table" -> "impala::default.customers")).kudu
df.select("id","name","age").show()

+---+---------------+---+
| id|           name|age|
+---+---------------+---+
|102|Casey Fernandez| 45|
|101|       Lisa Kim| 60|
+---+---------------+---+

我将在第六章更详细地讨论 Spark 和 Kudu 集成。

Note

从 Kudu 1.6 开始,Spark 通过利用扫描局部性来提高性能。Spark 将扫描最近的平板电脑副本,而不是扫描领导者,领导者可能在不同的平板电脑服务器中。

Spark 流和 Kudu

在清单 2-1 所示的示例中,我们将使用 Flafka (Flume 和 Kafka)和 Spark Streaming 从 Flume spooldir 源读取数据,将其存储在 Kafka 中,并使用 Spark Streaming 处理数据并将其写入 Kudu。

Spark 2.0 中包含了一个基于 Spark SQL 的新的流处理引擎,称为结构化流。从 Spark 2.2.0 开始,结构化流的实验标签已经被移除。然而,在撰写本文时,Cloudera 仍然不支持结构化流(CDH 5.13)。第七章更详细地描述了 Flafka 和 Spark 流。

import org.apache.kudu.client.CreateTableOptions;
import org.apache.kudu.spark.kudu._
import org.apache.spark._
import org.apache.spark.rdd.NewHadoopRDD
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.flume._
import org.apache.spark.streaming.Seconds
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.util.IntParam
import org.apache.spark.sql.SQLContext

object FlumeStreaming {

   case class MySensorData(tableid: String, deviceid: String, thedate: String, thetime: String, temp: Short, status: String)


    def readSensorData(str: String): MySensorData = {
      val col = str.split(",")

      val thetableid = col(0)
      val thedeviceid = col(1)
      val thedate = col(2)
      val thetime = col(3)
      val thetemp = col(4)
      val thestatus = col(5)

      MySensorData(col(0), col(1), col(2), col(3), col(4).toShort, col(5))
    }

  def main(args: Array[String]) {

    val sparkConf = new SparkConf().setMaster("local[2]").setAppName("FlumeStreaming")
    val sc = new SparkContext(sparkConf)
    val ssc = new StreamingContext(sc, Seconds(1))

       // the arguments are for host name and port number
    val flumeStream = FlumeUtils.createPollingStream(ssc,args(0),args(1).toInt)

    val sensorDStream = flumeStream.map (x => new String(x.event.getBody.array)).map(readSensorData)

    sensorDStream.foreachRDD {rdd =>

             val sqlContext = new SQLContext(sc)
             import sqlContext.implicits._
             val kuduContext = new KuduContext("kudumaster01:7051")

// convert the RDD into a DataFrame and insert it into the Kudu table

             val DataDF = rdd.toDF
             kuduContext.insertRows(DataDF, "impala::default.sensortable")

             DataDF.registerTempTable("currentDF")

             // Update the table based on the thresholds

             val WarningFilteredDF = sqlContext.sql("select * from currentDF where temp > 50 and temp <= 60")
             WarningFilteredDF.registerTempTable("warningtable")
             val UpdatedWarningDF = sqlContext.sql("select tableid,deviceid,thedate,thetime,temp,'WARNING' as status from warningtable")
             kuduContext.updateRows(UpdatedWarningDF, "impala::default.sensortable")

             val CriticalFilteredDF = sqlContext.sql("select * from currentDF where temp > 61")
             CriticalFilteredDF.registerTempTable("criticaltable")
             val UpdatedCriticalDF = sqlContext.sql("select tableid,deviceid,thedate,thetime,temp,'CRITICAL' as status from criticaltable")
             kuduContext.updateRows(UpdatedCriticalDF, "impala::default.sensortable")

     }

    ssc.start()
    ssc.awaitTermination()

  }

}

Listing 2-1Spark Streaming and Kudu

清单 2-2 显示了 flume 配置文件,其中 Kafka 被用作 flume 通道。

agent1.sources  = source1
agent1.channels = channel1
agent1.sinks = spark

agent1.sources.source1.type = spooldir
agent1.sources.source1.spoolDir = /tmp/streaming
agent1.sources.source1.channels = channel1

agent1.channels.channel1.type = org.apache.flume.channel.kafka.KafkaChannel
agent1.channels.channel1.brokerList = kafkabroker01:9092, kafkabroker02:9092, kafkabroker03:9092
agent1.channels.channel1.zookeeperConnect = server03:2181
agent1.channels.channel1.topic = mytopic

agent1.sinks.spark.type = org.apache.spark.streaming.flume.sink.SparkSink
agent1.sinks.spark.hostname = 127.0.0.1
agent1.sinks.spark.port =  9999
agent1.sinks.spark.channel = channel1
agent1.sinks.spark.batchSize=5

Listing 2-2Flume configuration file

编译完包后,将应用程序提交给集群来执行它。

spark-submit \
--class FlumeStreaming \

--jars kudu-spark_2.10-0.10.0.jar \
--master yarn-client \
--driver-memory=512m \
--executor-memory=512m \
--executor-cores 4  \
/mydir/spark/flume_streaming_kudu/target/scala-2.10/test-app_2.10-1.0.jar \ localhost 9999

带 Spark 流和 Kudu 的 Flafka 管道应如图 2-5 所示。

A456459_1_En_2_Fig5_HTML.jpg

图 2-5

A Flafka pipeline with Spark Streaming and Kudu

Kudu C++、Java 和 Python 客户端 API

Kudu 提供了 NoSQL 风格的 Java、C++和 Python 客户端 API。需要 Kudu 提供最佳性能的应用程序应该使用客户端 API。事实上,第七章中讨论的一些数据摄取工具,比如 StreamSets、CDAP 和 Talend,利用客户端 API 将数据摄取到 Kudu 中。通过 API 对 DML 的修改可以立即在 Impala 中进行查询,而不需要执行无效元数据。

Kudu Java 客户端 API

清单 2-3 提供了一个使用 Java 客户端 API 的例子。

import org.apache.kudu.ColumnSchema;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
import org.apache.kudu.client.*;

import java.util.ArrayList;
import java.util.List;

public class JavaKuduClient {

  public static void main(String[] args) {

// Create Kudu client object

KuduClient myKuduClient = new KuduClient.KuduClientBuilder("kudumaster01").build();

// Create the schema

         List<ColumnSchema> myColumns = new ArrayList(3);
      myColumns.add(new ColumnSchema.ColumnSchemaBuilder("rowid", Type.INT32)
          .key(true)
          .build());
      myColumns.add(new ColumnSchema.ColumnSchemaBuilder("customername", Type.STRING)
          .build());
         myColumns.add(new ColumnSchema.ColumnSchemaBuilder("customerage", Type.INT8)
          .build());
      List<String> partKeys = new ArrayList<>();
      partKeys.add("key");

// Create the table based on the schema

      Schema mySchema = new Schema(myColumns);
      client.createTable("CustomersTbl", mySchema, new CreateTableOptions().setRangePartitionColumns(partKeys));

// Open the Kudu table

      KuduTable myTable = myKuduClient.openTable("CustomersTbl");
      KuduSession mySession = myKuduClient.newSession();

// Insert new rows

        Insert myInsert = myTable.newInsert();
        myInsert.getRow().addInt("rowid", 1);
        myInsert.getRow().addString("customername", "Jerry Walsh");
             myInsert.getRow().addInt("customerage", 64)
             mySession.apply(myInsert);

// Update existing rows

        Update myUpdate = myTable.newUpdate();
        myUpdate.getRow().addInt("rowid", 1);
        myUpdate.getRow().addString("customername", "Jerome Walsh");
             myUpdate.getRow().addInt("customerage", 65)
             mySession.apply(myUpdate);

// Upsert rows

        Upsert myUpsert = myTable.newUpsert();
        myUpsert.getRow().addInt("rowid", 2);
        myUpsert.getRow().addString("customername", "Tim Stein");
             myUpsert.getRow().addInt("customerage", 49)
             myUpsert.apply(myUpdate);

// Delete row
             Delete myDelete = myTable.newDelete()
             myDelete.getrow().addString("rowid", 1);
             mySession.apply(myDelete)

// Display rows

      List<String> myColumns = new ArrayList<String>();
      myColumns.add("rowid");
         myColumns.add("customername");
         myColumns.add("customerage");

      KuduScanner myScanner = myClient.newScannerBuilder(myTable)
          .setProjectedColumnNames(myColumns)
          .build();

      while (myScanner.hasMoreRows()) {
        RowResultIterator myResultIterator = myScanner.nextRows();
        while (myResultIterator.hasNext()) {
          RowResult myRow = myResultIterator.next();
          System.out.println(myRow.getInt("rowid"));
               System.out.println(myRow.getString("customername"));
               System.out.println(myRow.getString("customerage"));
        }
       }  

// Delete table

        myKuduClient.deleteTable(myTable);

// Close the connection

        myKuduClient.shutdown();

  }
}

Listing 2-3Sample Java code using the Kudu client API

Maven 工件

pom.xml 文件中需要以下内容。

<dependency>
  <groupId>org.apache.kudu</groupId>
  <artifactId>kudu-client</artifactId>
  <version>1.1.0</version>
</dependency>

Kudu Python 客户端 API

Python 客户端 API 提供了一种与 Kudu 交互的简单方法。Python API 仍处于试验阶段,可能会随时更改。参见清单 2-4 中的示例。

import kudu
from kudu.client import Partitioning
from datetime import datetime

# Connect to Kudu
myclient = kudu.connect(host='kudumaster01', port=7051)

# Define the columns
mybuilder = kudu.schema_builder()
mybuilder.add_column('rowid').type(kudu.int64).nullable(False).primary_key()
mybuilder.add_column('customername', type_=kudu.string, nullable=False)
mybuilder.add_column('customerage', type_=kudu.int8, nullable=False)
myschema = mybuilder.build()

# Define partitioning method
mypartitioning = Partitioning().add_hash_partitions(column_names=['rowid'], num_buckets=24)

# Create new table
myclient.create_table('customers', myschema, mypartitioning)

# Open a table
mytable = myclient.table('customers')

# Create a new session
mysession = client.new_session()

# Insert a row
myinsert = mytable.new_insert({'rowid': 1, 'customername': "Jason Weinstein", 'customerage': 62})
mysession.apply(myinsert)

# Upsert a row
myupsert = mytable.new_upsert({'rowid': 2, 'customername': "Frank Nunez", 'customerage': 47})
session.apply(myupsert)

# Updating a row
myupdate = table.new_update({'rowid': 1, 'customername': "Jason Dean Weinstein"})
session.apply(myupdate)

# Delete a row
mydelete = table.new_delete({'rowid': 1})
session.apply(mydelete`)

# Flush the session
mysession.flush()

# Create a scanner with a predicate
myscanner = mytable.scanner()
myscanner.add_predicate(table['customerage'] < 50)

# Read the data. Note that this method doesn't scale well for large table scans
myresult = myscanner.open().read_all_tuples()

Listing 2-4Sample Python code using the Kudu client API

Kudu C++客户端 API

Kudu 还提供了一个 C++客户端 API。参见清单 2-5 中的示例。

#include <ctime>
#include <iostream>
#include <sstream>

#include "kudu/client/callbacks.h"
#include "kudu/client/client.h"
#include "kudu/client/row_result.h"
#include "kudu/client/stubs.h"
#include "kudu/client/value.h"
#include "kudu/client/write_op.h"
#include "kudu/common/partial_row.h"
#include "kudu/util/monotime.h"

using kudu::client::KuduClient;
using kudu::client::KuduClientBuilder;
using kudu::client::KuduColumnSchema;
using kudu::client::KuduError;
using kudu::client::KuduInsert;
using kudu::client::KuduPredicate;
using kudu::client::KuduRowResult;
using kudu::client::KuduScanner;
using kudu::client::KuduSchema;
using kudu::client::KuduSchemaBuilder;
using kudu::client::KuduSession;
using kudu::client::KuduStatusFunctionCallback;
using kudu::client::KuduTable;
using kudu::client::KuduTableAlterer;
using kudu::client::KuduTableCreator;
using kudu::client::KuduValue;
using kudu::client::sp::shared_ptr;
using kudu::KuduPartialRow;
using kudu::MonoDelta;
using kudu::Status;

using std::string;
using std::vector;

int main(int argc, char* argv[]) {

  // Enable verbose debugging for the client library.
  // Set parameter to 0 to disable
  kudu::client::SetVerboseLogLevel(2);

  // Create and connect a client.
  shared_ptr<KuduClient> client;
  KUDU_CHECK_OK(KuduClientBuilder().add_master_server_addr("kudumaster01:7051").Build(&client));
  KUDU_LOG(INFO) << "Client connection created.";

  // Create a schema.
  // Available data types:  INT8 = 0, INT16 = 1, INT32 = 2,
  // INT64 = 3, STRING = 4, BOOL = 5, FLOAT = 6, DOUBLE = 7,
  // BINARY = 8, UNIXTIME_MICROS = 9, TIMESTAMP = UNIXTIME_MICROS
  KuduSchema mytable_schema;
  KuduSchemaBuilder mytable_builder;
  categories_builder.AddColumn("rowid")->Type(KuduColumnSchema::INT32)->NotNull();
  categories_builder.AddColumn("name")->Type(KuduColumnSchema::STRING)->NotNull();
  categories_builder.AddColumn("age")->Type(KuduColumnSchema::INT8)->NotNull();
  categories_builder.AddColumn("salary")->Type(KuduColumnSchema::DOUBLE)->NotNull();
  categories_builder.SetPrimaryKey({"rowid"});
  KUDU_CHECK_OK(categories_builder.Build(&mytable_schema));
  KUDU_LOG(INFO) << "Created a schema for mytable";

  // Delete table if it exists
  bool exists;
  KUDU_CHECK_OK(client->TableExists("mytable", &exists));
  if (exists) {
    KUDU_CHECK_OK(client->DeleteTable("mytable"));
       KUDU_LOG(INFO) << "Deleting table if it exists.";
  }

   // Generate the split keys for the table.
  vector<const KuduPartialRow*> splits;
  int32_t num_tablets = 20
  int32_t increment = 1000 / num_tablets;
  for (int32_t i = 1; i < num_tablets; i++) {
    KuduPartialRow* row = mytable_schema.NewRow();
    KUDU_CHECK_OK(row->SetInt32(0, i * increment));
    splits.push_back(row);
  }

  vector<string> column_names;
  column_names.push_back("rowid");

  // Create the table.
  KuduTableCreator* table_creator = client->NewTableCreator();

  KUDU_CHECK_OK(table_creator->table_name("mytable")
        .schema(&mytable_schema)
      .set_range_partition_columns(column_names)
      .split_rows(splits)
      .Create());

  // Confirm if the table was successfully created
  bool created;
  KUDU_CHECK_OK(client->TableExists("mytable", &created));
  created ? KUDU_LOG(INFO) << "Created table mytable." :
            KUDU_LOG(INFO) << "Failed to create table mytable.";

  // Insert two rows into the table.
  shared_ptr<KuduTable> table;
  client->OpenTable("mytable", &table);

  KuduInsert* my_insert = table->NewInsert();
  KuduPartialRow* row = categories_insert->mutable_row();
  KUDU_CHECK_OK(row->SetInt32("rowid", 100));
  KUDU_CHECK_OK(row->SetStringCopy("name", "Fred Smith"));
  KUDU_CHECK_OK(row->SetInt8("age", 56));
  KUDU_CHECK_OK(row->SetDouble("salary", 110000));
  KUDU_CHECK_OK(session->Apply(my_insert));

  KuduInsert* my_insert = table->NewInsert();
  KuduPartialRow* row = categories_insert->mutable_row();
  KUDU_CHECK_OK(row->SetInt32("rowid", 101));
  KUDU_CHECK_OK(row->SetStringCopy("name", "Linda Stern"));
  KUDU_CHECK_OK(row->SetInt8("age", 29));
  KUDU_CHECK_OK(row->SetDouble("salary", 75000));
  KUDU_CHECK_OK(session->Apply(my_insert));

  KUDU_CHECK_OK(session->Flush());
  KUDU_LOG(INFO) << "Inserted two rows into mytable";

  // Scan one row based on a predicate

  KuduScanner scanner(table.get());

  // Add a predicate: WHERE name = "Linda Stern"
  KuduPredicate* pred = table->NewComparisonPredicate(
      "name", KuduPredicate::EQUAL, KuduValue::FromString("Linda Stern"));
  KUDU_RETURN_NOT_OK(scanner.AddConjunctPredicate(pred));

  KUDU_RETURN_NOT_OK(scanner.Open());
  vector<KuduRowResult> results;

  while (scanner.HasMoreRows()) {
    KUDU_RETURN_NOT_OK(scanner.NextBatch(&results));
    for (vector<KuduRowResult>::iterator iter = results.begin();
        iter != results.end();
        iter++) {
      const KuduRowResult& result = *iter;
      string_t myname;
      KUDU_RETURN_NOT_OK(result.GetString("name", &myname));
      KUDU_LOG(INFO) << "Scanned some rows out of a table" << myname;
    }
    results.clear();
  }

  // Delete the table.
  KUDU_CHECK_OK(client->DeleteTable("mytable"));
  KUDU_LOG(INFO) << "Deleted mytable.";

}

Listing 2-5Sample C++ code using the Kudu client API

更多例子 xviii 可以在 Kudu 官网 xix 和 github 资源库找到。 xx 在线提供的示例代码由 Kudu 开发团队提供,作为本章的参考。

备份和恢复

Kudu 没有备份和恢复工具。但是,有几种方法可以使用 Impala、Spark 和第三方工具(如 StreamSets 和 Talend)来备份(和恢复)Kudu 表。

Note

HDFS 快照不能用于备份 Kudu 表,因为 Kudu 数据不在 HDFS。XXI

经由 CTAS 的备份

备份 Kudu 表的最简单方法是使用 CREATE TABLE AS (CTAS)。您基本上只是在 HDFS 创建 Kudu 表的另一个副本,最好是 Parquet 格式(或其他压缩格式),这样您就可以将文件复制到远程位置,比如另一个集群或 S3。

CREATE TABLE AS DimCustomer_copy AS SELECT * FROM DimCustomer;

+-----------------------+
| summary               |
+-----------------------+
| Inserted 18484 row(s) |
+-----------------------+

您可以先创建表格,以便在需要时自定义表格选项,然后使用“插入”从 Kudu 表格中插入数据。

CREATE TABLE DimCustomer_Parquet (
ID STRING,
CustomerKey BIGINT,
FirstName STRING,
LastName STRING,
BirthDate STRING,
YearlyIncome FLOAT,
TotalChildren INT,
EnglishEducation STRING,
EnglishOccupation STRING,
HouseOwnerFlag INT,
NumberCarsOwned INT
)
STORED AS PARQUET;

set COMPRESSION_CODEC=gzip;

insert into DimCustomer_Parquet Select * from DimCustomer;
Modified 18484 row(s) in 4.52s

Note

Kudu 表不支持 CREATE TABLE LIKE 语法。如果您尝试使用该语法创建一个表,您将会收到一条类似如下的错误消息:“错误:分析异常:不支持使用 CREATE TABLE LIKE 克隆 Kudu 表。”

用 HDFS 命令检查文件。

hadoop fs -du -h /user/hive/warehouse/dimcustomer_parquet

636.8 K  1.9 M  /user/hive/warehouse/dimcustomer_parquet/f948582ab9f8dfbb-5e57d0ca00000000_1052047868_data.0.parq

将拼花文件复制到另一个集群或 S3

现在,您可以使用 distcp 将 Parquet 文件复制到另一个集群中。

hadoop distctp -pb hftp://kuducluster:50070/user/hive/warehouse/dimcustomer_parquet hdfs://kuducluster2/backup_files

你也可以把文件复制到 S3。

hadoop distcp –pb -Dfs.s3a.access.key=s3-access-key -Dfs.s3a.secret.key=s3-secret-key hdfs://user/hive/warehouse/dimcustomer_parquet s3a://myWarehouseBucket/backup_files

为了保持一致性,请注意,我使用了-pb 选项来保证保留 Parquet 数据文件的特殊块大小。XXII

Note

Cloudera 有一个名为 Cloudera 企业备份和灾难恢复(BDR)的集群复制功能。BDR 提供了一个易于使用的图形用户界面,允许您安排从一个集群到另一个集群的复制。BDR 不支持 Kudu,但是您可以复制位于 HDFS 的目标拼花文件。XXIII

通过 impala-shell 将结果导出到本地目录、NFS 或 SAN 卷

Impala-shell 可以生成带分隔符的文件,然后您可以将其压缩并复制到远程服务器或 NFS/SAN 卷。请注意,这种方法不适用于大型表。

impala-shell -q "SELECT * FROM DimCustomer" --delimited --output_delimiter=, --output_file /backup_nfs/dimcustomer_bak.csv

使用 Kudu 客户端 API 导出结果

Kudu 客户端 API 也可以用来导出数据。参见清单 2-6 中的示例。

import org.apache.kudu.ColumnSchema;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
import org.apache.kudu.client.*;

import java.util.ArrayList;
import java.util.List;

public class TableBackup {

  public static void main(String[] args) {

// Create Kudu client object

KuduClient myKuduClient = new KuduClient.KuduClientBuilder("kudumaster").build();

KuduTable myTable = myKuduClient.openTable("CustomersTbl");
KuduSession mySession = myKuduClient.newSession();

// Display rows

      List<String> myColumns = new ArrayList<String>();
      myColumns.add("rowid");
        myColumns.add("customername");
        myColumns.add("customerage");

      KuduScanner myScanner = myKuduClient.newScannerBuilder(myTable)
          .setProjectedColumnNames(myColumns)
          .build();

      while (myScanner.hasMoreRows()) {
        RowResultIterator myResultIterator = myScanner.nextRows();
        while (myResultIterator.hasNext()) {
          RowResult myRow = myResultIterator.next();
          System.out.println(myRow.getInt("rowid"));
              System.out.println(myRow.getString("customername"));
              System.out.println(myRow.getString("customerage"));
        }
       }
 }
}

Listing 2-6Sample Java code using the Kudu client API to export data

编译 java 代码并从命令行运行它。将结果重定向到文件。这种方法适用于小型数据集。

java TableBackup >> /backup_nfs/mybackup.txt

使用 Spark 导出结果

您也可以使用 Spark 备份数据。这更适合大型表,因为您可以控制并行性、执行器数量、执行器核心和执行器内存。

首先创建一个数据帧。

val df = sqlContext.read.options(Map("kudu.master" -> "localhost:7051","kudu.table" -> "impala::default.DimCustomer")).kudu

以 CSV 格式保存数据。

df.coalesce(1).write.format("com.databricks.spark.csv").option("header", "true").save("/backup/dimcustomer_bak")

或者你可以把它保存为拼花地板。

df.coalesce(1).write.mode("append").parquet("/backup/dimcustomer_p_bak”)

使用合并来限制写入 HDFS 时生成的文件数量可能会导致性能问题。我将在第五章详细讨论联合。

使用 Spark 和 Kudu 数据源 API 进行复制

我们可以使用 Spark 将数据从一个 Kudu 集群复制到另一个集群。

启动 Spark 壳。

spark-shell --packages org.apache.kudu:kudu-spark_2.10:1.1.0 --driver-class-path mysql-connector-java-5.1.40-bin.jar --jars mysql-connector-java-5.1.40-bin.jar


连接到 Kudu master 并检查 users 表中的数据。我们将把这个 Kudu 表与另一个集群中的另一个 Kudu 表同步。

import org.apache.kudu.spark.kudu._

val kuduDF = sqlContext.read.options(Map("kudu.master" -> "kuducluster:7051","kudu.table" -> "impala::default.users")).kudu

kuduDF.select("userid","name","city","state","zip","age").sort($"userid".asc).show()

+------+---------------+---------------+-----+-----+---+
|userid|           name|           city|state|  zip|age|
+------+---------------+---------------+-----+-----+---+
|   100|   Wendell Ryan|      San Diego|   CA|92102| 24|
|   101|Alicia Thompson|       Berkeley|   CA|94705| 52|
|   102|Felipe Drummond|      Palo Alto|   CA|94301| 33|
|   103|  Teresa Levine|   Walnut Creek|   CA|94507| 47|
|   200|  Jonathan West|         Frisco|   TX|75034| 35|
|   201| Andrea Foreman|         Dallas|   TX|75001| 28|
|   202|   Kirsten Jung|          Plano|   TX|75025| 69|
|   203| Jessica Nguyen|          Allen|   TX|75002| 52|
|   300|   Fred Stevens|       Torrance|   CA|90503| 23|
|   301|    Nancy Gibbs|       Valencia|   CA|91354| 49|
|   302|     Randy Park|Manhattan Beach|   CA|90267| 21|
|   303|  Victoria Loma|  Rolling Hills|   CA|90274| 75|
+------+---------------+---------------+-----+-----+---+

让我们继续将数据插入到另一个 Kudu 集群的表中。目标表需要存在于另一个 Kudu 簇中。

val kuduContext = new KuduContext("kuducluster2:7051")

kuduContext.insertRows(kuduDF, "impala::default.users2")

验证目标表中的数据。

impala-shell

select * from users2 order by userid;
+------+---------------+---------------+-----+-----+---+
|userid|           name|           city|state|  zip|age|
+------+---------------+---------------+-----+-----+---+
|   100|   Wendell Ryan|      San Diego|   CA|92102| 24|
|   101|Alicia Thompson|       Berkeley|   CA|94705| 52|
|   102|Felipe Drummond|      Palo Alto|   CA|94301| 33|
|   103|  Teresa Levine|   Walnut Creek|   CA|94507| 47|
|   200|  Jonathan West|         Frisco|   TX|75034| 35|
|   201| Andrea Foreman|         Dallas|   TX|75001| 28|
|   202|   Kirsten Jung|          Plano|   TX|75025| 69|
|   203| Jessica Nguyen|          Allen|   TX|75002| 52|
|   300|   Fred Stevens|       Torrance|   CA|90503| 23|
|   301|    Nancy Gibbs|       Valencia|   CA|91354| 49|
|   302|     Randy Park|Manhattan Beach|   CA|90267| 21|
|   303|  Victoria Loma|  Rolling Hills|   CA|90274| 75|
+------+---------------+---------------+-----+-----+---+

行已成功复制。

使用流集进行实时复制

StreamSets 是一个强大的实时和批量接收工具,主要用于实时流和物联网(IoT)用例。您可以使用流集实时或接近实时地将数据从 Oracle、MySQL、SQL Server 或 Kudu 等 JDBC 源复制到另一个目的地。StreamSets 提供了两个源来促进通过 JDBC 连接的复制:JDBC 查询消费者和 JDBC 多表消费者。JDBC 查询使用者源使用用户定义的 SQL 查询从表中读取数据。参见图 2-6 。

A456459_1_En_2_Fig6_HTML.jpg

图 2-6

StreamSets and Kudu

JDBC 多表消费者源读取同一个数据库中的多个表。JDBC 多表消费者源适用于数据库复制。StreamSets 包括一个 Kudu 目的地;或者,JDBC 生成器是一个(较慢的)选项,可用于将数据复制到其他关系数据库。第七章更详细地介绍了流集。

使用 Talend、Pentaho 和 CDAP 等 ETL 工具复制数据

Talend(图 2-9 )和木桶数据平台(图 2-7 )为 Kudu 提供原生支持。两者都提供 Kudu 源和汇,并可用于将数据从一个 Kudu 集群复制到一个或多个 Kudu 集群;另一个目的地,如 S3;或 RDBMS,如 SQL Server、MySQL 或 Oracle。其他工具如 Pentaho PDI(图 2-8 )没有本地 Kudu 支持。然而,它可以通过 Impala 将数据传输到 Kudu,尽管速度较慢。第七章详细介绍了 StreamSets、Talend、Pentaho 和 CDAP 中的批处理和实时摄取工具。

A456459_1_En_2_Fig9_HTML.jpg

图 2-9

ETL with Talend and Kudu

A456459_1_En_2_Fig8_HTML.jpg

图 2-8

ETL with Pentaho and Kudu

A456459_1_En_2_Fig7_HTML.jpg

图 2-7

ETL with CDAP and Kudu Note

Talend Kudu 组件由第三方公司 One point Ltd .提供。这些组件可从 Talend Exchange 免费下载,网址为—https://exchange.talend.com/。在将 Talend 与 Kudu 配合使用之前,需要安装 Kudu 输出和输入组件。

蟒蛇和黑斑羚

使用 Python 不是备份大型 Kudu 表的最快或最具可伸缩性的方法,但对于中小型数据集来说应该足够了。下面是从 Python 中访问 Kudu 表的最常用方法列表。

黑斑羚

Cloudera 构建了一个名为 Impyla 的 Python 包。 xxiv Impyla 只是使用标准 ODBC/JDBC 与 Impala 通信。Impyla 的一个很好的特性是它能够轻松地将查询结果转换成 pandas 数据帧(不要与 Spark 数据帧混淆)。这里有一个例子。

>>> from impala.util import as_pandas
>>> cur.execute('SELECT id, name, salary FROM employees')
>>> df = as_pandas(cur)
>>> type(df)

<class 'pandas.core.frame.DataFrame'>

>>> df
              id     name                      salary
0            001     James Chan                100000
1            002     Roger Lim                  75000
2            003     Dan Tanner            v    65000
3            004     Lilian Russo               90000
4            005     Edith Sarkisian           110000

pyodbc

pyodbc 是一个流行的开源 Python 包,可以用来通过 ODBC/JDBC 访问数据库。下面是一个如何使用 pyodbc 的例子。要了解更多关于 pyodbc 的信息,请访问它在 github.com/mkleehammer/pyodbc.的 github 页面

import pyodbc

myconnection_str = '''Driver=/mypath/libclouderaimpalaodbc.dylib;HOST=localhost;PORT=21050'''

myconnection = pyodbc.connect("myconnection_str")
cursor = myconnection.cursor()
cursor.execute("select id, name, salary  from employees")

sqllcemy(SQL 语法)

SQLAlchemy 是一个用于 Python 的 SQL 工具包,具有对象关系映射器(ORM)的特性。要了解更多关于 SQLAlchemy 的信息,请访问它在 sqlalchemy.org 的网站。这里有一个关于如何使用 SQLAlchemy 连接到 Impala 的例子。

import sqlalchemy
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

myconnection_str = '''Driver=/mypath/libclouderaimpalaodbc.dylib;HOST=localhost;PORT=21050'''

myconnection = create_engine("myconnection_str")
session = sessionmaker(bind=db)
user = session.query(Employees).filter_by(salary > '65000').first()

高可用性选项

除了 Kudu 的默认平板电脑复制因子为 3(可以增加到 5 或 7),Kudu 没有内置的高可用性工具或功能。幸运的是,您可以使用 Cloudera Enterprise 中的内置组件和 StreamSets 等第三方工具来为 Kudu 提供高可用性功能。通过拥有两个或更多 Kudu 集群,高可用性可以保护您免受整个站点故障的影响。集群可以位于地理上分散的数据中心或云提供商。 xxvi 请注意,复制的数据量可能会影响性能和成本。拥有主动-主动环境的另一个好处是能够将两个群集用于不同的使用情形。例如,第二个集群可用于即席查询、构建机器学习模型和其他数据科学工作负载,而第一个集群用于实时分析或具有明确定义的 SLA 的用例。让我们探索 Kudu 的几个高可用性选项。

使用 Kafka 和 Spark 流的主动-主动双重摄取

在此选项中,所有数据都发布到 Kafka 集群。Spark 流用于从 Kafka 主题中读取数据。使用 Spark Streaming,您可以选择在将数据写入 Kudu 之前执行数据转换和清理。图 2-10 显示了两个 Kudu 目的地,但是根据您的 HA 要求,您可以有更多目的地。

A456459_1_En_2_Fig10_HTML.jpg

图 2-10

Active-Active Dual Ingest with Kafka and Spark Streaming

使用 MirrorMaker 进行主动-主动 Kafka 复制

另一个高可用性选项是使用 MirrorMaker 复制 Kafka。如图 2-11 所示,使用 MirrorMaker 将摄取到源 Kafka 集群的数据复制到目标 Kafka 集群。从那里,数据被 Spark 流读取并写入 Kudu 目的地,类似于图 2-10 。如果您的目标是双重摄取,那么使用 MirrorMaker 来复制您的 Kudu 集群可能是多余的。但是,它提供了更好的数据保护,因为数据是在两个或更多 Kafka 和 Kudu 集群上复制的。

A456459_1_En_2_Fig11_HTML.jpg

图 2-11

Active-Active Kafka Replication with MirrorMaker

使用 Kafka 和 StreamSets 的主动-主动双重摄取

该选项与图 2-10 非常相似,但使用流集代替 Spark 流。StreamSets 比 Spark Streaming 更易于使用和管理,并提供内置的监控、警报和异常处理。它还提供了一个事件框架,使得基于事件开始任务变得容易。在大多数项目中,我通常推荐 StreamSets 而不是 Spark Streaming(图 2-12 )。

A456459_1_En_2_Fig12_HTML.jpg

图 2-12

Active-Active Dual Ingest with Kafka and StreamSets

带流集的主动-主动双重接收

从技术上来说,你不需要卡夫卡来进行主动-主动双重摄取。StreamSets 允许您配置多个目的地。使用流选择器处理器,它甚至可以根据某些条件将数据路由到不同的目的地(图 2-13 )。使用 Kafka 提供了额外的高可用性和可伸缩性,在大多数情况下仍然推荐使用。我会在第七章中更详细地介绍流集。

A456459_1_En_2_Fig13_HTML.jpg

图 2-13

Active-Active Dual Ingest with Kafka and StreamSets

管理和监控

就像其他数据管理平台一样,Kudu 提供了帮助系统管理和监控的工具。

Cloudera 经理 Kudu 服务

Cloudera Manager 是 Cloudera 的集群管理工具,为管理 Cloudera 企业集群提供了单一平台。使用 Cloudera Manager,您可以执行常见的管理任务,例如启动和停止 Kudu 服务、更新配置、监控性能和检查日志。

Kudu Master Web UI

Kudu Masters 提供了一个 web 界面(在端口 8051 上可用),该界面提供了有关集群的信息。它显示有关 tablet 服务器、心跳、主机名、表和模式的信息。您还可以查看可用日志、内存使用和资源消耗的详细信息。

Kudu 平板电脑服务器 Web 用户界面

每个 table server 还提供了一个 web 界面(在端口 8050 上可用),该界面提供了关于 table t server 集群的信息。它显示有关平板电脑服务器上托管的每台平板电脑的更多详细信息、调试和状态信息、资源消耗以及可用日志。

Kudu 度量

Kudu 提供了几个指标,您可以使用它们来监控集群并对集群进行故障排除。您可以通过执行$ Kudu-tserver-dump _ metrics _ JSON 或 kudu-master - dump_metrics_json 来获得可用 Kudu 指标的列表。一旦知道了想要检查的指标,就可以通过 HTTP 访问/metrics end-point 来收集实际值。这些指标也由 Cloudera Manager 收集和汇总。例如:

curl -s 'http://tabletserver01:8050/metrics?include_schema=1&metrics=read_bytes_rate'

Kudu 命令行工具

除了主服务器和平板服务器提供的 Cloudera Manager 和可访问的 web 用户界面,Kudu 还包括用于常见系统管理任务的命令行工具。

验证集群运行状况

ksck:检查集群元数据是否一致,主服务器和平板服务器是否正在运行。默认情况下,Ksck 检查所有表和 tables,但是您可以使用 tables 标志指定要检查的表的列表,或者使用 tables 标志指定 tables 服务器的列表。使用校验和扫描和校验和快照检查数据中的不一致。

用法:

kudu cluster ksck --checksum_scan [--tables <tables>] <master_address>

文件系统

检查:检查 Kudu 文件系统的不一致性

用法:

kudu fs check [-fs_wal_dir=<dir>] [-fs_data_dirs=<dirs>] [-repair]

list:显示本地文件系统中的 tablet 副本列表

用法:

kudu local_replica list [-fs_wal_dir=<dir>] [-fs_data_dirs=<dirs>] [-list_detail]

data_size:总结给定本地副本的数据大小/空间使用情况。

用法:

kudu local_replica data_size <tablet_id_pattern> [-fs_wal_dir=<dir>] [-fs_data_dirs=<dirs>] [-format=<format>]

掌握

状态:获得一个 Kudu 大师的状态

用法:

kudu master status <master_address>

时间戳:获取 Kudu 主机的当前时间戳

用法:

kudu master timestamp <master_address>

列表:列出 Kudu 集群中的主节点

用法:

kudu master list <master_addresses> [-columns=<columns>] [-format=<format>] [-timeout_ms=<ms>]

衡量 Kudu 集群的性能

loadgen:运行负载生成,随后进行可选扫描

loadgen 以集群可以执行的速度将自动生成的随机数据插入到现有的或自动创建的表中。Loadgen 还可以检查实际插入的行数是否与原始行数匹配。

用法:

kudu perf loadgen <master_addresses> [-buffer_flush_watermark_pct=<pct>] [-buffer_size_bytes=<bytes>] [-buffers_num=<num>] [-error_buffer_size_bytes=<bytes>] [-flush_per_n_rows=<rows>] [-keep_auto_table] [-num_rows_per_thread=<thread>] [-num_threads=<threads>] [-run_scan] [-seq_start=<start>] [-show_first_n_errors=<errors>] [-string_fixed=<fixed>] [-string_len=<len>] [-table_name=<name>] [-table_num_buckets=<buckets>] [-table_num_replicas=<replicas>] [-use_random]

桌子

删除:删除表格

用法:

kudu table delete <master_addresses> <table_name>

列表:列出所有表格

用法:

kudu table list <master_addresses> [-list_tablets]

药片

leader_step_down:强制平板电脑的 leader 副本下台

用法:

kudu tablet leader_step_down <master_addresses> <tablet_id>

add_replica:向平板电脑的 Raft 配置添加新副本

用法:

kudu tablet change_config add_replica <master_addresses> <tablet_id> <ts_uuid> <replica_type>

move_replica:将一个 tablet 副本从一个 tablet 服务器移动到另一个

副本移动工具通过向新服务器添加副本,然后从旧服务器删除副本,有效地将副本从一台平板服务器移动到另一台。

用法:

kudu tablet change_config move_replica <master_addresses> <tablet_id> <from_ts_uuid> <to_ts_uuid>

平板电脑服务器

状态:获取 Kudu 平板电脑服务器的状态

用法:

kudu tserver status <tserver_address>

时间戳:获取 Kudu Tablet 服务器的当前时间戳

用法:

kudu tserver timestamp <tserver_address>

列表:列出 Kudu 集群中的平板电脑服务器

用法:

kudu tserver list <master_addresses> [-columns=<columns>] [-format=<format>] [-timeout_ms=<ms>]

查阅 Kudu 的在线命令行参考指南 xxvii 以获得 Kudu 命令行工具的完整列表和描述。

已知问题和限制

Kudu 有几个问题和局限性。根据您的使用案例,它们可以被认为是小问题或大问题。他们中的大多数都有解决方法,但有些没有。在构建新的应用程序或将工作负载迁移到 Kudu 时,您必须意识到这些限制。我在下面列出了一些主要的。Kudu 提交者和贡献者正在努力修正这些限制。

  • Kudu 不支持 DECIMAL、CHAR、VARCHAR、DATE 和复杂类型,如 ARRAY、MAP 和 STRUCT。
  • Kudu 表最多可以有 300 列。
  • Kudu 没有二级索引。
  • Kudu 没有外键。
  • 不支持多行多表交易。
  • Kudu 没有内置的备份和恢复以及高可用性功能。
  • Kudu 不支持行、列和表级别的基于角色的访问控制。
  • Kudu 建议平板服务器的最大数量为 100 台。
  • Kudu 建议最多 3 个大师。
  • Kudu 建议每台平板电脑服务器在复制后和压缩后的最大存储数据量为 8TB。
  • Kudu 建议复制后每台平板电脑服务器的最大平板电脑数量为 2000 台。
  • Kudu 建议在复制后创建表时,每个 tablet 服务器的最大 table t 数量为 60。
  • Kudu 不支持机架感知、多数据中心和滚动重启。

有关 Kudu 限制的更完整和最新列表,请参考 Cloudera 的在线文档。XXVIII

安全

Kudu 支持 Kerberos 进行强认证。Kudu 客户端和服务器之间的通信使用 TLS 加密。Kudu 不支持表、行或列级访问控制。相反,它使用白名单样式的访问控制列表来实现粗粒度授权。两种访问级别包括超级用户和用户。未经验证的用户将无法访问 Kudu 集群。XXIX

从安全角度来看,还有很多工作要做。同时,加强安全性的其他建议包括限制对 Kudu 表的直接访问,以及通过商业智能工具的语义层实现基于角色的访问控制。实现数据库视图来模拟行和列级别的基于角色的访问控制是另一种选择。还可以研究如何配置 IP 访问列表,以限制某些 IP 地址对 RPC 的主服务器使用的端口(默认端口是 7051)的访问。

有关 Kudu security 的最新开发,请参考 Cloudera 的在线文档。

摘要

尽管 Hadoop 以其处理结构化、非结构化和半结构化数据的能力而闻名,但结构化关系数据仍然是大多数公司数据管理和分析策略的重点,并且在可预见的未来仍将如此。 xxx 事实上,大部分大数据用例都涉及从关系数据库复制工作负载。Kudu 是结构化数据的完美存储引擎。在整本书中,我们将重点关注 Kudu 以及它如何与 Hadoop 生态系统中的其他项目和第三方应用程序集成,以实现有用的业务用例。

参考

  1. 环球新闻网;“随着 Cloudera Enterprise 5.10 的发布,Cloudera 宣布 Apache Kudu 正式上市,”Cloudera,2017, https://globenewswire.com/news-release/2017/01/31/912363/0/en/Cloudera-Announces-General-Availability-of-Apache-Kudu-with-Release-of-Cloudera-Enterprise-5-10.html
  2. 托德·利普孔;“可变大数据中的勇敢新世界:关系存储”,奥赖利,2017, https://conferences.oreilly.com/strata/strata-ny/public/schedule/speaker/75982
  3. 吉米·香;《Apache HBase 写入路径》,Cloudera,2012, https://blog.cloudera.com/blog/2012/06/hbase-write-path/
  4. 阿帕奇软件基金会;《阿帕奇库杜简介》,ASF,2017, https://kudu.apache.org/docs/#kudu_use_cases
  5. 阿帕奇软件基金会;“Apache 软件基金会宣布推出 Apache Kudu v 1.0,”ASF,2017, https://blogs.apache.org/foundation/entry/the_apache_software_foundation_announces100
  6. 帕特·帕特森;“与@ApacheKafka、#StreamSets、@ApacheKudu & @Cloudera 在澳大利亚@DeptDefence 的创新-在 Kudu Slack 频道发现,”Twitter,2017, https://twitter.com/metadaddy/status/843842328242634754
  7. 托德·利普孔;“Kudu:针对快速数据的快速分析存储”,Cloudera,2015 年, https://kudu.apache.org/kudu.pdf
  8. 迭戈·翁加罗和约翰·奥斯特胡特;《寻找一种可理解的共识算法(扩展版)》,斯坦福大学,2014 年, https://raft.github.io/raft.pdf
  9. 迭戈·翁加罗;《共识:理论与实践的桥梁》,斯坦福大学,2014 年, https://github.com/ongardie/dissertation#readme
  10. 尼尔·钱德勒;“Oracle 的锁定模型——多版本并发控制”,Neil Chandler,2013, https://chandlerdba.com/2013/12/01/oracles-locking-model-multi-version-concurrency-control/
  11. 甲骨文;《多版本并发控制》,甲骨文,2018, https://docs.oracle.com/cd/B19306_01/server.102/b14220/consist.htm#i17881
  12. 微软;《SQL Server 2005 中的数据库并发和行级版本控制》,微软,2018, https://technet.microsoft.com/en-us/library/cc917674.aspx
  13. Teradata《关于并发控制》,Teradata,2018, https://info.teradata.com/HTMLPubs/DB_TTU_16_00/index.html#page/General_Reference%2FB035-1091-160K%2Fvju1472241438286.html%23
  14. 大卫·阿尔维斯和詹姆斯·金利;《阿帕奇库杜读写路径》,Cloudera,2017, https://blog.cloudera.com/blog/2017/04/apache-kudu-read-write-paths/
  15. 微软;“使用十进制、浮点和实数数据”,微软,2018, https://technet.microsoft.com/en-us/library/ms187912(v=sql.105).aspx
  16. 阿帕奇黑斑羚;“时间戳数据类型”,Apache Impala,2017, https://impala.apache.org/docs/build/html/topics/impala_timestamp.html
  17. Cloudera《黑斑羚命令与库杜的例子》,Cloudera,2017, https://kudu.apache.org/docs/developing.html#_kudu_integration_with_spark
  18. 威廉·柏克莱,《scantoken_noncoveringrange.cc》,Cloudera,2017, https://gist.github.com/wdberkeley/50e2e47548a0daa3d3bff68e388da37a
  19. 阿帕奇库杜,《用阿帕奇库杜开发应用》,阿帕奇库杜,2017, http://kudu.apache.org/docs/developing.html
  20. apache kudu“Kudu C++客户端样本”,Apache Kudu,2018 年,
  21. 阿巴契人:“阿帕契族常见问题”阿帕契族 2018 年“??””
  22. Cloudera《用 Impala 表使用拼花文件格式》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_parquet.html
  23. Cloudera《如何使用 Cloudera Enterprise BDR 备份和恢复 HDFS 数据》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/cm_bdr_howto_hdfs.html
  24. Cloudera《面向 Impala 的全新 Python 客户端》,Cloudera,2018, http://blog.cloudera.com/blog/2014/04/a-new-python-client-for-impala/
  25. Cloudera《将数据导入 Cloudera 数据科学工作台》,Cloudera,2018, https://www.cloudera.com/documentation/data-science-workbench/latest/topics/cdsw_import_data.html#impala_impyla
  26. Cloudera“利用 Cloudera Enterprise 实现主动/主动多集群部署”,Cloudera,2018, https://www.cloudera.com/content/dam/www/marketing/resources/whitepapers/implementing-active-deployments-with-cloudera-enterprise-whitepaper.pdf.landing.html
  27. Cloudera《Apache Kudu 命令行工具参考》,Cloudera,2018, https://kudu.apache.org/docs/command_line_tools_reference.html
  28. Cloudera《黑斑羚的整合局限》,Cloudera,2018, https://www.cloudera.com/documentation/kudu/latest/topics/kudu_known_issues.html#impala_kudu_limitations
  29. Cloudera《阿帕奇库杜安全》,Cloudera,2018, https://www.cloudera.com/documentation/kudu/latest/topics/kudu_security.html
  30. Cloudera“戴尔调查:尽管信息管理环境快速变化,结构化数据仍是焦点”,Cloudera,2018, http://www.dell.com/learn/us/en/uscorp1/press-releases/2015-04-15-dell-survey

三、Impala 简介

Impala 是一个大规模并行处理(MPP) SQL 引擎,它是为在 Hadoop 平台上运行而全新设计和构建的。 i Impala 提供快速、低延迟的响应时间,适用于商业智能应用和特定数据分析。Impala 的性能匹配,在大多数情况下,超过商用 MPP 发动机。

Impala 最初是 Cloudera 的一个项目。它被捐赠给了阿帕奇软件基金会,并于 2015 年 12 月 2 日被纳入阿帕奇孵化器。在加入 Cloudera 之前,Impala 的架构师和技术负责人 Marcel Kornacker 是谷歌的一名软件工程师,他领导了谷歌 F1 项目的分布式查询引擎的开发, ii 是一个分布式关系数据库系统,用于支持谷歌高度关键和非常受欢迎的 AdWords 业务, iii Cloudera 于 2012 年 10 月发布了 Impala 的测试版,并于 2013 年 5 月宣布正式上市。

本章不会为你提供 Impala 所有特性的详尽列表。我写这一章的目的是给你足够的关于 Impala 的信息,让你马上开始。

体系结构

Impala 和其他 Hadoop 组件一样有一个分布式架构(图 3-1 )。它利用守护进程在工作节点上直接本地执行查询。Impala 只是一个查询引擎,旨在集成多个存储引擎,如 HDFS、HBase、S3 和 Kudu。与 Oracle 和 SQL Server 等传统关系数据库系统相比,这是一种不同的体系结构,在这些系统中,查询和存储引擎紧密耦合在一起。

A456459_1_En_3_Fig1_HTML.jpg

图 3-1

Impala Architecture

Impala 可以通过 ODBC 或 JDBC 访问流行的商业智能和数据可视化工具,如 Microsoft Power BI、Qlik、Tableau、Zoomdata、Business Objects 和 Microstrategy 等。

Impala 服务器组件

Impala 的架构由三个服务组成:Impala 守护进程、目录服务和 Statestore。

黑斑羚精灵

Impala 守护进程充当查询协调器,负责接受和管理集群中查询的执行。Impala 守护进程通常安装在数据节点上,利用数据局部性,允许它直接访问存储在 HDFS 的数据块。

英帕拉目录服务

Impala 的目录服务存储和聚合从其他元数据存储库中收集的元数据信息,如 Hive Metastore 和 HDFS Namenode。

黑斑羚国家商店

Statestore 向集群中的所有 Impala 进程分发和同步元数据信息。Statestore 是一个 pub-sub Impala 服务,它向 Impala 守护进程推送更新。

Impala 和 Hadoop 生态系统

Impala 是 Hadoop 生态系统的一部分,旨在与其他 Hadoop 组件协同工作。

黑斑羚和蜂巢

Hive 是 Hadoop 的第一个 SQL 接口。Hive 最初构建在 MapReduce 之上,但也运行在其他数据处理引擎上,如 Tez 和 Spark。尽管 Hive 是出了名的慢,但是已经有很多人在使用它,所以构建一个不兼容的 SQL 接口是没有意义的。当 Cloudera 工程师在设计 Impala 时,他们的一个主要目标是使它尽可能与 Hive 互操作。利用现有的 Hive 基础设施,比如 Hive Metastore,是使 Impala 能够访问 Hive 表的关键。Impala(和 Hive)可以通过 HUE(Hadoop 用户体验)访问,这是一个基于 web 的 Hadoop SQL 工作台。此外,Impala 有自己的 shell,称为 impala-shell,可以通过命令行访问。我将在本章的后面讨论黑斑羚的外壳和色调。

黑斑羚和 HDFS

在 Hive 和 Impala 中创建的表通常使用各种文件格式存储在 HDFS 中。要在 Impala 中创建 CSV 表格:

CREATE TABLE my_csv_table (
id BIGINT,
name STRING,
gender STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';

若要大容量插入数据,可以使用下面的 LOAD DATA 或 INSERT…SELECT 语句。请注意,您必须首先创建表。

LOAD DATA INPATH '/mydata/mytextfile.txt' INTO TABLE my_csv_table;INSERT INTO my_csv_table SELECT customerkey, concat(firstname,' ',lastname), gender FROM dimcustomer;

可以一次插入一个值,但不建议这样做。

INSERT INTO my_csv_table VALUES (200,'Norman Bates','M');

执行的每个 INSERT…VALUES 语句都将在 HDFS 中生成一个小文件,并可能导致碎片和性能问题。执行大容量插入时,建议使用 LOAD DATA 或 INSERT…SELECT。如果必须执行许多单行插入,最好使用不同的存储引擎,比如 HBase 或 Kudu。

也可以手动复制表的数据目录(/user/hive/warehouse/my _ CSV _ table)中的 CSV 文件。在运行 refresh 或 invalidate 语句之前,以这种方式添加的数据对 Impala 是不可见的。我们将在本章的后面介绍元数据的刷新和失效。

英帕拉和 HBase

Impala 可以通过 Hive 外部表查询 HBase 中存储的表。Impala 还可以将数据插入 Hive 外部表,数据存储在底层 HBase 表中。这里有一个例子。

启动 hbase shell。创建一个 HBase 表并插入一些测试数据。

create 'table1', 'colfam1'

put 'table1','firstrow','colfam1:name', 'Jeff Frazier'
put 'table1','firstrow','colfam1:city', 'Los Angeles'
put 'table1','firstrow','colfam1:age', '75'

put 'table1','secondrow','colfam1:name', 'Susan Fernandez'
put 'table1','secondrow','colfam1:city', 'New York'
put 'table1','secondrow','colfam1:age', '72'

put 'table1','thirdrow','colfam1:name', 'Tony Cheng'
put 'table1','thirdrow','colfam1:city','San Francisco'
put 'table1','thirdrow','colfam1:age','77'

在配置单元中创建外部表。

CREATE EXTERNAL TABLE hive_on_hbase (key string,name string, age int, city string)
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES
('hbase.columns.mapping'=':key, colfam1:name,colfam1:age,colfam1:city')
TBLPROPERTIES('hbase.table.name'='table1');

启动黑斑羚壳。因为这个表是在 Impala 之外创建的,所以它不知道这个新表的任何信息。在从新表中选择数据之前,请执行 invalidate metadata。

invalidate metadata;

select * from hive_on_hbase;

key          age    city            name
firstrow     75     Los Angeles     Jeff Frazier
secondrow    72     New York        Susan Fernandez
thirdrow     77     San Francisco   Tony Cheng

INSERT INTO hive_on_hbase VALUES ('fourthrow', 65,'Palo Alto','Kevin Lee');

select * from hive_on_hbase;

key         age    city            name
firstrow    75     Los Angeles     Jeff Frazier
secondrow   72     New York        Susan Fernandez
thirdrow    77     San Francisco   Tony Cheng
fourthrow   65     Palo Alto       Kevin Lee

黑斑羚和 S3

亚马逊 S3 是一个流行的对象存储,经常被用作临时集群的数据存储。它还是备份和冷数据的经济高效的存储方式。从 S3 读取数据就像从 HDFS 或任何其他文件系统读取数据一样。从 CDH 5.8 (Impala 2.6)及更高版本开始,Impala 支持对存储在 S3 上的数据进行查询和插入。

CREATE TABLE my_s3_table (
id BIGINT,
name STRING,
gender STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LOCATION 's3://impala01/csvfiles/';

黑斑羚和库杜

Kudu 是 Cloudera 的新关系数据存储。Kudu 是结构化数据的推荐存储引擎,可以与 Impala 无缝协作。我将在第四章中对黑斑羚和 Kudu 进行深入报道。

文件格式

Impala 支持多种文件格式,如 Text、Avro、RCFile、SequenceFile 和 Parquet。Impala 不支持 Hortonworks 支持的 ORC 文件格式。Impala 可以创建所有文件格式的表格,并从中进行查询。Impala 不允许插入 Avro、RCFile 和 SequenceFile 表。您必须从 Hive 运行 insert 语句或使用 LOAD DATA 导入数据。

Impala 在创建表格时的默认文件格式是用 ASCII 0x01 字符(Ctrl-A)分隔值的文本。支持多种压缩编解码器,如 Snappy(速度和压缩率的最佳平衡)、GZIP(最高压缩级别,但速度较慢)、Deflate、BZip2 和 LZO。一般大多数情况下用 Snappy。

拼花是 Impala 的推荐文件格式。Parquet 最初是 Twitter 和 Cloudera 的一个联合项目。Parquet 是一种分栏式文件格式,专门为大型全表扫描和分析查询而设计,这正是 Impala 的设计初衷。要在 Impala 中创建拼花表,只需在 CREATE TABLE 语句中包含 STORED AS PARQUET,例如:

CREATE TABLE customer (
id BIGINT,
name STRING)
STORED AS PARQUET;

Impala SQL

Impala 的行为就像一个传统的关系数据库管理系统。Impala 使用 SQL 来检索、管理和修改数据(与 Kudu 一起使用时)。表、视图和其他对象在数据库中进行逻辑组织。存储在表中的数据是按行和列组织的。这些列中的每一列都有一个关联的数据类型。

数据类型

就像典型的 RDBMS 一样,Impala 支持标准数据类型来存储不同种类的数据,如表 3-1 所示。

表 3-1

List of Data Types Supported by Impala

| 数据类型 | 描述 | | :-- | :-- | | 排列 | 可以包含一组元素的复杂数据类型。 | | 比吉斯本 | 8 字节整数数据类型。范围:-9223372036854775808..9223372036854775807. | | 布尔代数学体系的 | 代表真或假选择的数据类型。 | | 茶 | 固定长度的字符类型。您可以指定的最大长度为 255。 | | 小数 | 具有固定小数位数和精度的数值数据类型。适用于金融和其他算术计算。没有精度或小数位数值的 DECIMAL 等效于 DECIMAL(9,0)。 | | 两倍 | 双精度浮点数据类型。范围:4.94065645841246544e-324d..1.79769313486231570e+308,正或负 | | 浮动 | 单精度浮点数据类型。范围:1.40129846432481707e-45..3.40282346638528860e+38,正或负。 | | (同 Internationalorganizations)国际组织 | 4 字节整数数据类型。范围:-2147483648..2147483647.没有无符号子类型。 | | 地图 | 可以包含一组键值对的复杂数据类型。 | | 真实的 | DOUBLE 数据类型的别名。 | | 斯莫列特 | 双字节整数数据类型。范围:-32768..32767. | | 线 | 长度:最大 32,767 字节。 | | 结构体 | 可以包含单个项目的多个字段的复杂数据类型。 | | 时间戳 | 数据类型。范围:允许的日期值范围从 1400-01-01 到 9999-12-31。 | | Tinyint | 1 字节整数数据类型。范围:-128..127. | | 可变长字符串 | 可变长度字符类型。您可以指定的最大长度是 65,535。 |

SQL 语句

如前所述,Impala 使用 SQL 作为查询、管理和操作数据的语言。Impala 遵循 SQL-92 标准,其中包括许多现代 SQL 实现中常见的标准特性和内置函数。下面是 Impala 支持的常用 SQL 语句的部分列表。查阅 Impala SQL 语言参考 iv 以获得对 Impala SQL 的完整介绍。

创建数据库

创建数据库在 Impala 中创建一个数据库。就像其他 RDBMS 一样,Impala 使用数据库的概念来存储它的表和其他对象。

CREATE DATABASE salesdb;

创建表格

默认情况下,Impala 创建“内部”表。内部表由 Impala 管理。如果删除一个内部表,组成该表的物理文件也会被删除。

CREATE TABLE salesdb.customers (
id BIGINT,
name STRING,
gender STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','

创建外部表

外部表允许您指定已经存储在存储引擎(如 HDFS、HBase、S3 或 Kudu)中的数据的位置。例如,我可以根据 HDFS 目录/user/bob/products 中存储的 CSV 数据创建一个名为 products 的外部表。外部表类似于视图。如果删除外部表,组成该表的物理文件不会被删除,只会删除表定义。您需要从底层存储中手动删除数据。

CREATE EXTERNAL TABLE salesdb.products (
(
id BIGINT,
productname STRING,
productdesc STRING,
cost DOUBLE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','

LOCATION '/user/bob/products/';

挑选

SELECT 允许您从表或视图中检索数据。您可以手动指定列名以仅返回特定的列,或者如果希望返回所有列,则指定星号。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer;

Customerkey  firstname    lastname    birthdate
11001        Eugene       Huang       1976-05-10
11044        Adam         Flores      1954-11-21
11063        Angela       Murphy      1980-10-04
11075        Felicia      Jimenez     1963-05-16
11099        Adam         Ross        1966-09-05
11107        Bianca       Lin         1976-03-04
11118        Alvin        Zeng        1962-12-31
11156        Maria        Roberts     1981-08-06
11169        Bryce        Richardson  1973-12-20
11171        Jonathan     Hill        1978-10-05

在哪里

基于条件过滤数据。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
WHERE firstname="Eugene";

Customerkey  firstname    lastname    birthdate
11001        Eugene       Huang       1976-05-10

与和或

AND 和 OR 允许用户在 WHERE 子句中组合多个条件。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
WHERE firstname = 'Adam'
AND lastname="Flores";

customerkey  firstname    lastname    birthdate
11044        Adam         Flores      1954-11-21

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
WHERE firstname = 'Adam'
OR firstname="Alvin";

customerkey  firstname    lastname    birthdate
11044        Adam         Flores      1954-11-21
11099        Adam         Ross        1966-09-05
11118        Alvin        Zeng        1962-12-31

喜欢

LIKE 允许用户指定通配符来搜索列中的模式。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
WHERE lastname
LIKE 'R%'

Customerkey  firstname   lastname     birthdate
11099        Adam        Ross         1966-09-05
11156        Maria       Roberts      1981-08-06
11169        Bryce       Richardson   1973-12-20

限制

LIMIT 限制 SQL 查询返回的行数。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
LIMIT 5;

customerkey  firstname    lastname    birthdate
11001        Eugene       Huang       1976-05-10
11044        Adam         Flores      1954-11-21
11063        Angela       Murphy      1980-10-04
11075        Felicia      Jimenez     1963-05-16
11099        Adam         Ross        1966-09-05

以...排序

排序依据按降序或升序对结果进行排序。

例子

SELECT customerkey, firstname, lastname, birthdate
FROM dimcustomer
ORDER BY customerkey;

Customerkey  firstname    lastname    birthdate
11001        Eugene       Huang       1976-05-10
11002        Ruben        Torres      1971-02-09
11003        Christy      Zhu         1973-08-14
11004        Elizabeth    Johnson     1979-08-05
11005        Julio        Ruiz        1976-08-01
11006        Janet        Alvarez     1976-12-02
11007        Marco        Mehta       1969-11-06
11008        Rob          Verhoff     1975-07-04
11009        Shannon      Carlson     1969-09-29

分组依据和拥有

GROUP BY 与 COUNT、AVG 和 SUM 等聚合函数一起使用,按一列或多列对结果进行分组。在汇总结果后过滤数据。它与 GROUP BY 一起使用。

例子

SELECT firstname, count(*)
FROM dimcustomer
GROUP BY firstname
HAVING firstname="Adam";

firstname    count(*)
Adam         51

明显的

DISTINCT 返回列中的不同值。

例子

SELECT DISTINCT firstname
FROM dimcustomer;

联合和联合独特

UNION 和 UNION DISTINCT 组合两个或多个 SELECT 语句的结果。两者都只返回不同的值。

例子

SELECT firstname
FROM dimcustomer
UNION
SELECT firstname
FROM dimcustomer2;

联合所有

UNION ALL 类似于 UNION,只是它允许在结果中出现重复值。

例子

SELECT firstname
FROM dimcustomer
UNION ALL SELECT
firstname
FROM dimcustomer2;

加入

JOIN 使用两个表中的公共值合并两个或多个表中的行。Impala 支持不同类型的连接。更多细节请参考 Impala SQL 语言参考。

例子

SELECT t1.firstname, t1.lastname, t2.salesamount
FROM
dimcustomer AS t1
INNER JOIN
salesfact AS t2
WHERE
t1.customerkey = t2.customerkey;

子查询

子查询是另一个 SQL 查询中的 SQL 查询。

例子

SELECT firstname
FROM
dimcustomer
WHERE firstname
IN
(SELECT firstname FROM dimcustomer2);

形容

DESCRIBE 返回关于表的元数据信息。

例子

DESCRIBE dimcustomer;

DESCRIBE FORMATTED dimcustomer;

使元数据无效

无效元数据使一个或多个表的元数据无效。它迫使 Impala 在对表执行查询之前重新加载表的元数据。

例子

INVALIDATE METADATA dimcustomer;

加载数据

加载数据将存储在 HDFS 文件中的数据加载到表格中。

例子

LOAD DATA INPATH '/home/bob/customers.txt' INTO TABLE dimcustomer;

恢复精神

Refresh 立即重新加载表的元数据,但只加载新添加数据的部分元数据,与使元数据无效相比,这是一种开销较低的操作。

例子

REFRESH dimcustomer;

集合语句

SET 语句允许用户改变 SQL 语句的行为。这些设置只影响在 impala-shell 会话中执行的查询。

数量 _ 节点

NUM_NODES 限制将执行 SQL 查询的节点数量。

例子

SET NUM_NODES=1

MEM _ 极限

MEN_LIMIT 定义了一个 SQL 查询在每个节点上可以利用的最大内存量。

例子

set mem_limit=3G;

set mem_limit=3000M;

实时 _ 进度

LIVE_PROGRESS 显示一个实时进度条,显示已完成的执行百分比。

例子

SET LIVE_PROGRESS=TRUE;

批量大小

BATCH_SIZE 显示 SQL 查询一次处理的行数。较大的数量可以提高 SQL 查询的性能,但会消耗更多内存。默认值设置为 1024。

例子

SET BATCH_SIZE=2048

显示语句

SHOW 语句使用户能够获得关于 Impala 对象的信息。下面是 Impala 支持的最常见的 SHOW 语句列表。

显示数据库

显示数据库显示所有可用的数据库。

例子

SHOW DATABASES;

name                comment
_impala_builtins    System database for Impala builtin functions
default             Default Hive database

显示表格

显示表格显示数据库中所有可用的表格。

例子

SHOW TABLES;

name
customers
dimcustomer
dimgeography
dimproductcategory
hbase_users
sample_07
sample_08
sensordata
testtable
testuser
users
web_logs

显示文件

显示文件显示组成表格的所有 HDFS 文件。

例子

SHOW FILES IN web_logs;

Path                                         Size        Partition
hdfs://kuducluster:8020/user/cloudera/2015_11_18/web_logs_1.csv                    113.00KB    date=2015-11-18
hdfs://kuducluster:8020/user/cloudera/2015_11_19/web_logs_2.csv                    103.18KB    date=2015-11-19
hdfs://kuducluster:8020/user/cloudera/2015_11_20/web_logs_3.csv                    105.59KB    date=2015-11-20
hdfs://kuducluster:8020/user/cloudera/2015_11_21/web_logs_4.csv                     82.68KB    date=2015-11-21

显示表格统计

显示表格 TATS 显示表格的低级信息。

例子

SHOW TABLE STATS web_logs;

date                 #Rows    #Files          Size       Bytes Cached Cache Replication    Format   Incremental stats Location
2015-11-18           -1       1               113.00KB   NOT CACHED   NOT CACHED           TEXT     false           hdfs://kuducluster:8020/user/cloudera/2015_11_18
2015-11-19           -1       1               103.18KB   NOT CACHED  NOT CACHED           TEXT     false           hdfs://kuducluster:8020/user/cloudera/2015_11_19
2015-11-20           -1       1               105.59KB   NOT CACHED      NOT CACHED           TEXT     false           hdfs://kuducluster:8020/user/cloudera/2015_11_20
2015-11-21           -1       1               82.68KB    NOT CACHED      NOT CACHED           TEXT     false           hdfs://kuducluster:8020/user/cloudera/2015_11_21
Total               -1        4               404.45KB   0B

内置函数

Impala 包含类似于现代 RDBMS 中的内置函数。这些函数可以在 SQL 语句中用于转换数据、操作字符串以及执行日期和数学计算。Impala 包括聚合函数、字符串函数、日期和时间函数以及数学函数等等。下面列出了 Impala 中包含的一些常用功能。参考 Impala SQL 语言参考 v 完整覆盖 Impala 的内置函数。

全局唯一识别

uuid函数以字符串形式返回 UUID 或通用唯一标识符。此函数对于生成唯一值来标识对象非常有用。它也可以用作主键值。

select uuid();

uuid()                               
c14f1469-233b-4d95-9efe-8cf1d2205d7f

现在

now函数返回当前日期和时间。

select now();

now()                         
2017-11-06 22:44:19.926002000

正则表达式 _like

regexp函数返回 true 或 false 来表示字符串是否包含模式指定的正则表达式。

select regexp_like('water','wat');

regexp_like('water', 'wat')
true                        

丙烯腈-丁二烯-苯乙烯

abs函数返回自变量的绝对值。

select abs(-100);

abs(-100)

100     

fnv_hash(类型五)

基于输入参数返回 64 位 BIGINT 值。此函数实现了一个版本的 Fowler–Noll–Vo 哈希函数,该函数在加密方面并不安全。它可用于需要简单哈希功能的应用程序,如负载平衡和哈希分区。

select abs(fnv_hash((now())));
abs(fnv_hash((now())))
5911639952301673123   

用户定义的函数

用户定义函数或 UDF 允许您编写自己的 Impala 函数。这在你需要一个 Impala 中没有的特性时很有用。Impala 支持用 Java 编写的 UDF,但是为了获得最佳性能,建议你用 C++编写 UDF。还可以创建用户定义的聚合函数(UDAFs ),根据一组值返回一个值。关于如何创建用户自定义函数,请参考 Cloudera 的文档。

黑斑羚的复杂类型

Impala 支持复杂类型,如 STRUCT、ARRAY 和 MAP。 vi 复杂数据类型有几个好处,比如通过消除对表连接的需要以及更简单的 ETL 和数据建模来提高查询性能。目前,对复杂类型的支持仅适用于通过 Hive 或 Spark 创建的拼花表。Impala 目前不能创建包含复杂类型的拼花桌。另外,Kudu 还不支持复杂类型;因此,在使用 Kudu 表时,需要传统的维度建模或反规范化。CDH 包括带有嵌套类型的示例表,您可以通过 HUE 安装这些表。让我们展示一些如何处理复杂类型的例子。

DESCRIBE customers;
+-------------------+------------------------+---------+
| name              | type                   | comment |

+-------------------+------------------------+---------+
| id                | int                    |         |
| name              | string                 |         |
| email_preferences | struct<                |         |
|                   |   email_format:string, |         |
|                   |   frequency:string,    |         |
|                   |   categories:struct<   |         |
|                   |     promos:boolean,    |         |
|                   |     surveys:boolean    |         |
|                   |   >                    |         |
|                   | >                      |         |
| addresses         | map<string,struct<     |         |
|                   |   street_1:string,     |         |
|                   |   street_2:string,     |         |
|                   |   city:string,         |         |
|                   |   state:string,        |         |
|                   |   zip_code:string      |         |
|                   | >>                     |         |
| orders            | array<struct<          |         |
|                   |   order_id:string,     |         |
|                   |   order_date:string,   |         |
|                   |   items:array<struct<  |         |
|                   |     product_id:int,    |         |
|                   |     sku:string,        |         |
|                   |     name:string,       |         |
|                   |     price:double,      |         |
|                   |     qty:int            |         |
|                   |   >>                   |         |
|                   | >>                     |         |
+-------------------+------------------------+---------+

查询结构字段

嵌套字段通过点符号来访问。

SELECT
id,
name,

email_preferences.email_format ef,
email_preferences.categories.promos ecp
FROM customers
LIMIT 10;

+-------+---------------------+------+-------+
| id    | name                | ef   | ecp   |
+-------+---------------------+------+-------+
| 75012 | Dorothy Wilk        | html | true  |
| 17254 | Martin Johnson      | text | true  |
| 12532 | Melvin Garcia       | html | true  |
| 42632 | Raymond S. Vestal   | html | true  |
| 77913 | Betty J. Giambrone  | text | false |
| 38807 | Rebecca T. Johnson  | html | true  |
| 71843 | David B. Allison    | text | true  |
| 67099 | Jay N. Weaver       | text | false |
| 83510 | Carol B. Houser     | html | false |
| 48072 | Octaviana Guiterrez | text | false |
+-------+---------------------+------+-------+

查询深度嵌套的集合

使用点标记法在 FROM 子句中指定嵌套数组。

SELECT
order_id,
order_date,
items.name,
items.qty
FROM
customers.orders,
customers.orders.items
LIMIT 10;
+----------------------+-----+
| name                 | qty |
+----------------------+-----+
| Evening Clutch       | 1   |
| Large Tassel Pouch   | 1   |
| Flameless Candle     | 2   |
| Tea for One          | 1   |
| Towel Set            | 4   |
| Maple Dining Table   | 1   |
| Paloma Accent Table  | 1   |
| Trunk Coffee Table   | 1   |
| Simple Scallop Table | 2   |
| Murano Glass Vase    | 4   |

+----------------------+-----+

使用嵌套集合的 ANSI-92 SQL 连接进行查询

Impala 支持嵌套集合的 ANSI-92 SQL 连接。注意,由于父子关系上的隐式连接,您不需要使用 on 子句。

SELECT
id,
name,
co.order_id,
co.order_date
FROM
customers c
INNER JOIN
customers.orders co
LIMIT 10;

+-------+--------------+----------+---------------------------+
| id    | name         | order_id | order_date                |
+-------+--------------+----------+---------------------------+
| 75012 | Dorothy Wilk | 4056711  | 2015-05-01T14:22:25-04:00 |
| 75012 | Dorothy Wilk | J882C2   | 2015-06-10T11:00:00-05:00 |
| 75012 | Dorothy Wilk | I72T39   | 2015-03-14T11:00:00-05:00 |
| 75012 | Dorothy Wilk | PB6268   | 2015-02-11T14:22:25-04:00 |
| 75012 | Dorothy Wilk | B8623C   | 2015-04-21T11:00:00-05:00 |
| 75012 | Dorothy Wilk | R9S838   | 2015-07-09T11:00:00-05:00 |
| 75012 | Dorothy Wilk | HS3124   | 2015-10-14T00:00:00       |
| 75012 | Dorothy Wilk | BS5902   | 2014-10-23T00:00:00       |
| 75012 | Dorothy Wilk | DN8815   | 2015-09-07T00:00:00       |
| 75012 | Dorothy Wilk | XR2771   | 2015-12-25T00:00:00       |

+-------+--------------+----------+---------------------------+

黑斑羚壳

Impala shell (impala-shell)是一个命令行实用程序,可以用来与 Impala 交互。您可以运行 SQL 语句,将查询的输出保存到文件中,或者显示有关查询的低级性能信息。您可以在交互和非交互模式下使用 impala-shell。

在执行 impala-shell 命令时,您可以使用几个命令行选项。表 3-2 中列出了一些最重要的选项。要获得更详尽的列表,请参考 Impala 文档。VII

表 3-2

Impala Shell Command-Line Options

| 命令行选项 | 描述 | | :-- | :-- | | -i 主机名或-impalad =主机名 | impala-shell 将连接到运行 impala 守护进程的主机。 | | -o 文件名或-输出文件文件名 | 将查询结果存储在指定的文本文件中。 | | -d 默认数据库或-数据库=默认数据库 | 指定启动 impala-shell 时使用的默认数据库。 | | -q 查询或-query =查询 | 从命令行执行查询。 | | -f 查询文件或-查询文件=查询文件 | 执行存储在文件中的查询。 | | -h 或者-救命 | 显示帮助信息。 |

让我们开始在交互模式下使用 impala-shell。让我们通过使用 impala-shell 选项使输出更容易显示。

impala-shell
Starting Impala Shell without Kerberos authentication
Connected to quickstart.cloudera:21000
Server version: impalad version 2.9.0-cdh5.12.0 RELEASE (build 03c6ddbdcec39238be4f5b14a300d5c4f576097e)
***************************************************************************
Welcome to the Impala shell.
(Impala Shell v2.9.0-cdh5.12.0 (03c6ddb) built on Thu Jun 29 04:17:31 PDT 2017)
Press TAB twice to see a list of available commands.
***************************************************************************
Pressing tab twice will list available impala-shell commands.

alter     create    describe  explain   insert    quit      shell     src
unset     use       with      compute   delete    drop      help      load
select    show      summary   update    values    connect   desc      exit
history   profile   set       source    tip       upsert    version

You can use help to list the commands anytime you're inside impala-shell.

help;

Documented commands (type help <topic>):
========================================
compute  describe  explain  profile  select  shell  tip    use     version
connect  exit      history  quit     set     show   unset  values  with   

Undocumented commands:
======================
alter   delete  drop  insert  source  summary  upsert
create  desc    help  load    src     update

如果您想要了解特定命令的功能,可以键入 help“command”

help tip;
Print a random tip

执行 tip 命令。

tip;

When you set a query option it lasts for the duration of the Impala shell session.

您可以查看和查询可用的数据库和表。

show databases;

+------------------+----------------------------------------------+
| name             | comment                                      |
+------------------+----------------------------------------------+
| _impala_builtins | System database for Impala builtin functions |
| default          | Default Hive database                        |
+------------------+----------------------------------------------+

use default;

show tables;

+-----------------+
| name            |
+-----------------+
| customer_backup |
| customers       |
| sample_07       |
| sample_08       |
| testtable       |
| web_logs        |
+-----------------+

select count(*) from customers;
+----------+
| count(*) |
+----------+
| 53       |
+----------+

select client_ip,city,country_name from web_logs limit 10;

+-----------------+---------------+---------------+
| client_ip       | city          | country_name  |
+-----------------+---------------+---------------+
| 128.199.234.236 | Singapore     | Singapore     |
| 128.199.234.236 | Singapore     | Singapore     |
| 128.199.234.236 | Singapore     | Singapore     |
| 128.199.234.236 | Singapore     | Singapore     |
| 128.199.234.236 | Singapore     | Singapore     |
| 66.249.76.236   | Mountain View | United States |
| 222.85.131.87   | Guiyang       | China         |
| 222.85.131.87   | Guiyang       | China         |
| 101.226.168.225 | Shanghai      | China         |
| 66.249.76.225   | Mountain View | United States |
+-----------------+---------------+---------------+

describe web_logs;

+-------------------+----------+---------+
| name              | type     | comment |

+-------------------+----------+---------+
| _version_         | bigint   |         |
| app               | string   |         |
| bytes             | smallint |         |
| city              | string   |         |
| client_ip         | string   |         |
| code              | tinyint  |         |
| country_code      | string   |         |
| country_code3     | string   |         |
| country_name      | string   |         |
| device_family     | string   |         |
| extension         | string   |         |
| latitude          | float    |         |
| longitude         | float    |         |
| method            | string   |         |
| os_family         | string   |         |
| os_major          | string   |         |
| protocol          | string   |         |
| record            | string   |         |
| referer           | string   |         |
| region_code       | bigint   |         |
| request           | string   |         |
| subapp            | string   |         |
| time              | string   |         |
| url               | string   |         |
| user_agent        | string   |         |
| user_agent_family | string   |         |
| user_agent_major  | string   |         |
| id                | string   |         |
| date              | string   |         |
+-------------------+----------+---------+

impala-shell 的一个便利特性是它能够运行 shell 命令。

shell iostat;

Linux 2.6.32-642.15.1.el6.x86_64 (kuducluster)         11/03/2017         _x86_64_        (4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          40.91    0.00    2.75    0.12    0.00   56.22

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
sda              16.11       643.23       619.74    6647326    6404562
dm-0             83.61       631.40       597.55    6525034    6175256
dm-1              0.03         0.25         0.00       2600          0
dm-2              3.74        10.91        22.18     112770     229256

历史记录允许您列出最近执行的命令的历史记录。

history;

[1]
[2]: show tables;
[3]: select * from users;
[4]: select count(*) from dimcustomer;
[5]: select customerkey, firstname, lastname, birthdate from dimcustomer limit 10;
[6]: describe dimcustomer;
[7]: show databases;
[8]: show tables;
[9]: help;
[10]: history;

性能调整和监控

Impala 是一个高性能的 SQL MPP 引擎,能够查询 1 万亿行或更多。 viii 然而,有时仍然需要一些性能调整和监控,以确保 Impala 发挥其最大潜力。幸运的是,Cloudera Enterprise 包括一些工具和命令,可以帮助您调优和监控 Impala。

解释

Explain 显示 SQL 查询的执行计划。执行计划显示了 Impala 为执行您的 SQL 查询而执行的低级操作的顺序。您可以使用执行计划来评估查询的性能。你从下到上阅读执行计划。

explain select count(*) from web_logs;

Query: explain select count(*) from web_logs
Explain String
Estimated Per-Host Requirements: Memory=90.00MB VCores=1
WARNING: The following tables are missing relevant table and/or column statistics.
default.web_logs
""
PLAN-ROOT SINK
|
03:AGGREGATE [FINALIZE]
|  output: count:merge(*)
|
02:EXCHANGE [UNPARTITIONED]
|
01:AGGREGATE
|  output: count(*)
|
00:SCAN HDFS [default.web_logs]
   partitions=4/4 files=4 size=404.45KB
Fetched 16 row(s) in 3.50s

摘要

为查询执行的不同阶段提供计时。它帮助您确定潜在的瓶颈在哪里。下面的结果表明,大部分活动都用于执行 HDFS 扫描。

summary;

Operator      #Hosts        Avg Time     Max Time     #Rows
03:AGGREGATE   1            0ns           0ns          1
02:EXCHANGE    1            0ns           0ns          1
01:AGGREGATE   1            0ns           0ns          1
00:SCAN HDFS   1            44.00ms       44.00ms      1.00K

Est. #Rows    Peak Mem  Est.      Peak Mem       Detail
1              20.00 KB           -1 B           FINALIZE       
1              0 B                -1 B           UNPARTITIONED
1              16.00 KB           10.00 MB
-1             280.00 KB          80.00 MB       default.web_log

轮廓

Profile 显示关于 SQL 查询的更详细的性能报告,例如内存使用、网络和存储等待时间等。下面显示的是结果的摘录。

profile;

Query Runtime Profile:
Query (id=26414d8a59a8fff4:fd59c4f500000000):
  Summary:
    Session ID: 8e4d099336a7cabe:83d7327345384cb4
    Session Type: BEESWAX
    Start Time: 2017-11-05 22:21:59.218421000
    End Time: 2017-11-05 22:21:59.345006000
    Query Type: QUERY
    Query State: FINISHED
    Query Status: OK
    Impala Version: impalad version 2.7.0-cdh5.10.0 RELEASE (build 785a073cd07e2540d521ecebb8b38161ccbd2aa2)
    User: hadoop
    Connected User: hadoop
    Delegated User:
    Network Address: ::ffff:10.0.1.101:53796
    Default Db: default
    Sql Statement: select count(*) from web_logs
    Coordinator: kuducluster:22000
    Query Options (non default):
    Plan:

Cloudera 经理

Cloudera Manager 提供了一个图形用户界面,可以用来监控 Impala(图 3-2 )。

A456459_1_En_3_Fig2_HTML.jpg

图 3-2

Cloudera Manager

您可以放大图表以获得不同性能指标的更详细视图,例如查询持续时间(图 3-3 )。

A456459_1_En_3_Fig3_HTML.jpg

图 3-3

Query Duration

您还可以监视查询。在 Cloudera Manager 中,导航到集群➤黑斑羚查询,如图 3-4 所示。

A456459_1_En_3_Fig4_HTML.jpg

图 3-4

Cloudera Manager – Impala Queries

您将看到集群中已经执行或正在执行的 Impala 查询的历史。Cloudera Manager 还显示持续时间、用户和其他指标等信息(图 3-5 )。

A456459_1_En_3_Fig5_HTML.jpg

图 3-5

Impala Queries

Impala 有自己的 web 界面,提供关于其 StateStore 和目录服务器的底层细节(图 3-6 )。

A456459_1_En_3_Fig6_HTML.jpg

图 3-6

Impala Web UI

Impala StateStore WebUI 提供了关于 Impala StateStore 的详细性能和诊断信息(图 3-7 )。

A456459_1_En_3_Fig7_HTML.jpg

图 3-7

Impala StateStore WebUI

Impala 目录服务器 WebUI 提供了关于 Impala 目录服务器的详细性能和诊断信息(图 3-8 )。

A456459_1_En_3_Fig8_HTML.jpg

图 3-8

Impala Catalog Server WebUI

Impala 性能建议

有些建议是特定于 Impala 的,而有些是大多数数据仓库共有的。

使用黑斑羚拼花地板

Parquet 是一种高度优化的柱状文件格式,设计用于 Impala。创建表时尽可能使用拼花文件格式(如果您的环境中没有 Kudu)。我会在下一章讨论 Impala 和 Kudu。

解决“小文件”问题

小文件对黑斑羚来说是个大问题。HDFS 没有优化处理大量的小文件。这通常是由于在一段时间内对一个表执行几个 INSERT…值造成的。解决这个问题的一种方法是通过创建另一个表并使用 INSERT…SELECT 将数据大容量插入该表来合并小文件。insert 语句的并行度取决于运行 Impala 的工作节点的数量。这意味着,如果集群中有 20 个工作节点,insert 语句可能会生成 20 个小文件。通过将 NUM_NODES 设置为 1,可以强制 Impala 写一个大文件,而不是 20 个小文件。这将强制 Impala 在 1 个节点上执行 insert 语句。因为您将并行度降低到 1,这可能会导致其他性能问题,所以请相应地进行测试。

表分区

分区是大型表的一项要求。如果您有大型表分区,它应该导致查询处理单个较小的分区,而不是读取整个表。这就是所谓的分区修剪。在大多数情况下,分区修剪可以显著缩短执行时间,减少 Impala 检索的数据量。我在第四章中讨论了表分区。

反规格化

反规范化是另一个优化选项。表连接通常是一些最低效的数据库操作,尤其是当您连接几个大表时。通过对表进行反规范化,可以提高读取性能,但代价是重复数据和降低写入性能。对于大多数 DSS 和报告系统来说,这是一个可以接受的折衷方案。

创建汇总或汇总表

从技术上讲,创建汇总或汇总表是一种反规范化形式。在大多数 RDBMS 中,这是使用物化视图实现的。Impala 不支持物化视图,但是您可以通过预先构建包含聚合数据的汇总表来模仿它们。

收集大型表连接中使用的表的统计信息

表统计数据需要是最新的,这样 Impala 就可以为包含在查询中的表计算出最有效的连接顺序。可以使用 COMPUTE STATS 定期收集频繁连接的表的统计信息。

关于如何调优 Impala 的更多细节,请参考 Impala 性能指南和最佳实践。 ix

工作量和资源管理

Impala 有自己的资源和工作负载管理功能,不像 Spark 和 MapReduce 等其他 Hadoop 组件那样与 YARN 集成。

Note

在早期版本的 CDH 中有一个称为骆驼的组件,它提供了与黑斑羚的纱线集成,但从 CDH 5.5 /黑斑羚 2.3 开始停止使用。

在 Impala 中,如果启用了静态分区,那么 CPU、网络和磁盘 IO 上的资源限制是通过使用 cgroups 来实现的。在内存使用方面,Impala 的内存限制(设置 MEM _ 限制)是用来限制内存使用的。动态资源池允许基于池中可用资源对 Impala 查询进行动态资源管理。

准入控制

准入控制是 Impala 中的一个资源管理特性,它限制 SQL 查询的并发性,以防止大量用户同时使用而导致的过度使用。准入控制将用户查询排队,直到资源被释放并变得可用。

有关 Impala 工作负载和资源管理特性的更多信息,请参考 Impala 的在线文档。 x

Hadoop 用户体验

Hadoop 用户体验或 HUE 是一个基于 web 的用户界面,为 Impala 和 Hive 提供了一个易于使用的 SQL 编辑器和 Metastore 管理器。HUE 还包括其他功能,可以让您与 HBase、文件浏览器和作业浏览器进行交互。

SQL 编辑器允许用户运行 Impala 查询,是一个比 impala-shell 更友好的工具(图 3-9 )。

A456459_1_En_3_Fig9_HTML.jpg

图 3-9

HUE – Impala

Metastore 管理器允许用户管理 Impala 和 Hive 表以及其他对象(图 3-10 )。

A456459_1_En_3_Fig10_HTML.jpg

图 3-10

HUE – Metastore Manager

企业中的黑斑羚

由于其 JDBC 和 ODBC 驱动程序,Impala 与市场上流行的商业智能、数据整理和数据可视化工具兼容,例如 Microstrategy、Business Objects、Cognos、Oracle 商业智能企业版(OBIEE)、Tableau、Qlik、Power BI 和 Zoomdata 等。我在第九章中用 Impala 介绍了数据可视化和商业智能。

摘要

Impala 是 Cloudera Enterprise 最重要的组件之一。虽然 Hive 使 Hadoop 可以为大众所用,但正是 Impala 等 SQL MPP 引擎使 Hadoop 入侵企业成为可能。我们将在下一章讨论黑斑羚和库杜。

参考

  1. 马塞尔·科尔纳克;“Impala:面向 Hadoop 的现代开源 SQL 引擎”,Cloudera,2015, http://cidrdb.org/cidr2015/Papers/CIDR15_Paper28.pdf
  2. 马塞尔·科尔纳克;《遇见工程师:Marcel Kornacker》,Cloudera,2013, https://blog.cloudera.com/blog/2013/01/meet-the-engineer-marcel-kornacker/
  3. 杰夫·舒特;F1:“可扩展的分布式 SQL 数据库”,谷歌,2013 年, https://research.google.com/pubs/pub41344.html
  4. Cloudera《Impala SQL 语言参考》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_langref.html
  5. Cloudera《Impala SQL 语言参考》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_langref.html
  6. http://blog.cloudera.com/blog/2015/11/new-in-cloudera-enterprise-5-5-support-for-complex-types-in-impala/
  7. Cloudera《impala-shell 命令行选项》,Cloudera,2018, http://www.cloudera.com/documentation/cdh/5-1-x/Impala/Installing-and-Using-Impala/ciiu_shell_options.html
  8. Cloudera《黑斑羚常见问题》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_faq.html
  9. Cloudera《Impala 性能指南和最佳实践》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_perf_cookbook.html#perf_cookbook__perf_cookbook_stats
  10. Cloudera《黑斑羚的资源管理》,Cloudera,2018, https://www.cloudera.com/documentation/enterprise/latest/topics/impala_resource_management.html