Kıvılcım> = 2.3.0
SPARK-22614, aralık bölümlemesini açığa çıkarır.
val partitionedByRange = df.repartitionByRange(42, $"k")
partitionedByRange.explain
// == Parsed Logical Plan ==
// 'RepartitionByExpression ['k ASC NULLS FIRST], 42
// +- AnalysisBarrier Project [_1#2 AS k#5, _2#3 AS v#6]
//
// == Analyzed Logical Plan ==
// k: string, v: int
// RepartitionByExpression [k#5 ASC NULLS FIRST], 42
// +- Project [_1#2 AS k#5, _2#3 AS v#6]
// +- LocalRelation [_1#2, _2#3]
//
// == Optimized Logical Plan ==
// RepartitionByExpression [k#5 ASC NULLS FIRST], 42
// +- LocalRelation [k#5, v#6]
//
// == Physical Plan ==
// Exchange rangepartitioning(k#5 ASC NULLS FIRST, 42)
// +- LocalTableScan [k#5, v#6]
SPARK-22389 , Veri Kaynağı API v2'de harici format bölümlemesini ortaya çıkarır .
Kıvılcım> = 1.6.0
Spark> = 1.6'da sorgu ve önbelleğe alma için sütuna göre bölümlemeyi kullanmak mümkündür. Bakınız: SPARK-11410 ve SPARK-4849repartition
yöntemi kullanarak :
val df = Seq(
("A", 1), ("B", 2), ("A", 3), ("C", 1)
).toDF("k", "v")
val partitioned = df.repartition($"k")
partitioned.explain
// scala> df.repartition($"k").explain(true)
// == Parsed Logical Plan ==
// 'RepartitionByExpression ['k], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Analyzed Logical Plan ==
// k: string, v: int
// RepartitionByExpression [k#7], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Optimized Logical Plan ==
// RepartitionByExpression [k#7], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Physical Plan ==
// TungstenExchange hashpartitioning(k#7,200), None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- Scan PhysicalRDD[_1#5,_2#6]
RDDs
Spark'ın aksine Dataset
( Dataset[Row]
aka dahil DataFrame
) şimdilik özel bölümleyici kullanamaz. Bunu genellikle yapay bir bölümleme sütunu oluşturarak ele alabilirsiniz, ancak bu size aynı esnekliği vermez.
Kıvılcım <1.6.0:
Yapabileceğiniz bir şey, giriş verilerini oluşturmadan önce ön bölümlemektir. DataFrame
import org.apache.spark.sql.types._
import org.apache.spark.sql.Row
import org.apache.spark.HashPartitioner
val schema = StructType(Seq(
StructField("x", StringType, false),
StructField("y", LongType, false),
StructField("z", DoubleType, false)
))
val rdd = sc.parallelize(Seq(
Row("foo", 1L, 0.5), Row("bar", 0L, 0.0), Row("??", -1L, 2.0),
Row("foo", -1L, 0.0), Row("??", 3L, 0.6), Row("bar", -3L, 0.99)
))
val partitioner = new HashPartitioner(5)
val partitioned = rdd.map(r => (r.getString(0), r))
.partitionBy(partitioner)
.values
val df = sqlContext.createDataFrame(partitioned, schema)
Yana DataFrame
bir gelen yaratma RDD
bölüm düzeni varolan sadece basit bir harita aşamasını gerektirir * korunması gerektiğini:
assert(df.rdd.partitions == partitioned.partitions)
Aynı şekilde var olanı yeniden bölümlere ayırabilirsiniz DataFrame
:
sqlContext.createDataFrame(
df.rdd.map(r => (r.getInt(1), r)).partitionBy(partitioner).values,
df.schema
)
Yani imkansız değilmiş gibi görünüyor. Hiç mantıklıysa soru kalır. Çoğu zaman bunun olmadığını iddia edeceğim:
Yeniden bölümleme pahalı bir süreçtir. Tipik bir senaryoda, verilerin çoğu serileştirilmeli, karıştırılmalı ve serileştirilmemelidir. Öte yandan, önceden bölümlenmiş bir veriden yararlanabilecek işlemlerin sayısı nispeten azdır ve dahili API bu özelliği kullanmak üzere tasarlanmadıysa daha da sınırlıdır.
- bazı senaryolara katılır, ancak dahili bir destek gerektirir,
- pencere işlevleri eşleşen bölümleyiciyle çağrılar. Yukarıdakinin aynısı, tek bir pencere tanımıyla sınırlıdır. Zaten dahili olarak bölümlenmiştir, bu nedenle ön bölümleme gereksiz olabilir,
- basit toplamalar
GROUP BY
- geçici tamponların bellek ayak izini azaltmak mümkündür **, ancak toplam maliyet çok daha yüksektir. groupByKey.mapValues(_.reduce)
(Mevcut davranış) - reduceByKey
(ön bölümleme) ile aşağı yukarı eşdeğerdir . Pratikte yararlı olması pek olası değildir.
- ile veri sıkıştırma
SqlContext.cacheTable
. Çalıştırma uzunluğu kodlaması kullanıyor gibi göründüğünden, uygulama OrderedRDDFunctions.repartitionAndSortWithinPartitions
sıkıştırma oranını iyileştirebilir.
Performans, büyük ölçüde anahtarların dağıtımına bağlıdır. Eğri olursa, yetersiz kaynak kullanımına neden olur. En kötü senaryoda, işi bitirmek imkansız olacaktır.
- Yüksek seviyeli bildirim temelli bir API kullanmanın tüm amacı, kendinizi düşük seviyeli uygulama ayrıntılarından izole etmektir. @Dwysakowicz ve @RomiKuntsman tarafından daha önce bahsedildiği gibi, bir optimizasyon Katalizör Doktorunun bir işidir . Oldukça sofistike bir canavar ve iç kısımlarına çok daha fazla dalmadan bunu kolayca geliştirebileceğinizden gerçekten şüpheliyim.
Ilgili kavramlar
JDBC kaynakları ile bölümleme :
JDBC veri kaynakları predicates
argümanı destekler . Aşağıdaki şekilde kullanılabilir:
sqlContext.read.jdbc(url, table, Array("foo = 1", "foo = 3"), props)
Koşul başına tek bir JDBC bölümü oluşturur. Tek tek yüklemler kullanılarak oluşturulan kümeler ayrık değilse, ortaya çıkan tabloda kopyalar göreceğinizi unutmayın.
partitionBy
yöntemDataFrameWriter
:
Spark DataFrameWriter
, partitionBy
yazma sırasında verileri "bölümlemek" için kullanılabilecek bir yöntem sağlar . Verilen sütun setini kullanarak yazma sırasında verileri ayırır
val df = Seq(
("foo", 1.0), ("bar", 2.0), ("foo", 1.5), ("bar", 2.6)
).toDF("k", "v")
df.write.partitionBy("k").json("/tmp/foo.json")
Bu, anahtara dayalı sorgular için okumada aşağı doğru yüklemeyi etkinleştirir:
val df1 = sqlContext.read.schema(df.schema).json("/tmp/foo.json")
df1.where($"k" === "bar")
ama eşdeğer değildir DataFrame.repartition
. Özellikle aşağıdaki gibi toplamalar:
val cnts = df1.groupBy($"k").sum()
yine de gerektirecek TungstenExchange
:
cnts.explain
// == Physical Plan ==
// TungstenAggregate(key=[k#90], functions=[(sum(v#91),mode=Final,isDistinct=false)], output=[k#90,sum(v)#93])
// +- TungstenExchange hashpartitioning(k#90,200), None
// +- TungstenAggregate(key=[k#90], functions=[(sum(v#91),mode=Partial,isDistinct=false)], output=[k#90,sum#99])
// +- Scan JSONRelation[k#90,v#91] InputPaths: file:/tmp/foo.json
bucketBy
DataFrameWriter
(Spark> = 2.0) içindeki yöntem :
bucketBy
ile benzer uygulamalara sahiptir, partitionBy
ancak yalnızca tablolar ( saveAsTable
) için kullanılabilir . Gruplama bilgileri, birleştirmeleri optimize etmek için kullanılabilir:
// Temporarily disable broadcast joins
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
df.write.bucketBy(42, "k").saveAsTable("df1")
val df2 = Seq(("A", -1.0), ("B", 2.0)).toDF("k", "v2")
df2.write.bucketBy(42, "k").saveAsTable("df2")
// == Physical Plan ==
// *Project [k#41, v#42, v2#47]
// +- *SortMergeJoin [k#41], [k#46], Inner
// :- *Sort [k#41 ASC NULLS FIRST], false, 0
// : +- *Project [k#41, v#42]
// : +- *Filter isnotnull(k#41)
// : +- *FileScan parquet default.df1[k#41,v#42] Batched: true, Format: Parquet, Location: InMemoryFileIndex[file:/spark-warehouse/df1], PartitionFilters: [], PushedFilters: [IsNotNull(k)], ReadSchema: struct<k:string,v:int>
// +- *Sort [k#46 ASC NULLS FIRST], false, 0
// +- *Project [k#46, v2#47]
// +- *Filter isnotnull(k#46)
// +- *FileScan parquet default.df2[k#46,v2#47] Batched: true, Format: Parquet, Location: InMemoryFileIndex[file:/spark-warehouse/df2], PartitionFilters: [], PushedFilters: [IsNotNull(k)], ReadSchema: struct<k:string,v2:double>
* Bölüm düzeni ile sadece bir veri dağıtımını kastediyorum. partitioned
RDD'nin artık bir bölümleyicisi yoktur. ** Erken bir tahmin olmadığı varsayılır. Toplama yalnızca küçük bir sütun alt kümesini kapsıyorsa, muhtemelen herhangi bir kazanç yoktur.