hive重写分区数据异常

问题描述:

已有(外部/内部)表test,新建分区时指定数据位置,如下

alter table test add partition(day=‘20140101‘)

location ‘20140101‘;

这样会默认在表warehouse路径下生成/{warehouse}/test/20140101/这种格式目录

同时使用命令 desc formatted test partition(day=‘20140101‘)可以查看到相应的location为

hdfs://..:../{warehouse}/test/20140101/

然后使用insert overwrite向分区插入数据

insert overwrite table test partition (day=‘20140101‘)

select xx from xx....;

正常情况下一切正常,但是当设置属性fs.hdfs.impl.disable.cache为true时,会出现以下情况

desc formatted test partition(day=‘20140101‘)时发现location变成了以下格式

hdfs://..:../{warehouse}/test/day=20140101/

同时会在hdfs上生成一个新的目录/{warehouse}/test/day=20140101/,而此分区之前的location路径会被删掉,即/{warehouse}/test/20140101/这个路径被删除

解决:

(1)先看hql的执行计划,大概如下

Stage: Stage-1

Map Reduce

Alias -> Map Operator Tree:

dual

TableScan

alias: dual

Select Operator

expressions:

expr: ‘1‘

type: string

expr: ‘2‘

type: string

outputColumnNames: _col0, _col1

File Output Operator

compressed: true

GlobalTableId: 1

table:

input format: org.apache.hadoop.mapred.TextInputFormat

output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat

serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe

name: default.test

Stage: Stage-4

Move Operator

files:

hdfs directory: true

destination: hdfs://hadoop_namenode/tmp/hive-root/hive_2015-01-07_18-07-13_120_2026314954951095577/-ext-10000

Stage: Stage-0

Move Operator

tables:

partition:

day 20140101

replace: true  --overwrite

table:

input format: org.apache.hadoop.mapred.TextInputFormat

output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat

serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe

name: default.test

由执行计划能看出,前面的mapreduce过程不会影响到表分区路径的新建或删除,而真正影响到数据的操作是Move Operator

(2)找到Move Operator对应的源码task类,  org.apache.hadoop.hive.ql.exec.MoveTask.java

该类有个方法是move操作时执行的,public int execute(DriverContext driverContext) {...}方法较长,我们只找主要执行到的部分

// static partitions   对于静态分区,主要有以下方法执行操作

db.loadPartition(tbd.getSourcePath(),   //来源数据的位置,即mapreduce计算结果的临时目录

tbd.getTable()  .getTableName(),    //表的名字

tbd.getPartitionSpec(),   //获得指定的表分区

tbd  .getReplace(),   //是否采用覆盖的方式,overwrite

tbd.getHoldDDLTime(),  //if true, force [re]create the partition,没有分区则新建分区

tbd  .getInheritTableSpecs(),  //修改的分区是否继承之前的属性,默认为true

isSkewedStoredAsDirs(tbd));  //表是否是分桶表

(3)跟踪方法进入到org.apache.hadoop.hive.ql.metadata.Hive.java类,找到对应的方法loadPartition,找到相应代码段

Partition oldPart = getPartition(tbl, partSpec, false);

Path oldPartPath = null;

if(oldPart != null) {

  oldPartPath = oldPart.getDataLocation(); //表分区定义的location,即我们例子中的 /{warehouse}/test/20140101/

}

Path newPartPath = null;

if (inheritTableSpecs) {//默认值为true

  Path partPath = new Path(tbl.getDataLocation(),Warehouse.makePartPath(partSpec));

newPartPath = new Path(loadPath.toUri().getScheme(), loadPath.toUri().getAuthority(),

partPath.toUri().getPath());//值为由表的location信息和分区值组成的路径,即我们例子中的/{warehouse}/test/day=20140101/

  if(oldPart != null) {

   /*

   * If we are moving the partition across filesystem boundaries

   * inherit from the table properties. Otherwise (same filesystem) use the

   * original partition location.

   *

   * See: HIVE-1707 and HIVE-2117 for background

   */

   /*fs.hdfs.impl.disable.cache 这个参数 就影响到以下两个操作,决定了oldPartPathFS与loadPathFS 是否指向同一个对象,进而影响到 newPartPath
的值到底取什么

*/

   FileSystem oldPartPathFS = oldPartPath.getFileSystem(getConf());//分区的location

   FileSystem loadPathFS = loadPath.getFileSystem(getConf());//来源数据

   if (oldPartPathFS.equals(loadPathFS)) {

    newPartPath = oldPartPath;

   }

  }

}else {

  newPartPath = oldPartPath;

}

newPartPath 这个变量就是决定数据move操作时的目的路径,所以只要确定newPartPath 的值,我们就知道数据是怎么移动的

(4)目标路径的取值

我们嵌入一下org.apache.hadoop.fs.Path.java类的内容,找到方法getFileSystem(Configuration),研究一下这个方法是怎么实现的

public FileSystem getFileSystem(Configuration conf)

throws IOException

{

return FileSystem.get(toUri(), conf);

}

继续跟踪代码FileSystem.get(toUri(), conf),跟到类org.apache.hadoop.fs.FileSystem.java,跟踪方法public static FileSystem get(URI uri,
Configuration conf){...},看主要代码段:

String disableCacheName = String.format("fs.%s.impl.disable.cache", new Object[] {  scheme  });

if(conf.getBoolean(disableCacheName, false))

return createFileSystem(uri, conf); //如果设置了fs.hdfs.impl.disable.cache=true,则每次FileSystem.get(...)时,都是获得一个

新FileSystem对象,再执行上面的oldPartPathFS.equals(loadPathFS)时,肯定为false

else

return CACHE.get(uri, conf);//如果设置了fs.hdfs.impl.disable.cache=false,则从缓存CACHE中找相应的FileSystem对象,再执行上面

的oldPartPathFS.equals(loadPathFS)时,为true

根据这段的分析,再执行(3)中的代码时,如下

if (oldPartPathFS.equals(loadPathFS)) {

newPartPath = oldPartPath;

}

//如果设置了fs.hdfs.impl.disable.cache=false,则oldPartPathFS.equals(loadPathFS)返回true,newPartPath 取值为oldPartPath,值为上例中的 /{warehouse}/test/20140101/;否则newPartPath 的值保持不变,为/{warehouse}/test/day=20140101/

由于我们在操作中设置了fs.hdfs.impl.disable.cache=true,所以导致newPartPath 值为/{warehouse}/test/day=20140101/

(5) 移动数据,回到类org.apache.hadoop.hive.ql.metadata.Hive.java

/* 由于我们使用的操作是insert overwrite ,所以 replace为true,最终数据就是移动到newPartPath*/

if (replace) { // 判断是否替换掉原来的数据

  Hive.replaceFiles(loadPath, newPartPath, oldPartPath, getConf());

} else {

  FileSystem fs = tbl.getDataLocation().getFileSystem(conf);

Hive.copyFiles(conf, loadPath, newPartPath, fs);

}

跟踪到方法 void replaceFiles(Path srcf, Path destf, Path oldPath, HiveConf conf){...},看下对数据的操作

这个方法主要有两个操作

1.删除原来的数据 , oldPath ,即我们例子中的/{warehouse}/test/day=20140101/

if (fs2.exists(oldPath)) {

// use FsShell to move data to .Trash first rather than delete permanently

FsShell fshell = new FsShell();

fshell.setConf(conf);

fshell.run(new String[]{"-rmr", oldPath.toString()});

}

2.rename源数据到目标路径,完成数据移动,srcf->destf,上例中此时的destf为/{warehouse}/test/day=20140101/

boolean b = renameFile(conf, srcs[0].getPath(), destf, fs, true);

根据上面的分析,我们可以看出,由于设置了fs.hdfs.impl.disable.cache=true,,无法再缓存中取FileSystem对象,所以导致newPartPath的值无法取到oldPartPath的值,最终为/{warehouse}/test/day=20140101/,所以最终会在hdfs上面新建一个目录,然后删除了oldPartPath原来的数据,导致/{warehouse}/test/20140101/目录及下面的文件都被删除掉,所以出现了上面的情况!

时间: 2024-10-06 10:47:01

hive重写分区数据异常的相关文章

在Impala 和Hive里进行数据分区(1)

进行数据分区将会极大的提高数据查询的效率,尤其是对于当下大数据的运用,是一门不可或缺的知识.那么数据怎么创建分区呢?数据怎样加载到分区呢? Impala/Hive按State分区Accounts (1)示例:accounts是非分区表 通过以上方式创建的话,数据就存放在accounts目录里面.那么,如果Loudacre大部分对customer表的分析是按state来完成的?比如: 这种情况下如果数据量很大,为了避免全表扫描的发生,我们可以去创建分区.如果不创建分区的话,它会默认所有查询不得不扫

Hive[5] HiveQL 数据操作

5.1 向管理表中装载数据  Hive 没有行级别的数据插入更新和删除操作,那么往表中装载数据的唯一途径就是使用一种“大量”的数据装载操作,或者通过其他方式仅仅将文件写入到正确的目录下: LOAD DATA LOCAL INPATH '${env:HOME}/califonia-employees' OVERWRITE INOT TABLE employees PARTITON (country=''US, state='CA') ; 向管理表中装载数据,如果目录不存在的话, overwrite

Hive基础之Hive体系架构&运行模式&Hive与关系型数据的区别

Hive架构 1)用户接口: CLI(hive shell):命令行工具:启动方式:hive 或者 hive --service cli ThriftServer:通过Thrift对外提供服务,默认端口是10000:启动方式:hive --service hiveserver WEBUI(浏览器访问hive):通过浏览器访问hive,默认端口是9999:启动方式:hive --service hwi 2)元数据存储(Metastore):启动方式:hive -service metastore

Hive之分区(Partitions)和桶(Buckets)

转自:http://www.aahyhaa.com/archives/316 hive引入partition和bucket的概念,中文翻译分别为分区和桶(我觉的不是很合适,但是网上基本都是这么翻译,暂时用这个吧),这两个概念都是把数据划分成块,分区是粗粒度的划分桶是细粒度的划分,这样做为了可以让查询发生在小范围的数据上以提高效率. 首先介绍分区的概念,还是先来个例子看下如果创建分区表:[code lang=”sql”]create table logs_partition(ts bigint,l

hive学习笔记-数据操作

hive数据操作 hive命令行操作 hive -d --define <key=value> 定义一个key-value可以在命令行中使用 hive -d database <databasename>    指定使用的数据库 hive -e "hql"    不需要进入cli执行hql语句,可以在脚本中使用 hive -f fileName 将hql放到一个file文件中执行,sql语句来自file文件 hive -h hostname 访问主机,通过主机的

框架 day50 BOS项目 4 批量导入(ocupload插件,pinyin4J)/POI解析Excel/Combobox下拉框/分区组合条件分页查询(ajax)/分区数据导出(Excel)

知识点: 批量导入(ocupload插件,pinyin4J /POI解析Excel(apache POI) /区域分页查询 /Combobox下拉框 /分区组合条件分页查询(ajax) /分区数据导出(Excel下载) BOS项目笔记第4天 1.    区域批量导入功能 *Ajax不支持文件上传. *上传并且不刷新上传页面原理: Target到一个0,0,0的隐藏iframe里,造成一个没有刷新的假象 <form target="myIframe" action="ab

HIVE动态分区实战

一)hive中支持两种类型的分区: 静态分区SP(static partition) 动态分区DP(dynamic partition) 静态分区与动态分区的主要区别在于静态分区是手动指定,而动态分区是通过数据来进行判断.详细来说,静态分区的列实在编译时期,通过用户传递来决定的:动态分区只有在SQL执行时才能决定. 二)实战演示如何在hive中使用动态分区 1.创建一张分区表,包含两个分区dt和ht表示日期和小时 CREATE TABLE partition_table001 ( name ST

HIVE几种数据导入方式

HIVE几种数据导入方式 今天的话题是总结Hive的几种常见的数据导入方式,我总结为四种:(1).从本地文件系统中导入数据到Hive表:(2).从HDFS上导入数据到Hive表:(3).从别的表中查询出相应的数据并导入到Hive表中:(4).在创建表的时候通过从别的表中查询出相应的记录并插入到所创建的表中.我会对每一种数据的导入进行实际的操作,因为纯粹的文字让人看起来很枯燥,而且学起来也很抽象.好了,开始操作! 一.从本地文件系统中导入数据到Hive表 先在Hive里面创建好表,如下: hive

hive创建分区

HIVE的分区通过在创建表时启用partitionby实现,用来partition的维度并不是实际数据的某一列,具体分区的标志是由插入内容时给定的.当要查询某一分区的内容时可以采用where语句,形似where tablename.partition_key >a来实现. 创建含分区的表. 命令原型: CREATE TABLE page_view(viewTime INT, userid BIGINT, page_urlSTRING, referrer_url STRING, ip STRING