记录一次Hive表清理过程

背景

时间:2020-07-17

在用spark+hive做数仓的过程中往往会产生很多表,过多历史表会很快消耗掉有限的hdfs资源,并且时间过于久远的表一般不会具有利用价值了,如果不及时清理这部分hive表会造成hdfs资源的严重浪费,因此需要有一个类似于HiveClean的定时任务,执行定期清理旧数据的逻辑。

动机

在接手这个功能需求的时候,已经有了一个HiveClean的版本,基本的清理逻辑如下:

  • 遍历所有需要清理的hive表
  • 执行ALTER TABLE $cluster%s.$table%s SET TBLPROPERTIES('external'='false')来把hive外部表转换成hive内部表

ps:如果直接删除外部表,只会从hive metastore中删除外部表的元数据,而对应的物理文件依然存放在hdfs中,没有达成我们的目的

  • 筛选出需要删除的表分区,执行ALTER TABLE $cluster%s.$table%s DROP PARTITION (time_hour='xxx')来删除对应的分区

现在的需求是改进这个逻辑:注意上面的最后一步,删除分区的方式是一个一个删除,想看看能不能用范围删除的方式来清理旧分区

问题

  1. 一开始了解到hive是原生支持分区范围删除的:ALTER TABLE $cluster%s.$table%s DROP PARTITION (time_hour<'xxx'),但是事情并没有那么简单:在hive执行没有问题,但是Spark SQL引擎并不支持
spark-sql> ALTER TABLE xxx DROP PARTITION (time_hour<2020060110);    Error in query:    mismatched input '<' expecting {')', ','}(line 1, pos 54)
== SQL ==    ALTER TABLE xxx DROP PARTITION (time_hour<2020060110)    ------------------------------------------------------^^^
spark-sql>



这是Spark SQL自身的局限性

  1. 按照参数的设置,HiveClean定时任务会在每天的下午某个时间点执行,并且清理的是45天以前的旧分区
  2. 但是遍历HDFS文件目录可以发现,有一些Hive表45天前的分区中的物理文件虽然不存在了,但是分区目录还在;还有另一些Hive表干脆连45天前的物理文件还完好无损(后来分析猜测是有些表手工设置为了内部表,而后来新增的表没有设置)
  3. 在hive中执行SHOW PARTITIONS xxx时显示的分区确实只有45天以内的(从后面的分析可以知道这是删除外部表的结果,元数据删除了但是物理文件还在)
  1. Hive内部表和外部表的原因
  2. 执行DESC FORMATTED xxx可以查看Hive表的属性,例如:shell ... ... Type MANAGED Provider hive ... ... Location hdfs://hadoop-hdfs-nn/topics/cluster/xxx ... ... 上面显示的Type MANAGED表明Hive表示内部表,如果显示的是Type EXTERNAL则说明是外部表
  3. 在执行上述的命令查看Hive表属性的时候发现是外部表,但是奇怪的地方就在于每次HiveClean执行的时候,都会先通过ALTER TABLE $cluster%s.$table%s SET TBLPROPERTIES('external'='false')将外部表转换成内部表,然后再执行分区删除逻辑,理论上执行一次之后,Hive表就应该是内部表了,而现在来看却不是这样,问题出在了哪?
  4. 在Stack Overflow上面找到了这样的回答:


How can we convert an external table to managed table in SPARK 2.2.0?stackoverflow.com


hive什么属性控制reduce的数量_hive什么属性控制reduce的数量


,有两个值得关注的回答:

  1. 关于上面外部表转换语句无效的解释


hive什么属性控制reduce的数量_hive 删除表_02


重点在于下面的评论,先看回答中给出的方案,显然跟我们现有的HiveClean中的一样,说是大小写敏感的问题,但是这是网上流传甚广的误导方案,正确的设置语句应该是大写的:ALTER TABLE $cluster%s.$table%s SET TBLPROPERTIES('EXTERNAL'='FALSE'),但是如果在spark中执行的话会报错(hive中当然一点问题都没有):Error in query: Cannot set or change the preserved property key: 'EXTERNAL'; 现在来看答案下面的解释,简单来说就是EXTERNAL是保留字并且大小写敏感,如果设置的是external那么只会给Hive表新增一条自定义属性'external',EXTERNAL依然没有改变(即内外部表的属性没变)
既然改变EXTERNAL的值才行,但Spark中又没法执行,那怎么办呢?总不能手工把所有的表全部设置为内部表吧,万一将来新增了新的表忘记了设置为内部表,又或者希望将内部表转成外部表,每次都手工设置显得很不优雅,有什么好方法吗?别急,也在这个SO的回答中,看下面

  1. Spark中外部表转换内部表的正确姿势


hive什么属性控制reduce的数量_hive 删除分区_03


这个无需多说,无脑照抄就行,亲测有效

  1. 现在还有点小问题,就是一些表被当做外部表删除了,metastore中已经没了45天以前的元数据,因此在后面的分区删除任务中这些分区依然没法被删除,这时候就需要把这些表的分区修复回来,然后在下一次HiveClean定时任务执行时统一把远古分区给删掉

Hive分区修复命令MSCK用法:MSCK REPAIR TABLE xxx,可以参考这篇文章了解一下MSCK命令:

一二三冲鸭:一起学Hive——使用MSCK命令修复Hive分区zhuanlan.zhihu.com


后续

但是现在在Spark中按范围删除Hive表分区的问题依然没有解决,反倒歪打正着修复了存在已久的bug。。。