parquet.io.ParquetDecodingException:无法读取文件中块-1中0的值

时间:2016-06-15 07:54:59

标签: java hadoop apache-spark hive

我使用saveAsTable方法在Hive中保存了一个远程数据库表,现在当我尝试使用CLI命令select * from table_name访问Hive表数据时,它给出了以下错误:

2016-06-15 10:49:36,866 WARN  [HiveServer2-Handler-Pool: Thread-96]:
thrift.ThriftCLIService (ThriftCLIService.java:FetchResults(681)) -
Error fetching results: org.apache.hive.service.cli.HiveSQLException:
java.io.IOException: parquet.io.ParquetDecodingException: Can not read
value at 0 in block -1 in file hdfs:

知道我在这里做错了什么吗?

6 个答案:

答案 0 :(得分:2)

我有一个类似的错误(但在非负块的正索引处),这是因为我创建了Parquet数据,其中一些Spark数据帧类型在实际为null时被标记为不可为空

在我的情况下,我因此将错误解释为Spark试图从某个非可空类型中读取数据并在意外的空值上遇到障碍。

为了增加混乱,在阅读Parquet文件后,Spark报告printSchema()所有字段都可以为空,无论它们是否可用。但是,就我而言,在原始的Parquet文件中使它们真正可以解决这个问题。

现在,问题发生在“块-1中的0”的事实是可疑的:它实际上几乎看起来好像没有找到数据,因为块-1看起来好像Spark甚至没有开始读取任何东西(只是一个猜测)。

答案 1 :(得分:2)

<强>问题: 在查询impyla中的数据时遇到下面的问题(由spark job写的数据)

ERROR: Error while processing statement: FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.tez.TezTask. Vertex failed, vertexName=Map 1, vertexId=vertex_1521667682013_4868_1_00, diagnostics=[Task failed, taskId=task_1521667682013_4868_1_00_000082, diagnostics=[TaskAttempt 0 failed, info=[Error: Failure while running task:java.lang.RuntimeException: java.lang.RuntimeException: java.io.IOException: org.apache.parquet.io.ParquetDecodingException: Can not read value at 0 in block -1 in file hdfs://shastina/sys/datalake_dev/venmo/data/managed_zone/integration/ACCOUNT_20180305/part-r-00082-bc0c080c-4080-4f6b-9b94-f5bafb5234db.snappy.parquet
    at org.apache.hadoop.hive.ql.exec.tez.TezProcessor.initializeAndRunProcessor(TezProcessor.java:173)
    at org.apache.hadoop.hive.ql.exec.tez.TezProcessor.run(TezProcessor.java:139)
    at org.apache.tez.runtime.LogicalIOProcessorRuntimeTask.run(LogicalIOProcessorRuntimeTask.java:347)
    at org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable$1.run(TezTaskRunner.java:194)
    at org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable$1.run(TezTaskRunner.java:185)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:422)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1724)
    at org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable.callInternal(TezTaskRunner.java:185)
    at org.apache.tez.runtime.task.TezTaskRunner$TaskRunnerCallable.callInternal(TezTaskRunner.java:181)
    at org.apache.tez.common.CallableWithNdc.call(CallableWithNdc.java:36)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

根本原因:

此问题是由于Hive和Spark中使用的不同镶木地板惯例造成的。在Hive中,十进制数据类型表示为固定字节(INT 32)。在Spark 1.4或更高版本中,默认约定是使用标准Parquet表示形式的十进制数据类型。根据基于列数据类型精度的标准Parquet表示,基础表示会发生变化 例如: DECIMAL可用于注释以下类型: int32:for 1&lt; = precision&lt; = 9 int64:for 1&lt; = precision&lt; = 18;精度&lt; 10会产生警告

因此,只有使用在不同Parquet约定中具有不同表示形式的数据类型才会出现此问题。如果数据类型为DECIMAL(10,3),则两个约定都将其表示为INT32,因此我们不会遇到问题。如果您不了解数据类型的内部表示,则可以安全地使用与读取时相同的约定。使用Hive,您无法灵活选择Parquet约定。但是对于Spark,你可以。

<强>解决方案: Spark用于编写Parquet数据的约定是可配置的。这由属性spark.sql.parquet.writeLegacyFormat决定 默认值为false。如果设置为“true”,Spark将使用与Hive相同的约定来编写Parquet数据。这将有助于解决问题。

--conf "spark.sql.parquet.writeLegacyFormat=true"

<强>参考文献:

答案 2 :(得分:1)

这看起来像是架构不匹配问题。 如果您将模式设置为不可为空,并使用None值创建数据框,Spark会抛出 ValueError:此字段不可为空,但无错误。

[Pyspark]

from pyspark.sql.functions import * #udf, concat, col, lit, ltrim, rtrim
from pyspark.sql.types import *  

schema = ArrayType(StructType([StructField('A', IntegerType(), nullable=False)]))
# It will throw "ValueError". 
df = spark.createDataFrame([[[None]],[[2]]],schema=schema) 
df.show()

但是如果你使用udf则不是这样。

使用相同的模式,如果使用udf进行转换,即使你的udf返回None,也不会抛出 ValueError 。它是数据模式不匹配发生的地方。

例如:

df = spark.createDataFrame([[[1]],[[2]]], schema=schema)

def throw_none():
    def _throw_none(x):
        if x[0][0] == 1:
            return [['I AM ONE']] 
        else:
            return x 
    return udf(_throw_none, schema) 

# since value col only accept intergerType, it will throw null for 
# string "I AM ONE" in the first row. But spark did not throw ValueError
# error this time ! This is where data schema type mismatch happen !
df = df.select(throw_none()(col("value")).name('value'))  
df.show()  

enter image description here

然后,下面的镶木地板写入和读取会引发 parquet.io.ParquetDecodingException 错误。

df.write.parquet("tmp")
spark.read.parquet("tmp").collect()

因此,如果使用udf,请在null值上小心,在udf中返回正确的数据类型。除非没有必要,否则请不要在 StructField 中设置nullable = False。设置nullable = True将解决所有问题。

答案 3 :(得分:1)

捕获可能的差异的另一种方法是关注由蜂巢和火花等两种来源产生的镶木文件图式差异。您可以使用parquet-tools(对于macOS为brew install parquet-tools)转储架构:

λ $ parquet-tools schema /usr/local/Cellar/apache-drill/1.16.0/libexec/sample-data/nation.parquet
message root {
  required int64 N_NATIONKEY;
  required binary N_NAME (UTF8);
  required int64 N_REGIONKEY;
  required binary N_COMMENT (UTF8);
}

答案 4 :(得分:0)

您是否可以使用Avro而不是Parquet来存储您的Hive表?我遇到了这个问题,因为我使用的是Hive的Decimal数据类型,而Spark中的Parquet并不适合使用Decimal。如果发布表模式和一些数据样本,调试将更容易。

另一个可能的选项from the DataBricks Forum是使用Double而不是Decimal,但这不是我的数据的选项,所以我无法报告它是否有效。

答案 5 :(得分:0)

我遇到了类似的错误,就我而言,我缺少默认构造函数