Рассмотрим следующий пример запуска GROUP BY
с относительно большим числом агрегатов и относительно большим количеством групп:
import org.apache.spark.sql.hive.HiveContext
import org.apache.spark.SparkContext._
val h = new HiveContext(sc)
import h.implicits._
val num_columns = 3e3.toInt
val num_rows = 1e6.toInt
val num_groups = 1e5.toInt
case class Data(A: Long = (math.random*num_groups).toLong)
val table = (1 to num_rows).map(i => Data()).toDF
val aggregations = (1 to num_columns).map(i => s"count(1) as agg_$i")
table.registerTempTable("table")
val result = h.sql(s"select a, ${aggregations.mkString(",")} from table group by a")
// Write the result to make sure everyting is executed
result.save(s"result_${num_columns}_${num_rows}_${num_groups}.parquet", "parquet")
Ввод этого задания - всего 8 МБ, выход - около 2,4 ГБ, и я запускаю его на кластере с тремя рабочими машинами с 61 ГБ памяти. Результат: все рабочие сбой с исключениями OutOfMemory.
Даже с более низкими значениями для num_columns
задание становится неоправданно медленным из-за накладных расходов GC.
Мы попытались включить:
- уменьшение размера раздела (уменьшает объем памяти, но увеличивает накладные расходы)
- предварительная разбивка данных с помощью HashPartitioner перед выполнением агрегации (уменьшает потребление памяти, но требует полной перестановки до того, как произойдет какая-либо реальная работа)
Есть ли лучшие способы достижения желаемого эффекта?