分布式文件系统实验 2019.1

本文内容适用于 Apache Hadoop 2.9.x,而3.x或更高版本未经过测试。

Day 1 创建服务器和 SSH 密钥

服务器 SSH 密钥对的装载和使用

OpenSSH 是 SSH(Secure SHell)协议的免费开源实现。SSH 协议族可以用来进行远程控制,或在计算机之间传送文件。Debian/Ubuntu Linux 上所使用的 SSH 服务器均为 OpenSSH 开源软件套件。

我们使用阿里云自动生成的 SSH 密钥对 🔑。密钥对分为“公钥”和“私钥”,阿里云自动生成的“公钥”会自动下发给阿里云的 Linux 虚拟机,而私钥会下发给您(也就是管理员)。您可以使用 SSH 客户端,如 Xshell、Putty 等 Windows 工具连接服务器。

由于,阿里云的管理系统自动给服务器装载密钥的过程只能在服务器关机时运行(向磁盘上的/root/.ssh/authorized_keys 写“公钥”文本文件)。所以,我们使用阿里云管理面板创建、更新、删除密钥的操作,需要重启服务器才能生效。当然,你也可以手动创建、删除密钥,了解具体的方法可以阅读一篇阿里云专门为用户编写的指导文档 连接实例概述

元旦 资源编排

集群视图

名称 域名 IP 位置 状态
node0 node0.trafficmgr.net 47.107.187.239 深圳
node1 node1.trafficmgr.net 47.107.145.3 深圳
node2 node2.trafficmgr.net 39.108.54.34 深圳
node3 node3.trafficmgr.net 39.108.56.9 深圳
node4 node4.trafficmgr.net 47.107.35.249 深圳
  • 主机命名策略: 以<Name>.<Domain>的形式组成完成的  主机名称, 更改主机名所使用的指令为 hostnamectl set-hostname <Name>.<Domain>,例如hostnamectl set-hostname node0.trafficmgr.net
  • SSH 密钥管理策略:node0 节点拥有所有从节点的 root 用户私钥, 而各节点上的 hadoop 用户  密钥在集群内共享

服务视图

Hadoop HBase ZooKeeper Hive
node0 ☑️ ☑️
node1 ☑️ ☑️ ☑️
node2 ☑️ ☑️ ☑️
node3 ☑️ ☑️ ☑️
node4 ☑️ ☑️ ☑️
  • 软件安装策略 :以上软件统一编排在/usr/local目录之下,如/usr/local/hadoop/usr/local/hbase

Hadoop

NameNode DataNode ResourceManager NodeManager
node0 ☑️ ☑️
node1 ☑️ ☑️
node2 ☑️ ☑️
node3 ☑️ ☑️
node4 ☑️ ☑️
  • 分布式文件系统的复制策略:在配置文件 hdfs-site.xml 中,dfs.replication 的值设置为 2

HBase

HRegionServer HMaster
node0
node1 ☑️ ☑️
node2 ☑️
node3 ☑️
node4 ☑️
  • HBase 部署四个节点的 RegionServer,与 Hadoop DataNode  节点保持一致, 理由是方便就近从 HDFS 读写数据。其中选一个作为 HMaster(任意选:在哪个虚拟机上运行 start-hbase.sh,那这个机器就成为 HMaster)。

Day 2 Hadoop: 建立单节点集群

目的

本文档介绍如何建立单个 Hadoop 节点,以便您可以快速上手 Hadoop MapReduce 和 Hadoop 分布式文件系统(HDFS)。

先决条件

支持的平台

  • 支持 GNU/Linux 作为开发和生产平台。 已经在具有 2000 个节点的 GNU/Linux 集群上演示了 Hadoop。
  • Windows 也是受支持的平台,但以下步骤仅适用于 Linux。 要在 Windows 上设置 Hadoop,请参阅Wiki 页面

必备软件

Linux 所需的软件包括:

  • 必须安装 Java™。 HadoopJavaVersions描述了推荐的 Java 版本。
  • 必须安装 ssh 并且必须运行 sshd 才能使用 Hadoop 脚本,管理远程 Hadoop 实例。

安装软件

如果您的群集没有必备软件,则您需要手动安装它们。

例如在 Ubuntu Linux 上:

sudo apt install ssh
sudo apt install rsync

安装 Oracle Java

我们需要安装 Oracle JDK(Oracle 分发的正式版),需要执行几个步骤。

首先,添加 Oracle 的 PPA,然后更新您的包存储库。

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update

然后,根据要安装的版本,执行以下命令之一

Oracle JDK 8

这是 Java 编写时的最新稳定版本,也是推荐的安装版本。 您可以使用以下命令执行此操作:

sudo apt-get install oracle-java8-installer

Oracle JDK 9

这是一个开发人员预览版,一般版本计划于 2017 年 3 月发布。不建议您使用此版本,因为可能仍存在安全问题和错误。 有关 Java 9 的更多信息,请访问 JDK 9 官方网站。

要安装 JDK 9,请使用以下命令:

sudo apt-get install oracle-java9-installer

安装完成

安装完成!让我们来检查一下版本:

java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

由此输出结果可以验证 OpenJDK 已经成功安装。

管理 Java

一台服务器上可以安装多个 Java。 您可以使用update-alternatives配置命令行中默认使用的版本,该版本管理哪些符号链接用于不同的命令。

sudo update-alternatives --config java

输出将如下所示。 在这种情况下,这是安装上面提到的所有 Java 版本的输出。

There are 2 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-8-oracle/jre/bin/java          1081      auto mode
  1            /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java   1081      manual mode
* 2            /usr/lib/jvm/java-8-oracle/jre/bin/java          1081      manual mode

Press <enter> to keep the current choice[*], or type selection number:

安装 Hadoop

Java 就绪之后,我们将访问Apache Hadoop Releases页面以查找最新的稳定版本。 这里我们规定了软件包版本:

在服务器上,我们将使用wget来获取它们:

wget https://mirrors.aliyun.com/apache/hadoop/common/hadoop-2.9.2/hadoop-2.9.2.tar.gz; \
wget https://mirrors.aliyun.com/apache/hbase/hbase-1.2.9/hbase-1.2.9-bin.tar.gz; \
wget https://mirrors.aliyun.com/apache/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz; \
wget https://mirrors.aliyun.com/apache/hive/hive-1.2.2/apache-hive-1.2.2-bin.tar.gz

上方的链接为阿里云的 Apache 镜像,因此您使用的 URL 可能与上面的 URL 不匹配。

我们将使用tar -xvzf命令来提取文件:

  • -x用于解包
  • -v用于详细输出
  • -z用于解压缩类型为gzip/bzip2/xz/lzma的压缩包
  • -f用于指定解压对象。可以使用 tab 自动补全文件名:
tar -xzvf hadoop-2.9.2.tar.gz; \
tar -xzvf hbase-1.2.9-bin.tar.gz; \
tar -xzvf zookeeper-3.4.13.tar.gz; \
tar -xzvf apache-hive-1.2.2-bin.tar.gz

最后,我们将提取的文件移动到/usr/local,这是本地安装软件的适当位置。

sudo mv hadoop-2.9.2 /usr/local/hadoop; \
sudo mv hbase-1.2.9 /usr/local/hbase; \
sudo mv zookeeper-3.4.13 /usr/local/zookeeper; \
sudo mv apache-hive-1.2.2-bin /usr/local/hive

设置 JAVA_HOME

Hadoop 要求您指定 Java 路径,可以在全局环境变量里定义,也可以在 Hadoop 配置文件里局部定义。

Java 的路径/usr/bin/java/etc/alternatives/java的符号链接,后者又是默认 Java 二进制文件的符号链接。 我们将使用命令readlink -f以递归方式跟踪路径的每个部分中的每个符号链接。 然后,我们将使用sed从输出中修剪bin/java,以便为JAVA_HOME提供正确的值。

查找默认 Java 路径:

readlink -f /usr/bin/java | sed "s:bin/java::"

输出结果:

/usr/lib/jvm/java-8-openjdk-amd64/jre/

您可以将此输出作为定值传递给 Hadoop 的JAVA_HOME变量。 或者,您可以在 Hadoop 配置文件中动态使用readlink命令,以便 Hadoop 自动使用设置为系统默认值的任何 Java 版本。

首先,打开hadoop-env.sh

sudo vi /usr/local/hadoop/etc/hadoop/hadoop-env.sh

现在,选择下列一种方法

方法 1:使用定值

 . . .
#export JAVA_HOME=${JAVA_HOME}
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre/
 . . .
 . . .
#export JAVA_HOME=${JAVA_HOME}
export JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:bin/java::")
 . . .

对于 Hadoop,hadoop-env.shJAVA_HOME的值将覆盖/etc/profile或用户配置文件中环境中设置的任何值。

可以使用类似的方法,编辑 HBase 的环境变量初始化脚本/usr/local/hbase/conf/hbase-env.sh

运行 Hadoop

现在我们应该能够运行 Hadoop:

/usr/local/hadoop/bin/hadoop

输出结果:

Usage: hadoop [--config confdir] [COMMAND | CLASSNAME]
  CLASSNAME            run the class named CLASSNAME
 or
  where COMMAND is one of:
  fs                   run a generic filesystem user client
  version              print the version
  jar <jar>            run a jar file
                       note: please use "yarn jar" to launch
                             YARN applications, not this command.
  checknative [-a|-h]  check native hadoop and compression libraries availability
  distcp <srcurl> <desturl> copy file or directories recursively
  archive -archiveName NAME -p <parent path> <src>* <dest> create a hadoop archive
  classpath            prints the class path needed to get the
                       Hadoop jar and the required libraries
  credential           interact with credential providers
  daemonlog            get/set the log level for each daemon
  trace                view and modify Hadoop tracing settings

Most commands print help when invoked w/o parameters.

输出了帮助文档,这意味着我们已成功配置 Hadoop 以独立模式运行。 我们将通过运行它附带的示例 MapReduce 程序来确保它正常运行。 为此,在我们的主目录中创建一个名为 input 的目录,并将 Hadoop 的配置文件复制到其中,以将这些文件用作我们的数据。

设置 PATH

我们想在终端中直接输入hadoop就能使用 Hadoop 程序,而不是要输入一串很长的绝对路径。所以,有一个办法是把 HADOOP 的绝对路径加入系统的环境变量PATH

首先,创建一个脚本/etc/profile.d/dfs.sh

sudo vi /etc/profile.d/dfs.sh

之后,写一段初始化脚本。例如

  • HADOOP_PREFIX的值是 Hadoop 的根目录
  • $HADOOP_PREFIX/bin是 Hadoop 的二进制程序目录
HADOOP_PREFIX=/usr/local/hadoop
ZOOKEEPER_PREFIX=/usr/local/zookeeper
HBASE_PREFIX=/usr/local/hbase
HIVE_PREFIX=/usr/local/hive
PATH=$PATH:$HADOOP_PREFIX/sbin:$HADOOP_PREFIX/bin:$ZOOKEEPER_PREFIX/bin:$HBASE_PREFIX/bin:$HIVE_PREFIX/bin

最后,从远程终端退出,重新登录用户后就生效了。现在我们应该能直接使用hadoophbasehive等命令。此后,如果输出了帮助文档,这意味着我们已成功配置环境变量PATH

Day 3 Hadoop: 集群设置

本文档相比官方文档Hadoop Cluster Setup,只是简单地安装和配置由 5 个节点组成的 Hadoop 集群。要使用 Hadoop,您可能首先要将其安装在一台计算机上(请参阅单节点设置)。

参考文档:

Hadoop 集群的体系结构

在配置主节点和从节点之前,了解 Hadoop 集群的不同组件非常重要。

  • NameNode:管理分布式文件系统并知道集群内存储的数据块的位置。
  • ResourceManager:管理 YARN 作业并负责在从属节点上调度和执行进程。

从节点存储实际数据并提供处理能力。它们将是node1node4,并将托管两个守护进程:

  • DataNode 管理物理存储在节点上的实际数据; 它的名字是 NameNode。
  • NodeManager 管理节点上的任务执行。

配置系统

添加 DNS

如果您使用了 DNS,就可以将以 5 台服务器添加到 DNS 记录。如果没有域名,您也可以在/etc/hosts添加您的服务器 IP 地址。

47.107.187.239  node0.trafficmgr.net
47.107.145.3    node1.trafficmgr.net
39.108.54.34    node2.trafficmgr.net
39.108.56.9     node3.trafficmgr.net
47.107.35.249   node4.trafficmgr.net

注意:在通过防火墙、NAT 设备时,请使用从设备的 DHCP 服务获取的 IPv4 地址,而不是经过 NAT 后的公网 IP 地址。

创建账户

主节点将使用ssh,通过密钥对身份验证连接到其他节点,以管理群集。

  1. 在每个节点上,创建名为hadoop的账户,用于集群管理。使用 su - hadoop 切换用户。
useradd -m hadoop
su - hadoop
  1. hadoop用户身份登录主节点node0,并生成 ssh 密钥对:
ssh-keygen

输出结果:

[email protected]:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hadoop/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/hadoop/.ssh/id_rsa.
Your public key has been saved in /home/hadoop/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:K8r50mcgGnulDCrhB1Ebff4lkQXxu5Ux3n5szZzPLiw [email protected]
The key's randomart image is:
+---[RSA 2048]----+
|    .    o=.     |
|   o . . o.      |
|  . o o   .. o   |
| . .   . . .o =  |
|  .     S o. + . |
|..o . o  o  o .+o|
|.o.* =...  . . oB|
|o.oo=o..o   E ooo|
|. ..+o.o     . o+|
+----[SHA256]-----+
  1. 将密钥复制到其他节点。将密钥复制到主节点node0本身也是一种好习惯,这样您也可以根据需要将其用作 DataNode。对于每个从节点,我们需要获取主节点的公钥并将其复制到每个从节点的authorized_keys文件中。

  2. 运行 cat,从主节的 hadoop 用户目录下的.ssh文件夹获取公钥,以打印到控制台:

[email protected]:~$ cat ~/.ssh/id_rsa.pub
  1. 现在登录每个从节点,并切换到用户 hadoop(su - hadoop),打开authorized_keys文件,之后粘贴从主节点拷贝的公钥到文件末尾并且保存。
[email protected]:~$ vi ~/.ssh/authorized_keys
  1. 在 hadoop 主节点上,您应该设置 ssh 配置文件,以包含相关节点的每个主机名。 使用 vi 打开配置文件进行编辑:
[email protected]:~$ vi ~/.ssh/config

您应该将文件修改为如下所示,并添加相关的 IP/域名和用户名。

Host node0
    HostName node0.trafficmgr.net
    User hadoop
    IdentityFile ~/.ssh/id_rsa
Host node1
    HostName node1.trafficmgr.net
    User hadoop
    IdentityFile ~/.ssh/id_rsa
Host node2
    HostName node2.trafficmgr.net
    User hadoop
    IdentityFile ~/.ssh/id_rsa
Host node3
    HostName node3.trafficmgr.net
    User hadoop
    IdentityFile ~/.ssh/id_rsa
Host node4
    HostName node4.trafficmgr.net
    User hadoop
    IdentityFile ~/.ssh/id_rsa

保存并关闭文件。现在应该可以从主节点node0,SSH 进入每个从节点node1~node4

Hadoop 文件夹权限

之前我们安装单节点的 Hadoop 集群时一直使用 root 用户操作,因此/usr/local/hadoop, /usr/local/hbase, /usr/local/hive, /usr/local/zookeeper 的主人都是 root 用户和组,hadoop 用户组无法访问。现在要将权限和组以递归的方式改为 hadoop。

sudo chown -R hadoop:hadoop /usr/local/hadoop; \
sudo chown -R hadoop:hadoop /usr/local/hbase; \
sudo chown -R hadoop:hadoop /usr/local/hive; \
sudo chown -R hadoop:hadoop /usr/local/zookeeper

配置主节点

设置 NameNode

在每个节点上更新 $HADOOP_PREFIX/etc/hadoop/core-site.xml,您要在端口 9000 上将 NameNode 位置设置为node0

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://node0.trafficmgr.net:9000</value>
    </property>
</configuration>

设置 HDFS 的路径

编辑 hdfs-site.xml:

vi $HADOOP_PREFIX/etc/hadoop/hdfs-site.xml
<configuration>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:///home/hadoop/data/nameNode</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:///home/hadoop/data/dataNode</value>
    </property>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
</configuration>

最后一个属性 dfs.replication 指示在群集中复制数据的次数。 您可以设置 2 以在两个节点上复制所有数据。 请勿输入高于实际从属节点数的值。

将 YARN 设置为 Job Scheduler

  1. $HADOOP_PREFIX/etc/hadoop中,将mapred-site.xml.template重命名为mapred-site.xml:
cd $HADOOP_PREFIX/etc/hadoop
mv mapred-site.xml.template mapred-site.xml

编辑文件,将 yarn 设置为 MapReduce 操作的默认框架:

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

配置 YARN

编辑yarn-site.xml:

vi $HADOOP_PREFIX/etc/hadoop/yarn-site.xml
<configuration>
    <property>
        <name>yarn.acl.enable</name>
        <value>0</value>
    </property>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>node0.trafficmgr.net</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
</configuration>

配置 Slaves

启动脚本使用文件slaves在所有节点上启动所需的守护程序。 编辑$HADOOP_PREFIX/etc/hadoop/slaves为:

node1.trafficmgr.net
node2.trafficmgr.net
node3.trafficmgr.net
node4.trafficmgr.net

配置内存分配

低 RAM 节点上的内存分配可能很棘手,因为默认值不适合 RAM 少于 8GB 的节点。本节将重点介绍如何分配内存以适用于 MapReduce 作业,并提供 2GB RAM 节点的示例配置。

内存分配属性

执行 YARN 作业的两类资源:

  • Application Master (AM) 负责监视应用程序并协调群集中的分布式执行程序。
  • 实际上处理该作业的执行程序由 AM 创建。对于 MapReduce 作业,它们将并行执行 map 或 reduce 操作。

两者都在从属节点上的容器中运行。每个从属节点都运行一个 NodeManager 守护程序,该守护程序负责在节点上创建容器。整个集群由 ResourceManager 管理,ResourceManager 根据容量要求和当前开销调度所有从节点上的容器分配。

需要正确配置四种类型的资源分配才能使群集正常工作。 这些是:

  1. 为单节点上的 YARN 容器分配多少内存。这个限制应该高于所有其他限制;否则,容器分配将被拒绝,应用程序将失败。但是,它不应该占用节点上全部的 RAM。

    此值在yarn-site.xml中使用yarn.nodemanager.resource.memory-mb配置。

  2. 单个容器可以消耗多少内存以及允许的最小内存分配。容器永远不会大于最大值,否则分配将失败并始终被分配为最小 RAM 量的倍数。

    这些值在yarn-site.xml中配置,使用yarn.scheduler.maximum-allocation-mbyarn.scheduler.minimum-allocation-mb进行限制。

  3. 将为 ApplicationMaster 分配多少内存。这是一个常量值,应该适合容器的最大大小。

    这是在mapred-site.xml中使用yarn.app.mapreduce.am.resource.mb配置的。

  4. 将为每个映射分配多少内存或减少操作。这应该小于最大尺寸。

所有这些属性之间的关系如下图所示:

Hadoop Memory Allocation

2GB 节点的示例配置

对于 2GB 节点的一个可行的配置方案:

Property Value
yarn.nodemanager.resource.memory-mb 1536
yarn.scheduler.maximum-allocation-mb 1536
yarn.scheduler.minimum-allocation-mb 128
yarn.app.mapreduce.am.resource.mb 512
mapreduce.map.memory.mb 256
mapreduce.reduce.memory.mb 256
  1. 编辑$HADOOP_PREFIX/etc/hadoop/yarn-site.xml并添加以下行:
<property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>1536</value>
</property>
<property>
    <name>yarn.scheduler.maximum-allocation-mb</name>
    <value>1536</value>
</property>
<property>
    <name>yarn.scheduler.minimum-allocation-mb</name>
    <value>128</value>
</property>
<property>
    <name>yarn.nodemanager.vmem-check-enabled</name>
    <value>false</value>
</property>

最后一个属性禁用虚拟内存检查,可以防止在 JDK8 上正确分配容器。

  1. 编辑$HADOOP_PREFIX/etc/hadoop/mapred-site.xml并添加以下行:
<property>
    <name>yarn.app.mapreduce.am.resource.mb</name>
    <value>512</value>
</property>
<property>
    <name>mapreduce.map.memory.mb</name>
    <value>256</value>
</property>
<property>
    <name>mapreduce.reduce.memory.mb</name>
    <value>256</value>
</property>

在每个节点上分发配置文件

将 Hadoop 配置文件复制到从属节点:

for node in node1 node2 node3 node4; do
    scp $HADOOP_PREFIX/etc/hadoop/* $node:$HADOOP_PREFIX/etc/hadoop/;
done

格式化 HDFS

HDFS 需要像任何经典文件系统一样进行格式化。 在node0上,运行以下命令:

hdfs namenode -format

您的 Hadoop 安装现已配置并准备运行。

运行并监控 HDFS

本节将介绍如何在 NameNode 和 DataNodes 上启动 HDFS,并监控所有内容是否正常工作以及与 HDFS 数据交互。

启动和停止 HDFS

  1. 通过从node0运行以下脚本来启动 HDFS:
start-dfs.sh

根据 slave 配置文件中的配置,它将在node0上启动 NameNode 和 SecondaryNameNode,在node1~node4上启动 DataNode。

  1. 在每个节点上使用 jps 命令检查每个进程是否正在运行。首先查看node0(PID 会有所不同):
9830 Jps
3657 NameNode
3902 SecondaryNameNode

node1~node4上:

8196 Jps
7291 DataNode
  1. 要在主节点和从节点上停止 HDFS,请从node0运行以下命令:
stop-dfs.sh

监控您的 HDFS 群集

您可以使用hdfs dfsadmin命令获取有关运行 HDFS 群集的有用信息。 例如:

hdfs dfsadmin -report

这将打印所有正在运行的 DataNode 的信息(例如,容量和使用情况)。 要获取所有可用命令的描述,请键入:

hdfs dfsadmin -help

您还可以使用更友好的 Web 用户界面。 请在在浏览器打开 http://<IP-or-Domain>:50070,您将获得一个用户友好的监控控制台。

HDFS Overview

将数据存入 HDFS

使用命令hdfs dfs写入和读取 HDFS。首先,手动创建主目录。所有其他命令将使用相对于此默认主目录的路径:

hdfs dfs -mkdir -p /user/hadoop

让我们以Gutenberg 项目中的一些教科书为例。

在 HDFS 中创建 books 目录。以下命令将在主目录/user/hadoop/books中创建它:

hdfs dfs -mkdir books

从 Gutenberg 项目中获取一些书籍:

cd /home/hadoop
wget -O alice.txt https://www.gutenberg.org/files/11/11-0.txt
wget -O holmes.txt https://www.gutenberg.org/ebooks/1661.txt.utf-8
wget -O frankenstein.txt https://www.gutenberg.org/ebooks/84.txt.utf-8

将三本书通过 HDFS 放在books目录中:

hdfs dfs -put alice.txt holmes.txt frankenstein.txt books

列出图书目录的内容:

hdfs dfs -ls books

将其中一本书移到本地文件系统:

hdfs dfs -get books/alice.txt

您也可以直接从 HDFS 打印书籍:

hdfs dfs -cat books/alice.txt

有许多命令可以管理您的 HDFS。有关完整列表,您可以查看Apache HDFS shell 文档,或者打印以下帮助:

hdfs dfs -help

🎉 2019 新年快乐!

运行 YARN

HDFS 是一个分布式存储系统,它不为集群中的运行和调度任务提供任何服务。这是 YARN 框架的作用。以下部分介绍如何启动,监控和向 YARN 提交作业。

启动和停止 YARN

  1. 使用脚本启动 YARN:
start-yarn.sh
  1. 使用 jps 命令检查一切是否正在运行。除了以前的 HDFS 守护程序,您应该在 node0 上看到 ResourceManager,在 node1 和 node2 上看到 NodeManager。

  2. 要停止 YARN,请在 node0 上运行以下命令:

stop-yarn.sh

监控 YARN

  1. yarn命令提供管理 YARN 群集的实用程序。您还可以使用以下命令打印正在运行的节点的报告:
yarn node -list

同样,您可以使用以下命令获取正在运行的应用程序列表:

yarn application -list

要获取yarn命令的所有可用参数,请参阅Apache YARN 文档

  1. 与 HDFS 一样,YARN 提供了更友好的 Web UI,默认情况下在资源管理器的端口 8088 上启动。将浏览器指向http://<IP-or-Domain>:8088并浏览 UI:

YARN Web UI的屏幕截图

将 MapReduce 作业提交给 YARN

将 Yarn 作业打包到jar文件中并提交给 YARN,以便使用命令 yarn jar 执行。 Hadoop 安装包提供了可以运行以测试集群的示例应用程序。您将使用它们在之前上传到 HDFS 的三本书上运行字数统计。

将样品罐提交给 YARN。在node0上,运行:

yarn jar ~/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.1.jar wordcount "books/*" output

最后一个参数是保存作业的输出 - 在 HDFS 中。

作业完成后,您可以通过使用hdfs dfs -ls output查询 HDFS 来获得结果。如果成功,输出将类似于:

Found 2 items
-rw-r--r--   1 hadoop supergroup          0 2017-10-11 14:09 output/_SUCCESS
-rw-r--r--   1 hadoop supergroup     269158 2017-10-11 14:09 output/part-r-00000

打印结果:

hdfs dfs -cat output/part-r-00000

Day 4 ZooKeeper

ZooKeeper 安装

使用包管理器安装 ZooKeeper:

apt install zookeeperd

ZooKeeper 集群

  1. 在独立模式下运行 ZooKeeper 便于评估,开发和测试。 但在生产中,您应该以复制模式运行 ZooKeeper。 同一应用程序中的复制服务器组称为仲裁,在复制模式下,仲裁中的所有服务器都具有相同配置文件的副本。 该文件类似于独立模式中使用的文件,但有一些差异。 编辑配置文件 /etc/zookeeper/conf/zoo.cfg
server.1=node1.trafficmgr.net:2888:3888
server.2=node2.trafficmgr.net:2888:3888
server.3=node3.trafficmgr.net:2888:3888
  1. 将修改后的 HBase 配置文件下发到各节点:
myid=1
for node in node1 node2 node3; do
    echo $[myid++] > /etc/zookeeper/myid; \
    scp /etc/zookeeper/* $node:/etc/zookeeper;
done

HBase 配置

  1. 编辑 $HBASE_PREFIX/conf/hbase-site.xml
<configuration>
 <property>
    <name>hbase.rootdir</name>
    <value>hdfs://node0.trafficmgr.net:9000/hbase</value>
  </property>
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>
  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>node1.trafficmgr.net,node2.trafficmgr.net,node3.trafficmgr.net</value>
  </property>
</configuration>
  1. 编辑 $HBASE_PREFIX/conf/regionservers
node1.trafficmgr.net
node2.trafficmgr.net
node3.trafficmgr.net
node4.trafficmgr.net
  1. 将修改后的 HBase 配置文件下发到各节点:
for node in node1 node2 node3 node4; do
    scp $HBASE_PREFIX/conf/* $node:$HBASE_PREFIX/conf/;
done

运行并监控 HBase

本节将介绍如何在各节点上启动 HMaster 和 HRegionServer,并监控所有内容是否正常工作。

启动和停止 HBase

  1. 通过从node1运行以下脚本来启动 HBase:
start-hbase.sh

根据 regionservers 配置文件中的配置,它将在node1上启动 HMaster 和 HRegionServer,在node1~node4上只启动 HRegionServer。

  1. 在每个节点上使用 jps 命令检查每个进程是否正在运行。首先查看node1(PID 会有所不同):
17264 Jps
2738 HMaster
2439 NodeManager
2301 DataNode
1982 HRegionServer

node1~node4上:

8496 Jps
1556 NodeManager
1257 HRegionServer
1418 DataNode
  1. 要在各节点上停止 HBase,请从node1运行以下命令:
stop-hbase.sh

Day 5 HBase 数据库

package net.trafficmgr.api;

import java.io.*;
import java.util.*;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.compress.Compression;
import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
import org.apache.hadoop.hbase.util.Bytes;

public class HBase {

    private static Configuration configuration;
    private static Connection connection;
    private static Admin admin;
    private static Set<String> set;
    private static String[] fileQueue = {"user_info.csv", "user_hobby.csv", "user_friend.csv"};
    private static String mTableName = "user_info";
    private static String[] colFamily = {"des", "hob", "fri"};
    private static String[] des = {"", "name", "gender", "age", "addr"};
    private static String[] hob = {"", "h1", "h2", "h3", "h4"};
    private static String[] fri = {"", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9"};
    private static String[][] column = {des, hob, fri};

    public HBase() throws IOException {
        // 初始化配置文件,打开连接
        configuration = HBaseConfiguration.create();
        String path = this.getClass()
                .getClassLoader()
                .getResource("hbase-site.xml")
                .getPath();
        configuration.addResource(new Path(path));
        connection = ConnectionFactory.createConnection(configuration);
        admin = connection.getAdmin();
        set = new HashSet<>();

        // 创建表
        createTable(mTableName, colFamily);

        // 准备数据
        insetData(mTableName, colFamily, column, fileQueue);

        // 关闭连接
        if (admin != null) {
            admin.close();
        }
        if (connection != null) {
            connection.close();
        }

    }

    /**
     * Function 创建表
     *
     * @param myTableName
     * @param colFamily
     * @throws IOException
     */
    private static void createTable(String myTableName, String[] colFamily) throws IOException {
        TableName tableName = TableName.valueOf(myTableName);
        if (admin.tableExists(tableName)) {
            System.out.println("table is exists!");
        } else {
            TableDescriptorBuilder tableDescBuilder = TableDescriptorBuilder.newBuilder(tableName);
            for (String col : colFamily) {
                ColumnFamilyDescriptorBuilder columnFamilyDescBuilder = ColumnFamilyDescriptorBuilder
                        .newBuilder(Bytes.toBytes(col)).setBlocksize(32 * 1024)
                        .setCompressionType(Compression.Algorithm.SNAPPY)
                        .setDataBlockEncoding(DataBlockEncoding.NONE);
                tableDescBuilder.setColumnFamily(columnFamilyDescBuilder.build());
                tableDescBuilder.build();
            }
        }
    }

    /**
     * Function 准备数据
     *
     * @param mTableName
     * @param colFamily
     * @param column
     * @param fileQueue
     * @throws IOException
     */
    private static void insetData(String mTableName, String[] colFamily, String[][] column, String[] fileQueue) throws IOException {
        for (int type = 0; type < colFamily.length; type++) {
            BufferedReader bf = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fileQueue[type]))));
            bf.readLine();
            String str;
            while ((str = bf.readLine()) != null) {
                String[] s = str.split(",");
                for (int i = 1; i < s.length; i++) {
                    insertData(mTableName, s[0], colFamily[0], column[type][i], s[i]);
                }
                set.add(s[0]);
            }
            bf.close();
        }
    }

    /**
     * Function 插入数据
     *
     * @param tableName
     * @param rowKey
     * @param colFamily
     * @param col
     * @param val
     * @throws IOException
     */
    private static void insertData(String tableName, String rowKey, String colFamily, String col, String val) throws IOException {
        Table table = connection.getTable(TableName.valueOf(tableName));
        Put put = new Put(rowKey.getBytes());
        put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes());
        table.put(put);
        table.close();
    }
}

Day 6 Hive SQL 数据处理

Hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的 SQL 查询功能,可以将 SQL 语句转换为 MapReduce 任务进行运行。其优点是学习成本低,可以通过类 SQL 语句快速实现简单的 MapReduce 统计,不必开发专门的 MapReduce 应用,十分适合数据仓库的统计分析。

Hive 的配置方法可以看官方文档Quick Start Guide

数据源

使用 Hive 客户端

  1. 初始化 Hive 服务器。Hive 服务除了要使用 HDFS 文件系统以外,还要自己管理元数据库。元数据库是 Hive 的核心组件,它可以使用 Derby 或者 MySQL 作为自己的元数据库引擎。其中 Derby 轻量化的单线程数据库引擎,MySQL 则更时候生产环境下使用的多线程数据引擎。由于实验简单,我们只需要用 Derby 引擎即可。
schematool -dbType derby -initSchema
  1. Hive 服务由服务端和客户端组成,其中beeline是客户端,hiveserver2是服务器。我们首先要启动hiveserver2,让其常驻后台运行。
nohup hiveserver2 &
  1. 使用 beeline 客户端登录 Hive
# 直接键入名称
beeline
# 连接到本地的hiveserver2服务器,默认端口是10000
!connect jdbc:hive2://localhost:10000
# 当询问登陆用户名时,我们就使用HDFS文件系统的管理用户名hadoop

用 SQL 处理数据

  1. 预先处理:塞选出甘肃、湖南江苏、辽宁的用户数据
-- 创建表
CREATE TABLE user_info(uid string, name string, gender string, age string, city string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
CREATE TABLE user_hobby(uid string, h1 string, h2 string, h3 string, h4 string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
CREATE TABLE user_friend(uid string, f1 string, f2 string, f3 string, f4 string, f5 string, f6 string, f7 string, f8 string, f9 string) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
-- 导入数据
LOAD DATA LOCAL INPATH 'user_info.csv' OVERWRITE INTO TABLE user_info;
LOAD DATA LOCAL INPATH 'user_friends.csv' OVERWRITE INTO TABLE user_friend;
LOAD DATA LOCAL INPATH 'user_hobby.csv' OVERWRITE INTO TABLE user_hobby;
-- 选出甘肃、湖南、江苏、辽宁的社交用户兴趣
CREATE TABLE user_hobby_locate_1 COMMENT '甘肃+湖南+江苏+辽宁' AS SELECT user_hobby.*, user_info.city from user_hobby INNER JOIN user_info on user_info.uid = user_hobby.uid WHERE city='甘肃' OR city='湖南' OR city='江苏' OR city='辽宁';
-- 选出甘肃、湖南、江苏、辽宁的社交用户好友
CREATE TABLE user_friend_locate_1 COMMENT '甘肃+湖南+江苏+辽宁' AS SELECT user_friend.*, user_info.city from user_friend INNER JOIN user_info on user_info.uid = user_friend.uid WHERE city='甘肃' OR city='湖南' OR city='江苏' OR city='辽宁';
  1. 有两个相同兴趣的人群,任务摘要:
  • 使用一个简单的排列组合方法从 6 种兴趣爱好中选出有两个共同兴趣爱好的人群
  • 合并生成的 6 张表,得到输出结果common_hobby_00
-- 聚合相同兴趣的用户
CREATE TABLE common_hobby_12 COMMENT '聚合H1和H2' AS SELECT collect_list(uid) uid, h1, h2 from user_hobby_locate_1 GROUP BY h1, h2;
CREATE TABLE common_hobby_13 COMMENT '聚合H1和H3' AS SELECT collect_list(uid) uid, h1, h3 from user_hobby_locate_1 GROUP BY h1, h3;
CREATE TABLE common_hobby_14 COMMENT '聚合H1和H4' AS SELECT collect_list(uid) uid, h1, h4 from user_hobby_locate_1 GROUP BY h1, h4;
CREATE TABLE common_hobby_23 COMMENT '聚合H2和H3' AS SELECT collect_list(uid) uid, h2, h3 from user_hobby_locate_1 GROUP BY h2, h3;
CREATE TABLE common_hobby_24 COMMENT '聚合H2和H4' AS SELECT collect_list(uid) uid, h2, h4 from user_hobby_locate_1 GROUP BY h2, h4;
CREATE TABLE common_hobby_34 COMMENT '聚合H3和H4' AS SELECT collect_list(uid) uid, h3, h4 from user_hobby_locate_1 GROUP BY h3, h4;
-- 将6个分组聚合到1个表
CREATE TABLE common_hobby_00 LIKE common_hobby_12;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_12;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_13;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_14;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_23;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_24;
INSERT INTO common_hobby_00 SELECT * FROM common_hobby_34;
-- 在单个聚合组中找出这两个爱好的用户群体
SELECT * FROM common_hobby_00 WHERE h1='轮滑' AND h2='木雕';
  1. 二度朋友,任务摘要:
  • 首先将每个用户的朋友列表数组化 friend_array
  • 创建朋友列表的反向组合 friend_array_reverse
  • 合并两个表的组合到 friend_array
  • 使用爆破函数将列表拆分,选择出最后的共同朋友列表 friend_final
-- 将朋友列表组合成数组
CREATE TABLE friend_array COMMENT '朋友数组' AS SELECT uid, Array(f1, f2, f3, f4, f5, f6, f7, f8, f9) friends FROM user_friend_locate_1;
-- 反向朋友列表组合
CREATE TABLE friend_array_reverse COMMENT '反向朋友数组' AS SELECT x.col, collect_list(x.uid) user_friend_locate_1 FROM (SELECT tf.*, t.uid FROM friend_array t lateral view explode(t.friends) tf AS col) x GROUP BY x.col;
-- 将两个表合并到一起
INSERT INTO friend_array SELECT * FROM friend_array_reverse;
-- 先爆炸💥,再组合
CREATE TABLE friend_final COMMENT '最终朋友表' AS SELECT x.uid, collect_list(x.friend) friends FROM (SELECT t.uid, tf.* FROM friend_array t lateral view explode(t.friends) tf as friend) x GROUP BY x.uid;
  1. 展现数据
-- 🎉 展现所有的表
SHOW TABLES;
-- 🎉 展现出塞选地区后的用户朋友信息
SELECT * FROM user_friend_locate_1 LIMIT 10,10;
-- 🎉 展现出塞选地区后的用户爱好信息
SELECT * FROM user_hobby_locate_1 LIMIT 10,10;
-- 🎉 聚合的相同兴趣用户的最终结果表
SELECT * FROM common_hobby_00 LIMIT 10,10;
-- 🎉 在单个聚合组中找出这两个爱好的用户群体
SELECT * FROM common_hobby_00 WHERE hobby1='乒乓球' AND hobby2='玄幻小说';
-- 🎉 朋友列表组合成数组
SELECT * FROM friend_array LIMIT 10,10;
-- 🎉 反向朋友列表组合
SELECT * FROM friend_array_reverse LIMIT 10,10;
-- 🎉 爆破💥,再组合后的表
SELECT * FROM friend_final LIMIT 10,10;
  1. 输出结果
hive> -- 🎉 展现所有的表
hive> SHOW TABLES;
OK
common_hobby_00
common_hobby_12
common_hobby_13
common_hobby_14
common_hobby_23
common_hobby_24
common_hobby_34
friend_array
friend_array_reverse
friend_final
user_friend
user_friend_locate_1
user_hobby
user_hobby_locate_1
user_info
Time taken: 1.483 seconds, Fetched: 15 row(s)
hive> -- 🎉 展现出塞选地区后的用户朋友信息
hive> SELECT * FROM user_friend_locate_1 LIMIT 10,10;
OK
10018	10403	10847	11582	11980	12755	12957	13596	10117	14220	甘肃
10021	10316	10777	11535	11671	12544	13046	13715	10246	14233	甘肃
10023	10559	11136	11762	11764	12252	13140	13671		14302	江苏
10024	10317	11311	12002	11682	12405	13168	13935		14061	甘肃
10026	10356	11112	11440	11246	12322	13099	13756	10074	14434	江苏
10028	10581	10896	12112	11546	12649	13050	13763			湖南
10030	10510	11332	11975	11862	12469	12840	13548	10140	14435	湖南
10031	10646	11370	11439	11715	12142	13290	13614		14124	辽宁
10032	10371	11061	11544	11716	12487	13233	13606	10015		辽宁
10033	10653	11371	12043	11357	12035	12945	13310	10034		湖南
Time taken: 0.913 seconds, Fetched: 10 row(s)
hive> -- 🎉 展现出塞选地区后的用户爱好信息
hive> SELECT * FROM user_hobby_locate_1 LIMIT 10,10;
OK
10018	足球	排球	做菜	蒙古舞	甘肃
10021	羽毛球	养花	弹钢琴	架子鼓	甘肃
10023	篮球	轮滑	做菜	竹雕	江苏
10024	羽毛球	游泳	玄幻小说	传统民歌	甘肃
10026	篮球	长跑	做菜	木雕	江苏
10028	篮球	排球	做菜		湖南
10030	篮球	轮滑	手机游戏	广场舞	湖南
10031	乒乓球	长跑	网络直播	滚铁环	辽宁
10032	乒乓球	散打	滑板	滚铁环	辽宁
10033	篮球	排球	历史小说	滚铁环	湖南
Time taken: 0.062 seconds, Fetched: 10 row(s)
hive> -- 🎉 聚合的相同兴趣用户的最终结果表
hive> SELECT * FROM common_hobby_00 LIMIT 10,10;
OK
["10038","10233","10330","10690","10781","11044","11216","11251","11542","11563","11691","11788","12050","12105","12249","12416","12578","12603","12607","12669","12678","13126","13188","13226","13252","13334","13383","13468","13698","13730","13788","13876","13896","14235","14286","14454","14461","14466","14482"]	台球	健身
["10293","10333","10370","10401","10569","11060","11280","11338","11341","11346","11378","11607","11639","11699","11807","11901","11986","12117","12320","12328","12433","12492","12586","12633","12665","12832","12865","12876","12938","13056","13080","13236","13476","13508","13609","13704","13711","13727","13737","14039","14070","14301","14337"]	台球	养花
["10051","10432","10560","10627","10635","10749","10953","11328","11507","11522","11582","11621","11681","11796","11921","11955","12169","12211","12512","12723","12795","12857","12892","12991","13350","13598","13618","13634","13644","13676","13685","13912","13926","13945","14183","14313"]	台球	养金鱼
["10001","10017","10154","10161","10612","10670","10813","11098","11117","11244","11442","11519","11679","12029","12233","12380","12388","12546","12571","12600","12624","12649","13069","13074","13187","13649","13837","13990","14076","14097","14137","14410","14503","14540"]	台球	排球
["10040","10087","10103","10214","10300","10581","10652","10742","11029","11217","11453","11484","11677","11772","11942","12125","12406","12470","12478","12755","12803","12812","13160","13276","13387","13445","13489","13694","13699","13894","14190","14214","14452","14528"]	台球	散打
["10096","10124","10272","10292","10411","10609","10642","10695","11073","11076","11083","11205","11253","11294","11314","11628","11797","11884","12318","12446","12485","12643","13118","13139","13494","13636","13797","13915","14038","14206","14342","14405"]	台球	游泳
["10223","10424","10451","10509","10820","10873","10881","11201","11215","11351","12455","12659","12682","12750","12880","13057","13084","13166","13170","13246","13291","13297","13444","13446","13596","13709","13864","13877","14083","14123","14267","14290","14381","14545"]	台球	溜溜球
["10098","10105","10230","10410","10441","10503","10542","10579","10610","10638","10709","10712","10726","10913","11031","11148","11164","11181","11210","11278","11514","11872","11948","11976","12047","12165","12358","12451","12484","12652","12749","12826","12934","12950","13461","13991","14091","14182","14224","14229","14385"]	台球	看电影
["10039","10063","10314","10550","10701","10705","10806","10845","10876","10922","11059","11581","11780","11798","11935","12019","12155","12361","12384","12525","12722","12725","13215","13273","13337","13369","13492","13497","13584","13678","13948","14257"]	台球	轮滑
["10048","10054","10115","10389","10473","10637","11119","11817","11871","12043","12147","12236","12437","12480","12594","12867","12937","13021","13046","13051","13066","13143","13259","13386","13402","13424","13519","13532","13577","13663","13953","14351","14400","14407","14448","14526","14536"]	台球	长跑
Time taken: 0.053 seconds, Fetched: 10 row(s)
hive> -- 🎉 在单个聚合组中找出这两个爱好的用户群体
hive> SELECT * FROM common_hobby_00 WHERE hobby1='乒乓球' AND hobby2='玄幻小说';
FAILED: SemanticException [Error 10004]: Line 1:36 Invalid table alias or column reference 'hobby1': (possible column names are: uid, h1, h2)
hive> -- 🎉 朋友列表组合成数组
hive> SELECT * FROM friend_array LIMIT 10,10;
OK
10018	["10403","10847","11582","11980","12755","12957","13596","10117","14220"]
10021	["10316","10777","11535","11671","12544","13046","13715","10246","14233"]
10023	["10559","11136","11762","11764","12252","13140","13671","","14302"]
10024	["10317","11311","12002","11682","12405","13168","13935","","14061"]
10026	["10356","11112","11440","11246","12322","13099","13756","10074","14434"]
10028	["10581","10896","12112","11546","12649","13050","13763","",""]
10030	["10510","11332","11975","11862","12469","12840","13548","10140","14435"]
10031	["10646","11370","11439","11715","12142","13290","13614","","14124"]
10032	["10371","11061","11544","11716","12487","13233","13606","10015",""]
10033	["10653","11371","12043","11357","12035","12945","13310","10034",""]
Time taken: 0.049 seconds, Fetched: 10 row(s)
hive> -- 🎉 反向朋友列表组合
hive> SELECT * FROM friend_array_reverse LIMIT 10,10;
OK
10010	["10428","12811"]
10011	["12062","13997","14301","14306"]
10012	["12402"]
10013	["11457"]
10014	["11729","11808"]
10015	["10032","10232","10411","12957","13114"]
10016	["11199"]
10017	["10437","10568","11780","12681","12794","12813"]
10018	["11515","12951"]
10019	["12132","13143","14404"]
Time taken: 0.041 seconds, Fetched: 10 row(s)
hive> -- 🎉 爆破💥,再组合后的表
hive> SELECT * FROM friend_final LIMIT 10,10;
OK
10010	["10485","11003","11837","11680","12270","12829","13478","10178","14036","10428","12811"]
10011	["12062","13997","14301","14306"]
10012	["12402"]
10013	["11457"]
10014	["10397","10967","11860","11480","12036","12812","13860","","14408","11729","11808"]
10015	["10476","11003","11735","11483","12325","12903","13645","10041","14183","10032","10232","10411","12957","13114"]
10016	["10694","11034","11917","11632","12236","13120","13771","10225","14112","11199"]
10017	["10670","10901","11584","11555","12605","12887","13506","","14369","10437","10568","11780","12681","12794","12813"]
10018	["10403","10847","11582","11980","12755","12957","13596","10117","14220","11515","12951"]
10019	["12132","13143","14404"]
Time taken: 0.048 seconds, Fetched: 10 row(s)
hive>