项目上有一张Hive分区表,分区数非常多,大约有2000+个分区,然后需要增加一个字段,然后华丽丽的发现,执行了半小时之后,失败了…然后想着那就对表进行重命名吧,使用新表来替换这张旧表,旧表使用别的名字,然后又是漫长半小时等待,又失败了…表重命名操作不就是修改下元数据么?!为啥还会失败,带着这个问题,来探究下Hive表重命名相关的原理。
“参考一”和“参考二”也遇到了类似的问题,一个是数据库连接的超时时间设置的太短,另一个是数据库关键字段未建索引,这些原因和本人项目中发生的情况有所不同,我们的项目没有上述的问题,同时Hive版本使用的是1.2.X。所以还是通过源码去了解下Rename的时候到底是做了啥导致程序执行的这么慢吧
Hive分区表重命名源码解析:
表重命名相关的源码在HiveAlterHandler.java文件中,应该说Alter table的相关操作都在这个文件中。Hive表的重命名会涉及到MySQL中元数据信息的修改以及可能会移动HDFS上的文件路径。下文的源码分析只会涉及Hive分区表的重命名操作,其他暂且先不管。
我们从alterTable()方法看进去。首先是一些参数的校验,这个没啥好说的。接着就是执行msdb.openTransaction()开启事务 ,准备修改元数据信息了。源码中有这么一段注释:
// if this alter is a rename, the table is not a virtual view, the user // didn't change the default location (or new location is empty), and // table is not an external table, that means user is asking metastore to // move data to the new location corresponding to the new name 即对于virtual view或者外部表是不能修改表名的,也就是说只有内部表才可以修改表名。 同时如果创建表的时候使用的是默认路径(即没有显示指定location),那么HDFS数据目录也会移动到新表的目录下。
所以接下来就是移动HDFS数据同时修改分区的元数据信息,如果创建表时使用的是默认路径的话:
// that means user is asking metastore to move data to new location // corresponding to the new name // get new location Database db = msdb.getDatabase(newt.getDbName()); Path databasePath = constructRenamedPath(wh.getDatabasePath(db), srcPath); destPath = new Path(databasePath, newt.getTableName().toLowerCase()); destFs = wh.getFs(destPath); newt.getSd().setLocation(destPath.toString()); moveData = true; ...// HDFS移动下文件目录,这一步应该挺快的
接下来就是元数据信息修改了,主要是如下两步操作:
// 1. 查询分区信息,删除每个分区的列统计信息,修改分区元数据信息 List<Partition> parts = msdb.getPartitions(dbname, name, -1); for (Partition part : parts) { //existing partition column stats is no longer valid, remove them msdb.deletePartitionColumnStatistics(dbname, name, oldPartName, part.getValues(), null); msdb.alterPartition(dbname, name, part.getValues(), part); } // 2. 修改分区的列统计信息 updateTableColumnStatsForAlterTable(msdb, oldt, newt);
其中deletePartitionColumnStatistics()方法会查询出列统计信息,然后删除:
mStatsObjColl= (List<MPartitionColumnStatistics>)query.execute(partName.trim(), HiveStringUtils.normalizeIdentifier(dbName), HiveStringUtils.normalizeIdentifier(tableName)); pm.retrieveAll(mStatsObjColl); if (mStatsObjColl != null) { pm.deletePersistentAll(mStatsObjColl); } else { throw new NoSuchObjectException("Column stats doesn't exist for db=" + dbName + " table=" + tableName + " partition" + partName); }
hive.stats.column.autogather设置为true时,表示开启了Hive的列统计信息。每个分区每一列都会生成一条记录,列统计信息在元数据库表中记录如下所示:
所以分区数和列数越多,列统计信息就越多,2000+的分区,列数有100+列,这张表中记录条数就达到20W+。而且删除的时候是逐个分区删除的,速度更慢,咨询了下平台组的小伙伴以及网上的资料,大致确定慢是慢在这里。
后续的解决方案是,建一张新表,把数据写入到新表中。旧表中的数据通过ETL的方式迁移到新表中。
参考:
增加Hive表字段超时_MapReduce服务 MRS_故障排除_使用Hive_华为云