Код-ревью Middle Spark спрашивают часто обновлено
Код-ревью: PySpark-задача отчёта по заказам
Вопрос
Это ежедневная PySpark-задача. Найдите проблемы производительности и корректности.
orders = spark.read.parquet("s3://dwh/orders/")
users = spark.read.parquet("s3://dwh/users/")
countries = spark.read.parquet("s3://dwh/countries/")
@F.udf("string")
def norm_country(code):
return code.strip().upper()
df = orders.join(users, "user_id").join(countries, "country_code")
df = df.withColumn("country", norm_country(F.col("country_code")))
df = df.filter(F.col("order_date") == run_date)
print("rows:", df.count())
top = df.groupBy("country").agg(F.sum("amount").alias("revenue"))
rows = df.collect()
top.coalesce(1).write.mode("append").parquet("s3://marts/revenue_by_country/")
Коротко
Главное: фильтр до джойна и идемпотентная запись. Дальше встроенные функции вместо UDF, broadcast справочника, отбор колонок, без лишних действий и без collect. coalesce(1) для маленького агрегата допустим, но для больших результатов он сводит работу к одной задаче.
Что должен найти ревьюер
- Производительностьстрока 11Фильтр по дате стоит после джойнов: соединяются все заказы за всю историю. Фильтровать заказы по дате (и партиции) до джойна, тогда Spark прочитает только нужную партицию.
- Производительностьстрока 6Python UDF для
strip().upper(): есть встроенныеF.upper(F.trim(...)), UDF медленнее и скрывает логику от оптимизатора. - Ошибкастрока 7UDF упадёт на NULL в
country_code: уNoneнет методаstrip. - Производительностьстрока 9Справочник стран крошечный: стоит явно указать
F.broadcast(countries), а изusersвыбрать только нужные колонки до джойна. - Производительностьстрока 13
count()перед агрегацией запускает весь план второй раз; при нескольких действиях нуженcache()или отказ от лишнего действия. - Ошибкастрока 15
collect()всех строк на driver: при реальном объёме driver упадёт по памяти, а результат нигде не используется. - Ошибкастрока 16
mode("append")неидемпотентен: перезапуск за тот же день задвоит выручку. Нужна перезапись партиции дня (dynamic partition overwrite) и колонка даты в результате.
Разбор
Главное: фильтр до джойна и идемпотентная запись. Дальше встроенные функции вместо UDF, broadcast справочника, отбор колонок, без лишних действий и без collect. coalesce(1) для маленького агрегата допустим, но для больших результатов он сводит работу к одной задаче.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.