一般情况下,使用add partition语句为Hive表新增partition:
其中Location默认为最低层目录,如果该目录仍包含子目录,Hive就会报错。
然而在很多情况下,数据文件是以层级目录的方式组织的。典型的案例就是使用Flume写数据到HDFS,这时不得不为新的partition新建一个目录,并把数据文件手动拷到该路径下。更加糟糕的是,因为Flume经常使用日期或者日志内存动态创建子目录,有时你并不知道有哪些子目录。为了实现add partition,你可能还要读取目录下的文件夹列表。这显然给程序增加了不必要的复杂度,使程度变得冗长难以维护。
解决这个问题的方法是,为Hive设置以下两个参数:
讲完结论,我们再仔细研究下背后的原因。
Hive其实就是MapReduce的一个SQL接口,它只记录meta data,并不直接存储和计算数据。Add partition操作本质上是为MapReduce指定了一个新的输入目录,然后MapReduce会尝试在该目录在寻找输入文件。那么定位文件的方式是怎样呢?在JIRA上一个较早的issue HIVE-1083有提到
Basically, by default, a table’s file name pattern will be “*”.
也就是说,Hive只是简单地在location后面使用通配符来查找文件,当匹配到了文件夹,无法处理就报错了。那么既然是HDFS路径,使用HDFS支持的正则通配可以吗?很遗憾,Hive对location是进行了转义处理的,正则通配并不起作用。
这样一来,只好从MapReduce入手了。要过滤输入文件,第一个想到的必须是InputFormat。在编写MapReduce程序时,FileInputFormat有个函数,FileInputFormat.setInputDirRecursive(job, true),应该就是我们需要的。查一下FileInputFormat相关参数,在静态成员变量中找到了:
于是,将该参数设为true。在Hive中使用时,再将hive.mapred.supports.subdirectories设为true,再执行查询就没问题了!
本文是原创文章,转载请注明:时间与精神的小屋 - Hive使用递归子目录作为partition