コンテキスト
Spark 2.0.1、クラスターモードでのspark-submit。 hdfsから寄木細工のファイルを読んでいます。
val spark = SparkSession.builder
.appName("myApp")
.config("Hive.metastore.uris", "thrift://XXX.XXX.net:9083")
.config("spark.sql.sources.bucketing.enabled", true)
.enableHiveSupport()
.getOrCreate()
val df = spark.read
.format("parquet")
.load("hdfs://XXX.XX.X.XX/myParquetFile")
df
でグループ化された50個のバケットを持つuserid
をHiveテーブルに保存しています:
df0.write
.bucketBy(50, "userid")
.saveAsTable("myHiveTable")
今、hdfs /user/Hive/warehouse
でHiveウェアハウスを見ると、myHiveTable
という名前のフォルダーがあります。その中にはたくさんのpart-*.parquet
ファイルがあります。 50個のファイルがあると予想されます。しかし、いいえ、3201ファイルがあります!!!!パーティションごとに64個のファイルがあるのはなぜですか? Hiveテーブルとして保存したファイルごとに、パーティションごとに異なる数のファイルがあります。すべてのファイルは非常に小さく、それぞれ数十Kb!
さらに、異なるuserid
の数は、myParquetFile
内の1 000 000
についてです。
質問
50個ではなく3201個のファイルがフォルダーにあるのはなぜですか!彼らは何ですか?
このテーブルをDataFrameに読み戻し、パーティションの数を出力すると:
val df2 = spark.sql("SELECT * FROM myHiveTable")
println(df2.rdd.getNumPartitions)
パーティションの数は正しく50です。データがuserid
によって正しくパーティション化されていることを確認しました。
私の大規模なデータセット3Tbの1つに対して、文字通り〜millionのファイルを作成した1000個のパーティションを持つテーブルを作成します!これは、1048576のディレクトリ項目制限を超え、org.Apache.hadoop.hdfs.protocol.FSLimitException$MaxDirectoryItemsExceededException
を提供します
質問
作成されるファイルの数は何に依存しますか?
質問
作成されるファイルの数を制限する方法はありますか?
質問
これらのファイルについて心配する必要がありますか?これらのファイルをすべて持つことで、df2
のパフォーマンスが低下しますか?問題があるため、あまり多くのパーティションを作成するべきではないと常に言われています。
質問
私はこの情報を見つけました Hive Dynamic Partitioning tips ファイルの数はマッパーの数に関係しているかもしれないということです。 Hiveテーブルへの挿入中にdistribute by
を使用することをお勧めします。 Sparkでどうすればよいですか?
質問
上記のリンクのように問題が実際にある場合は、こちら MapR-FSにデータを挿入した後にHiveテーブルのファイル番号を制御する方法Hive.merge.mapfiles
やHive.merge.mapredfiles
などのオプションを使用してすべての小さなファイルをマージすることをお勧めしますマップ削減ジョブの後。 Sparkにはこのオプションがありますか?
spark sqlを使用してください。sqlはHiveContextを使用してHiveテーブルにデータを書き込むため、テーブルスキーマで設定したバケット数を使用します。
SparkSession.builder().
config("Hive.exec.dynamic.partition", "true").
config("Hive.exec.dynamic.partition.mode", "nonstrict").
config("Hive.execution.engine","tez").
config("Hive.exec.max.dynamic.partitions","400").
config("Hive.exec.max.dynamic.partitions.pernode","400").
config("Hive.enforce.bucketing","true").
config("optimize.sort.dynamic.partitionining","true").
config("Hive.vectorized.execution.enabled","true").
config("Hive.enforce.sorting","true").
enableHiveSupport().getOrCreate()
spark.sql(s"insert into hiveTableName partition (partition_column) select * from myParquetFile")
sparkのバケット実装は、指定されたバケットサイズの数を受け入れません。各パーティションは個別のファイルに書き込みを行うため、各バケットに多くのファイルが作成されます。
このリンクを参照してください https://www.slideshare.net/databricks/Hive-bucketing-in-Apache-spark-with-tejas-patil
ラビ
回避策を見つけることができました(Spark 2.1)。これはファイル数の問題を解決しますが、パフォーマンスに影響を与える可能性があります。
dataframe
.withColumn("bucket", pmod(hash($"bucketColumn"), lit(numBuckets)))
.repartition(numBuckets, $"bucket")
.write
.format(fmt)
.bucketBy(numBuckets, "bucketColumn")
.sortBy("bucketColumn")
.option("path", "/path/to/your/table")
.saveAsTable("table_name")
Sparkのバケットアルゴリズムは、バケット列値のMurmurHash3の正のmodを実行すると思います。これは単純にそのロジックを複製し、データを再分割して、各パーティションにバケットのすべてのデータが含まれるようにします。
パーティショニング+バケティングでも同じことができます。
dataframe
.withColumn("bucket", pmod(hash($"bucketColumn"), lit(numBuckets)))
.repartition(numBuckets, $"partitionColumn", $"bucket")
.write
.format(fmt)
.partitionBy("partitionColumn")
.bucketBy(numBuckets, "bucketColumn")
.sortBy("bucketColumn")
.option("path", "/path/to/your/table")
.saveAsTable("table_name")
Csv形式を使用して、3つのパーティションと5つのバケットでローカルにテストしました(パーティションとバケットの両方の列は単なる数字です):
$ tree .
.
├── _SUCCESS
├── partitionColumn=0
│ ├── bucket=0
│ │ └── part-00004-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00000.csv
│ ├── bucket=1
│ │ └── part-00003-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00001.csv
│ ├── bucket=2
│ │ └── part-00002-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00002.csv
│ ├── bucket=3
│ │ └── part-00004-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00003.csv
│ └── bucket=4
│ └── part-00001-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00004.csv
├── partitionColumn=1
│ ├── bucket=0
│ │ └── part-00002-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00000.csv
│ ├── bucket=1
│ │ └── part-00004-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00001.csv
│ ├── bucket=2
│ │ └── part-00002-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00002.csv
│ ├── bucket=3
│ │ └── part-00001-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00003.csv
│ └── bucket=4
│ └── part-00003-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00004.csv
└── partitionColumn=2
├── bucket=0
│ └── part-00000-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00000.csv
├── bucket=1
│ └── part-00001-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00001.csv
├── bucket=2
│ └── part-00001-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00002.csv
├── bucket=3
│ └── part-00003-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00003.csv
└── bucket=4
└── part-00000-c2f2b7b5-40a1-4d24-8c05-084b7a05e399_00004.csv
3つのパーティションすべてのバケット= 0です(これらはすべて同じ値であることがわかります)。
$ paste partitionColumn=0/bucket=0/part-00004-5f860e5c-f2c2-4d52-8035-aa00e4432770_00000.csv partitionColumn=1/bucket=0/part-00002-5f860e5c-f2c2-4d52-8035-aa00e4432770_00000.csv partitionColumn=2/bucket=0/part-00000-5f860e5c-f2c2-4d52-8035-aa00e4432770_00000.csv | head
0 0 0
4 4 4
6 6 6
16 16 16
18 18 18
20 20 20
26 26 26
27 27 27
29 29 29
32 32 32
実際、追加のバケットインデックスが好きでした。しかし、そうしないと、書き込みの直前にバケット列をドロップでき、パーティションごとにnumBuckets個のファイル数を取得できます。