Как явные разделы таблицы в Databricks влияют на производительность записи?

У нас есть следующий сценарий:

  • У нас есть таблица, содержащая ок. 15 миллиардов записей. Он не был явно разделен при создании.
  • Мы создаем копию этой таблицы с разделами, надеясь на более быстрое чтение определенных типов запросов.
  • Наши таблицы находятся в облаке Databricks, и мы используем Databricks Delta.
  • Обычно мы фильтруем по двум столбцам, один из которых является идентификатором объекта (350 тыс. Различных значений), а второй - датой, когда произошло событие (на данный момент 31 отдельное значение, но увеличивается с каждым днем!).

Итак, при создании нашей новой таблицы мы выполнили такой запрос:

CREATE TABLE the_new_table
USING DELTA
PARTITIONED BY (entity_id, date)
AS SELECT
  entity_id,
  another_id,
  from_unixtime(timestamp) AS timestamp,
  CAST(from_unixtime(timestamp) AS DATE) AS date
FROM the_old_table

Этот запрос выполняется 48 часов и продолжает расти. Мы знаем, что он прогрессирует, потому что мы нашли около 250 тысяч префиксов, соответствующих первому ключу раздела в соответствующем префиксе S3, и, безусловно, в существующих префиксах есть несколько больших файлов.

Тем не менее, у нас возникают некоторые трудности с отслеживанием того, какой именно прогресс был достигнут и сколько времени мы можем ожидать для этого.

Пока мы ждали, мы попробовали такой запрос:

CREATE TABLE a_test_table (
  entity_id STRING,
  another_id STRING,
  timestamp TIMESTAMP,
  date DATE
)
USING DELTA
PARTITIONED BY (date);

INSERT INTO a_test_table
SELECT
  entity_id,
  another_id,
  from_unixtime(timestamp) AS timestamp,
  CAST(from_unixtime(timestamp) AS DATE) AS date
FROM the_old_table
  WHERE CAST(from_unixtime(timestamp) AS DATE) = '2018-12-01'

Обратите внимание, что основное различие в схеме новой таблицы состоит в том, что мы секционировали только по дате, а не по идентификатору объекта. Выбранная нами дата содержит почти ровно четыре процента данных старой таблицы, на что я хочу обратить внимание, потому что это намного больше, чем 1/31. Конечно, поскольку мы выбираем по одному значению, которое оказывается тем же самым, что и мы разбили, мы фактически записываем только один раздел вместо, вероятно, сотни тысяч или около того.

Создание этой тестовой таблицы заняло 16 минут с использованием того же количества рабочих узлов, поэтому мы ожидаем (исходя из этого), что создание таблицы в 25 раз больше займет всего около 7 часов.

Этот ответ, похоже, частично подтверждает, что использование слишком большого количества разделов может вызвать проблему, но основные причины, по-видимому, сильно изменились в последние пару лет, поэтому мы стремимся понять, какие могут быть текущие проблемы; Databricks docs не особо проясняет ситуацию.

На основе опубликованных рекомендаций по скорости запросов для S3, похоже, увеличение количества разделов (префиксов ключей) должно улучшить производительность. Губительные перегородки кажутся нелогичными.

В итоге: мы ожидаем записать много тысяч записей в каждый из многих тысяч разделов. Похоже, что уменьшение количества разделов резко сокращает время, необходимое для записи данных таблицы. Почему это могло быть правдой? Существуют ли какие-либо общие рекомендации по количеству разделов, которые следует создавать для данных определенного размера?


person Jesse Amano    schedule 23.02.2019    source источник


Ответы (3)


Я совсем не специалист по сборщикам данных, но надеюсь, что эти пули могут помочь

Количество разделов

Количество созданных разделов и файлов будет влиять на производительность вашей работы, несмотря ни на что, особенно при использовании s3 в качестве хранилища данных, однако это количество файлов должно легко обрабатываться кластером меньшего размера.

Динамический раздел

Существует огромная разница между динамическим разделением ваших двух ключей вместо одного, позвольте мне попытаться рассмотреть это более подробно.

Когда вы разделяете данные динамически, в зависимости от количества задач и размера данных, может быть создано большое количество небольших файлов на каждый раздел, что может (и, вероятно, повлияет) на производительность следующего задания, которые потребуют использования этих данных, особенно если ваши данные хранятся в формате ORC, паркете или любом другом столбцовом формате. Обратите внимание, что для этого потребуется только задание только для карты.

Проблема, о которой говорилось ранее, решается по-разному, чаще всего при консолидации файлов. Для этого данные перераспределяются с целью создания файлов большего размера. В результате потребуется перетасовка данных.

Ваши запросы

Для вашего первого запроса количество разделов будет 350k * 31 (около 11MM!), Что действительно много, учитывая количество перетасовки и задачу, необходимую для обработки задания.

Для вашего второго запроса (который занимает всего 16 минут) количество требуемых задач и требуемого перемешивания намного меньше.

Количество разделов (перетасовка / сортировка / планирование задач и т. Д.) И время выполнения вашего задания не имеют линейной зависимости, поэтому математика в этом случае не складывается.

Рекомендация

Я думаю, вы уже это поняли, вам следует разделить свою работу etl на 31 один запрос, что позволит оптимизировать время выполнения.

person hlagos    schedule 01.03.2019
comment
Спасибо! Я думаю, что основная идея вашего ответа заключается в том, что планировщику необходимо будет продолжать перемешивать данные до тех пор, пока он не будет уверен, что все записано в правильное место, и сложность этой задачи возрастает с увеличением количества различных места возможны. Ваши рекомендации также чрезвычайно полезны и предлагают общие передовые практики для нашей будущей работы (которая, несомненно, будет включать множество таких же больших и сложных таблиц). - person Jesse Amano; 01.03.2019
comment
Добро пожаловать! Я забыл добавить, как сказал Брэд, я бы рекомендовал разделение по дате, однако, если 100% времени вы будете запрашивать по сущности, я бы порекомендовал сохранить ваши разделы как (сущность, дата), учитывая, что запрос для 1 отдельной сущности будет более эффективно использовать фреймворки, не являющиеся ульями, потому что вы нацеливаетесь на одну отдельную папку вместо N или добавляете дополнительную логику для поиска правильных. - person hlagos; 04.03.2019

Вы должны разделить свои данные по date, потому что похоже, что вы постоянно добавляете данные по мере того, как время идет в хронологическом порядке. Это общепринятый подход к разделению данных временных рядов. Это означает, что вы будете писать в один раздел даты каждый день, и ваши предыдущие разделы даты больше не обновляются (это хорошо).

Вы, конечно, можете использовать вторичный ключ раздела, если это выгодно для вашего варианта использования (например, PARTITIONED BY (date, entity_id)).

Разделение по дате потребует, чтобы вы читали эти данные всегда также по дате, чтобы получить лучшую производительность. Если это не ваш вариант использования, вам необходимо уточнить свой вопрос.

Сколько разделов?

Никто не может дать вам ответ о том, сколько разделов вам следует использовать, потому что каждый набор данных (и обрабатывающий кластер) отличается. Чего вы действительно хотите избежать, так это «перекоса данных», когда одному исполнителю приходится обрабатывать огромные объемы данных, а другим рабочим приходится простаивать. В вашем случае это могло бы произойти, если бы один clientid составлял, например, 20% вашего набора данных. Разделение по дате должно предполагать, что каждый день имеет примерно один и тот же объем данных, поэтому каждый рабочий будет занят одинаково.

Я не знаю конкретно, как Databricks записывает на диск, но в Hadoop я бы хотел, чтобы каждый рабочий узел записывал свою собственную файловую часть, и поэтому производительность записи на этом уровне параллельна.

person Brad    schedule 01.03.2019
comment
Это отличный ответ! Я хочу вручить вторую награду! - person Jesse Amano; 01.03.2019
comment
Мы пытались использовать составной ключ раздела, потому что мы ожидаем запроса по дате в 100% случаев и по идентификатору объекта в 95% случаев. Как вы правильно догадались, данные гораздо более равномерно распределены по датам, чем по событиям, связанным с сущностью, поэтому существует крайний перекос данных, который не учитывался при первой попытке написать эту таблицу. В настоящее время мы обсуждаем, действительно ли раздел id так важен; если это так, мы попробуем ранжировать идентификаторы по количеству строк и писать пакетами, которые содержат идентификаторы с аналогичным ранжированием. Большое спасибо! - person Jesse Amano; 01.03.2019

Мои рекомендации в случае использования секционированных столбцов:

  • Определите количество всех столбцов и выберите те, которые имеют конечное количество по времени, поэтому исключите идентификаторы и столбцы даты.
  • Определите основной поиск по таблице, возможно, это дата или какое-то категориальное поле
  • Сгенерируйте подстолбцы с конечной мощностью, чтобы ускорить пример поиска: в случае дат можно разложить его на год, месяц, день и т. Д., Или в случае целочисленных идентификаторов, разложить их на целочисленное деление из этих идентификаторов% [1,2,3 ...]

Как я упоминал ранее, использование столбцов с высокой мощностью для разделения приведет к снижению производительности из-за создания большого количества файлов, что является наихудшим рабочим случаем.

Желательно работать с файлами, размер которых не превышает 1 Гб, для этого при создании дельта-таблицы рекомендуется занимать coalesce (1)

Если вам нужно выполнить обновления или вставки, укажите наибольшее количество секционированных столбцов, чтобы исключить случайные случаи чтения файла, что очень эффективно для сокращения времени.

person Cristián Vargas Acevedo    schedule 11.09.2020