25、HCatalog与Hive在数据处理中的应用及案例分析

HCatalog与Hive在数据处理中的应用及案例分析

1. HCatalog命令行工具

HCatalog利用Hive的元数据存储,Hive用户可继续使用Hive命令行工具与之交互。对于非Hive用户的HCatalog用户,提供了 hcat 命令行工具,它与Hive命令行类似,但仅接受不会触发MapReduce作业的命令,支持大部分DDL操作,例如:

$ /usr/bin/hcat -e "create table rawevents (user string, url string);"

hcat 命令行支持以下选项:
| 选项 | 解释 | 示例 |
| ---- | ---- | ---- |
| -e | 执行命令行提供的DDL | hcat -e “show tables;” |
| -f | 执行脚本文件中提供的DDL | hcat -f setup.sql |
| -g | 见下面的安全部分 | |
| -p | 见下面的安全部分 | |
| -D | Cassandra服务器的端口 | hcat -Dlog.level=INFO |
| -h | Cassandra服务器的端口 | hcat -h |

HCatalog命令行不支持的SQL操作有:
- SELECT
- CREATE TABLE AS SELECT
- INSERT
- LOAD
- ALTER INDEX REBUILD
- ALTER TABLE CONCATENATE
- ALTER TABLE ARCHIVE
- ANALYZE TABLE
- EXPORT TABLE
- IMPORT TABLE

2. HCatalog安全模型

HCatalog不使用Hive的授权模型,但用户认证与Hive相同。Hive试图复制传统数据库授权模型,但在Hadoop生态系统中有局限性,因为可直接访问文件系统获取底层数据,Hive的授权受限。

一种解决方法是让运行Hive作业的用户拥有包含Hive数据的所有文件和目录,这样可防止其他用户读写数据,但会使Hive中的所有UDF以超级用户身份运行,可读写仓库中的所有文件。短期内可将UDF声明为特权操作,仅允许有适当权限的用户创建UDF,但目前无强制机制。

HCatalog将授权委托给存储层,对于存储在HDFS中的数据,HCatalog通过查看包含数据的目录和文件来判断用户是否有权限访问数据,若有则给予相同的元数据访问权限。这种方式的优点是真正安全,无法通过改变抽象级别破坏系统;缺点是HDFS支持的安全模型比传统数据库差,如无法实现列级权限,用户只能通过加入拥有文件的文件系统组来获得表的访问权限。

3. HCatalog架构

HCatalog使用MapReduce和Pig的标准输入输出机制与其交互。HCatLoader和HCatStorer分别基于HCatInputFormat和HCatOutputFormat,这两个MapReduce类在集成MapReduce和Hive元数据存储方面做了大量工作。

  • HCatInputFormat :与Hive元数据存储通信,获取要读取的表和分区信息,包括表架构和每个分区的架构,确定读取分区的实际输入格式和SerDe。调用 getSplits 时,为每个分区实例化一个输入格式并调用其 getSplits 方法,收集所有分区的分片并返回。同时,使用每个底层输入格式的RecordReader解码分区,HCatRecordReader通过与分区关联的SerDe将底层RecordReader的值转换为HCatRecords,处理缺失列和多余列。
  • HCatOutputFormat :与Hive元数据存储通信,确定写入的正确文件格式和架构。由于HCatalog仅支持以表当前指定的格式写入数据,无需为每个分区打开不同的输出格式。底层输出格式由HCatOutputFormat包装,为每个分区创建一个RecordWriter包装底层RecordWriter,使用指定的SerDe将数据写入新记录。所有分区写入完成后,HCatalog使用OutputCommitter将数据提交到元数据存储。

其架构流程图如下:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    A(HCatInputFormat):::process -->|获取表和分区信息| B(Hive元数据存储):::process;
    A -->|实例化输入格式| C(底层InputFormat):::process;
    C -->|获取分片| D(InputSplits):::process;
    C -->|解码分区| E(RecordReader):::process;
    E -->|转换为HCatRecords| F(HCatRecordReader):::process;
    G(HCatOutputFormat):::process -->|确定写入格式和架构| B;
    G -->|包装底层输出格式| H(底层OutputFormat):::process;
    H -->|创建RecordWriter| I(RecordWriter):::process;
    I -->|写入数据| J(新记录):::process;
    J -->|提交数据| K(OutputCommitter):::process;
    K -->|提交到元数据存储| B;
4. Hive在m6d公司的应用案例

m6d是一家展示广告潜在客户挖掘公司,每天参与数十亿次拍卖和数千万次广告展示,产生大量数据。公司的数据科学团队主要使用Hive处理这些日志数据,Hive可帮助提取和处理大量数据,但团队熟悉的数据科学工具难以处理Hive提取的大数据集。

团队中部分数据科学家主要使用R进行统计学习,R有丰富的统计学习包且团队经验丰富,但R默认将整个数据集加载到内存,这对m6d提取的大数据集是巨大限制,数据超出内存会导致系统交换和性能下降。

团队面临使用新的大数据集工具或对数据降采样以适应熟悉工具的选择。使用新工具可从更多数据中学习,降低估计方差,但学习新工具耗时且有机会成本;降采样可使用熟悉的工具,但会损失信号和增加估计方差。

在一个案例中,团队分析广告潜在客户转换概率与得分的关系。使用广义相加模型(GAM)在R中生成得分与转换概率的关系线,最初的方法是从Hive表 scoretable 中提取数据:

SELECT score,convert
FROM scoretable
WHERE date >= (…)  AND date <= (…)
AND offer = (…);

然后在R中运行以下代码:

library(mgcv)
g1=gam(convert~s(score),family=binomial,data=[data frame name])

但该方法只能处理有限天数的数据,处理300个广告活动的三天数据约需50小时。

通过改进,采用将得分四舍五入到百分位并使用GROUP BY查询获取频率权重的方法:

SELECT round(score,2) as score,convert,count(1) AS freq
        FROM scoretable
        WHERE date >= [start.date] and date <= [end.date] and offer = [chosen.offer]
        GROUP BY round(score,2),convert;

新数据加载到R后运行以下代码:

library(mgcv)
g2=gam(convert~s(score),family=binomial,weights=freq,
 data=[frequency weight data frame name])

这种方法能处理更多数据,速度大幅提升,处理300个广告活动的七天数据仅需50分钟,且估计更精确。

5. M6D UDF Pseudorank

在某些情况下,需要对数据分组并找出每组的前N个元素,Hive未实现 rank() 函数时,可创建用户定义函数 p_rank()

以下是示例数据:
| 类别 | 国家 | 产品 | 销售额 |
| ---- | ---- | ---- | ---- |
| movies | us | chewblanca | 100 |
| movies | us | war stars iv | 150 |
| movies | us | war stars iii | 200 |
| movies | us | star wreck | 300 |
| movies | gb | titanus | 100 |
| movies | gb | spiderella | 150 |
| movies | gb | war stars iii | 200 |
| movies | gb | war stars iv | 300 |
| office | us | red pens | 30 |
| office | us | blue pens | 50 |
| office | us | black pens | 60 |
| office | us | pencils | 70 |
| office | gb | rulers | 30 |
| office | gb | blue pens | 40 |
| office | gb | black pens | 50 |
| office | gb | binder clips | 60 |

在大多数SQL系统中,可使用以下查询找出每个类别和国家的前三个产品:

SELECT
 category,country,product,sales,rank
FROM (
 SELECT
   category,country,product, sales,
   rank() over (PARTITION BY category, country ORDER BY sales DESC) rank
 FROM p_rank_demo) t
WHERE rank <= 3

使用HiveQL实现相同结果的步骤如下:
1. 使用 DISTRIBUTE BY 子句对数据进行分区,确保相同类别和国家的行发送到同一个Reducer:

DISTRIBUTE BY
 category,
 country
  1. 使用 SORT BY 子句对每个组内的数据按销售额降序排序:
SORT BY
 category,
 country,
 sales DESC
  1. 完整查询如下:
ADD JAR p-rank-demo.jar;
CREATE TEMPORARY FUNCTION p_rank AS 'demo.PsuedoRank';
SELECT
 category,country,product,sales,rank
FROM (
 SELECT
   category,country,product,sales,
   p_rank(category, country) rank
 FROM (
   SELECT
     category,country,product,
     sales
   FROM p_rank_demo
   DISTRIBUTE BY
     category,country
   SORT BY
     category,country,sales desc) t1) t2
WHERE rank <= 3

p_rank() 函数的实现代码如下:

package demo;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.
  PrimitiveObjectInspectorFactory;

public class PsuedoRank extends GenericUDF {
 /**
  * The rank within the group. Resets whenever the group changes.
  */
 private long rank;
 /**
  * Key of the group that we are ranking. Use the string form
  * of the objects since deferred object and equals do not work
  * as expected even for equivalent values.
  */
 private String[] groupKey;

 @Override
 public ObjectInspector initialize(ObjectInspector[] oi)
     throws UDFArgumentException {
   return PrimitiveObjectInspectorFactory.javaLongObjectInspector;
 }

 @Override
 public Object evaluate(DeferredObject[] currentKey) throws HiveException {
   if (!sameAsPreviousKey(currentKey)) {
     rank = 1;
   }
   return new Long(rank++);
 }

 /**
  * Returns true if the current key and the previous keys are the same.
  * If the keys are not the same, then sets {@link #groupKey} to the
  * current key.
  */
 private boolean sameAsPreviousKey(DeferredObject[] currentKey)
     throws HiveException {
   if (null == currentKey && null == groupKey) {
     return true;
   }
   String[] previousKey = groupKey;
   copy(currentKey);
   if (null == groupKey && null != previousKey) {
     return false;
   }
   if (null != groupKey && null == previousKey) {
     return false;
   }
   if (groupKey.length != previousKey.length) {
     return false;
   }
   for (int index = 0; index < previousKey.length; index++) {
     if (!groupKey[index].equals(previousKey[index])) {
       return false;
     }
   }
   return true;
 }

 /**
  * Copies the given key to {@link #groupKey} for future
  * comparisons.
  */
 // 此处原文档未给出copy方法实现,可根据需求补充
}

p_rank() 函数可用于找出每个类别和国家的第10 - 15名畅销产品,或结合预计算的产品数量计算百分位数。需注意, p_rank() 不是 rank() 的直接替代品,在有平局情况时结果不同。

HCatalog与Hive在数据处理中的应用及案例分析

6. 操作步骤总结

在前面的内容中,我们介绍了HCatalog和Hive在数据处理中的多种应用,下面将相关操作步骤进行总结。

6.1 HCatalog命令行操作
  • 执行命令行提供的DDL :使用 -e 选项,示例: hcat -e “show tables;”
  • 执行脚本文件中提供的DDL :使用 -f 选项,示例: hcat -f setup.sql
  • 设置Cassandra服务器端口 :使用 -D 选项,示例: hcat -Dlog.level=INFO
6.2 m6d公司使用R分析数据操作
  • 初始数据提取与分析
    1. 从Hive表 scoretable 中提取数据:
SELECT score,convert
FROM scoretable
WHERE date >= (…)  AND date <= (…)
AND offer = (…);
2. 在R中运行代码:
library(mgcv)
g1=gam(convert~s(score),family=binomial,data=[data frame name])
  • 改进后的数据提取与分析
    1. 从Hive表 scoretable 中提取四舍五入后的得分及频率权重数据:
SELECT round(score,2) as score,convert,count(1) AS freq
        FROM scoretable
        WHERE date >= [start.date] and date <= [end.date] and offer = [chosen.offer]
        GROUP BY round(score,2),convert;
2. 在R中运行代码:
library(mgcv)
g2=gam(convert~s(score),family=binomial,weights=freq,
 data=[frequency weight data frame name])
6.3 使用HiveQL实现分组排名操作
  1. 数据分区 :使用 DISTRIBUTE BY 子句,示例:
DISTRIBUTE BY
 category,
 country
  1. 组内排序 :使用 SORT BY 子句,示例:
SORT BY
 category,
 country,
 sales DESC
  1. 完整查询
ADD JAR p-rank-demo.jar;
CREATE TEMPORARY FUNCTION p_rank AS 'demo.PsuedoRank';
SELECT
 category,country,product,sales,rank
FROM (
 SELECT
   category,country,product,sales,
   p_rank(category, country) rank
 FROM (
   SELECT
     category,country,product,
     sales
   FROM p_rank_demo
   DISTRIBUTE BY
     category,country
   SORT BY
     category,country,sales desc) t1) t2
WHERE rank <= 3
7. 不同应用场景的选择

在实际的数据处理中,我们需要根据不同的场景选择合适的方法。以下是一个简单的决策流程图:

graph TD
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    A(数据处理需求):::process -->|数据规模小| B(使用熟悉工具,如R):::process;
    A -->|数据规模大| C(考虑新工具或优化方法):::process;
    C -->|需要分组排名| D(使用HiveQL和p_rank函数):::process;
    C -->|分析数据关系| E(考虑使用频率权重优化R分析):::process;
    C -->|管理元数据| F(使用HCatalog):::process;

如果数据规模较小,使用熟悉的工具如R可以更高效地完成分析。当数据规模较大时,如果需要进行分组排名操作,可以使用HiveQL结合 p_rank 函数;如果是分析数据关系,可考虑使用频率权重优化R的分析过程;而在管理元数据方面,HCatalog是一个不错的选择。

8. 总结

通过以上内容,我们了解了HCatalog的命令行工具、安全模型和架构,以及Hive在m6d公司的应用案例,包括使用R进行数据统计分析和自定义函数实现分组排名。在实际应用中,我们可以根据数据规模、处理需求等因素选择合适的方法和工具。同时,通过优化操作步骤,如使用频率权重和自定义函数,可以提高数据处理的效率和准确性。希望这些内容能为你在数据处理和分析方面提供一些帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值