根据指定日期范围限制Pig Latin中日志文件的加载

0 投票
1 回答
775 浏览
提问于 2025-04-17 15:56

我在根据参数输入加载日志文件时遇到了问题,想请教一下有没有人能给点建议。这些日志是Omniture日志,存放在按年、月、日分类的子目录中(比如说 /year=2013/month=02/day=14),文件名里也有日期戳。对于任何一天,可能会有多个日志文件,每个文件的大小都在几百MB。

我有一个Pig脚本,目前可以处理整个月的日志,月份和年份是作为脚本参数指定的(比如说 /year=$year/month=$month/day=*)。这个脚本运行得很好,我们对此很满意。不过,我们想改成按周处理日志,这样之前的加载路径就不适用了(因为一周可能跨越一个月甚至一年)。为了解决这个问题,我写了一个Python的用户定义函数(UDF),它接收一个开始日期,然后生成一周日志所需的路径模式,比如:

>>> log_path_regex(2013, 1, 28)
'{year=2013/month=01/day=28,year=2013/month=01/day=29,year=2013/month=01/day=30,year=2013/month=01/day=31,year=2013/month=02/day=01,year=2013/month=02/day=02,year=2013/month=02/day=03}'

这个路径模式会被插入到合适的路径中:

> %declare omniture_log_path 's3://foo/bar/$week_path/*.tsv.gz';
> data = LOAD '$omniture_log_path' USING OmnitureTextLoader(); // See http://github.com/msukmanowsky/OmnitureTextLoader

不幸的是,我完全搞不懂如何根据脚本参数 $year、$month 和 $day 来填充 $week_path。我尝试使用 %declare,但grunt总是报错,说它在记录日志,但从来没有实际记录:

> %declare week_path util.log_path_regex(year, month, day);
2013-02-14 16:54:02,648 [main] INFO  org.apache.pig.Main - Apache Pig version 0.10.1 (r1426677) compiled Dec 28 2012, 16:46:13
2013-02-1416:54:02,648 [main] INFO  org.apache.pig.Main - Logging error messages to: /tmp/pig_1360878842643.log % ls  /tmp/pig_1360878842643.log
ls: cannot access /tmp/pig_1360878842643.log: No such file or directory

如果我在参数前加上美元符号或者用引号括起来,还是会出现同样的错误。

如果我尝试使用define(我认为这个只适用于静态的Java函数),我会得到以下错误:

> define week_path util.log_path_regex(year, month, day);
2013-02-14 17:00:42,392 [main] ERROR org.apache.pig.tools.grunt.Grunt - ERROR 1200: <file script.pig, line 11, column 37>  mismatched input 'year' expecting RIGHT_PAREN

和 %declare 一样,如果我在参数前加上美元符号或者用引号括起来,还是会出现同样的错误。

我搜索了一下,没找到解决办法。可能我在找错东西。调用一个shell命令可能可行,但这会让我们的脚本部署变得复杂,而且考虑到我们是从S3获取日志,而不是从挂载的目录,这可能不太可行。同样,把生成的路径模式作为一个单独的参数传递,可能会让在实例化的MapReduce集群上运行的自动化任务变得复杂。

很可能还有其他Pig友好的方法来限制LOAD,而不需要使用路径模式。不过,我还是得用我的UDF,这似乎是问题的根源。

归根结底,我想在我的LOAD语句中包含一个在Pig内部构建的动态路径模式。Pig似乎对此并不友好。

我需要把我的UDF转换成静态的Java方法吗?还是会遇到同样的问题?(我不太想这样做,因为这只是一个8行的Python函数,容易部署,其他人维护起来也比相应的Java代码简单得多。)

自定义的LoadFunc会是解决方案吗?这样的话,我可能需要指定 /year=/month=/day=*,并强迫Pig测试每个文件名的日期戳是否在两个日期之间。这听起来像是个大麻烦,而且浪费资源。

有没有什么想法?

1 个回答

2

我在Pig用户讨论区上提了这个问题。我的理解是,Pig在处理脚本时,会先替换参数、导入和宏,然后再构建DAG(有向无环图)。这就导致了基于已有变量创建新变量变得有些困难,也解释了我为什么没能成功创建一个用于构建路径模式的用户定义函数(UDF)。

如果你是一个Pig开发者,需要基于现有参数创建新变量,你可以选择用另一个脚本来构建这些变量,然后把它们作为参数传递给你的Pig脚本;或者你可以考虑在需要使用这些新变量的地方,单独构建它们,依据你的需求来做。

在我的情况下,我无奈之下选择创建了一个自定义的LoadFunc,正如Cheolsoo Park所描述的那样。这个LoadFunc在构造时接受报告开始的日期、月份和年份,并构建一个pathGlob属性,用来匹配那个时间段的路径。然后,这个pathGlob会被插入到setLocation()中的某个位置。例如:

/**
 * Limit data to a week starting at given day. If day is 0, month is assumed.
 */
public WeeklyOrMonthlyTextLoader(String year, String month, String day) {
    super();
    pathGlob = getPathGlob(
        Integer.parseInt(year),
        Integer.parseInt(month),
        Integer.parseInt(day)
    );
}

/**
 * Replace DATE_PATH in location with glob required for reading in this
 * month or week of data. This assumes the following directory structure:
 *
 * <code>/year=&gt;year&lt;/month=&gt;month&lt;/day=&gt;day&lt;/*</code>
 */
@Override
public void setLocation(String location, Job job) throws IOException {
    location = location.replace(GLOB_PLACEHOLDER, pathGlob);
    super.setLocation(location, job);
}

然后可以在Pig脚本中这样调用:

DEFINE TextLoader com.foo.WeeklyOrMonthlyTextLoader('$year', '$month', '$day');

注意,构造函数接受的是String类型,而不是int。这是因为在Pig中,参数都是字符串,不能在Pig脚本中转换成其他类型(除了在MR任务中使用时)。

虽然创建一个自定义的LoadFunc相比于用一个包装脚本来说似乎有点过于复杂,但我希望这个解决方案能完全用Pig来实现,以避免让分析师在使用脚本前还得做额外的设置工作。我还希望在为一个定时任务创建Amazon MapReduce集群时,能够方便地使用一个标准的Pig脚本来处理不同的时间段。

撰写回答