saveAsTable
メソッドを使用してHiveにリモートDBテーブルを保存しましたが、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:
ここで私が間違っていると思われることはありますか?
問題: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では、10進数のデータ型は固定バイト(INT 32)として表されます。 Spark 1.4以降では、デフォルトの規則は、10進データ型に標準寄木細工表現を使用することです。列データ型の精度に基づく標準寄木細工表現に従って、基になる表現は変更されます。
eg:DECIMALを使用して、次のタイプに注釈を付けることができます。int32:for 1 <=精度<= 9 int64:for 1 <=精度<= 18;精度<10は警告を生成します
したがって、この問題は、異なるParquet規則で異なる表現を持つデータ型を使用した場合にのみ発生します。データ型がDECIMAL(10,3)の場合、両方の規則がそれをINT32として表すため、問題は発生しません。データ型の内部表現がわからない場合は、読み取り中に書き込みに使用するのと同じ規則を使用しても安全です。 Hiveでは、Parquet規則を選択する柔軟性がありません。しかし、Sparkを使用すれば、そうなります。
ソリューション:Parquetデータを書き込むためにSparkで使用される規則は構成可能です。これはプロパティspark.sqlによって決定されます.parquet.writeLegacyFormatデフォルト値はfalseです。「true」に設定すると、SparkはHiveと同じ規則を使用してParquetデータを書き込みます。これは問題の解決に役立ちます。
--conf "spark.sql.parquet.writeLegacyFormat=true"
参照:
同様のエラーが発生しましたが(ただし、非負のブロックの正のインデックスで)、いくつかのSparkデータフレームタイプがnon-とマークされたParquetデータを作成したことが原因です。それらが実際にnullの場合はnull可能。
私の場合、このようにしてエラーをSparkが特定のnull不可能なタイプからデータを読み取ろうとし、予期しないnull値に遭遇したと解釈します。
混乱を増すために、Parquetファイルを読み取った後、SparkはprintSchema()
を使用して、すべてのフィールドがnullであるかどうかにかかわらず、null可能であることを報告します。ただし、私の場合、元のParquetファイルでreally nullableにすると、問題が解決しました。
ここで、質問が「ブロック-1の0」で発生するという事実は疑わしいです。ブロック-1はSpark何でも読み始めた(推測)。
ここではスキーマの不一致の問題のようです。スキーマをnullにできないように設定し、None値でデータフレームを作成すると、Sparkはをスローします)ValueError:このフィールドはnullにできませんが、Noneエラー。
[ピスパーク]
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()
次に、次の寄木細工の書き込みと読み取りはparquet.io.ParquetDecodingExceptionエラーをスローします。
df.write.parquet("tmp")
spark.read.parquet("tmp").collect()
したがって、udfを使用している場合はnull値に十分注意して、正しいデータ型をudfに返します。また、不要な場合を除き、StructFieldにnullable = Falseを設定しないでください。 nullable = Trueを設定すると、すべての問題が解決されます。
Parquetの代わりにAvroを使用してHiveテーブルを保存できますか? HiveのDecimalデータ型を使用していて、SparkのParquetがDecimalでうまく機能しないため、この問題に遭遇しました。テーブルスキーマといくつかのデータサンプルを送信すると、デバッグが容易になります。
別の可能なオプション DataBricksフォーラムから は、Decimalの代わりにDoubleを使用することですが、それは私のデータのオプションではなかったため、機能するかどうかをレポートすることはできません。
起こり得る不一致を見つけるもう1つの方法は、Hiveとsparkのように、両方のソースによって生成された寄木細工ファイルのスキーマの違いに注目することです。 parquet-tools(brew install parquet-tools
for macos):
λ $ 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);
}