Кирпичи данных Spark CREATE TABLE - это навсегда для 1 миллиона маленьких файлов XML - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть набор 1 млн. XML-файлов, каждый из которых имеет размер ~ 14 КБ в хранилище BLOB-объектов Azure, смонтированных в блоке данных Azure, и я пытаюсь использовать CREATE TABLE,с ожиданием одной записи для каждого файла.

Эксперимент

Структура содержимого файлов изображена ниже.Для простоты и производительности все содержимое файлов, кроме элемента <ID>, остается идентичным.

<OBSERVATION>
  <HEADER>...</HEADER>
  <RESULT>
    <ID>...</ID>
    <VALUES>...</VALUES>
  </RESULT>
</OBSERVATION>

Для анализа / десериализации я использую spark-xml от Databricks.В настоящий момент я ожидаю записи с двумя столбцами HEADER и RESULT, что я и получаю.

CREATE TABLE Observations
USING XML
OPTIONS (
  path "/mnt/blobstorage/records/*.xml",
  rowTag "RESULT",
  rootTag "OBSERVATION",
  excludeAttribute True
)

Проблема

Оператор CREATE TABLE выполняется в течение 5,5 часов (SQL-запрос с именем sql at SQLDriverLocal.scala:87 в пользовательском интерфейсе Spark), из которых только 1 час тратится в заданиях Spark (на вкладке Заданияинтерфейса Spark).

Я заметил, что ячейка с командой CREATE TABLE остается застрявшей на Listing files at "/mnt/blobstorage/records/*.xml" в течение большей части времени.Сначала я подумал, что это проблема масштабирования в разъеме хранения.Однако я могу запустить команду для ~ 500K файлов JSON аналогичного размера в ~ 25 с ( Проблема с XML против JSON? ).

Я также знаю, что spark-xml читает все файлы для вывода схемы, что может быть узким местом.Чтобы исключить эту возможность, я попытался:

  • предопределить схему (только из первого XML-файла)
  • в качестве открытого текста без анализа (с использованием провайдера TEXT).Одна и та же проблема сохраняется в обоих случаях.

Один и тот же оператор выполняется в 20 с для 10K записей и в 30 мин для 200K записей.При линейном масштабировании (что, очевидно, не происходит), 1 миллион записей были бы сделаны за ~ 33 минуты .

Мой кластер Databricks имеет 1 рабочий узел и 3 узла драйвера, каждый из которых имеет 256 ГБ ОЗУ и 64 ядер, поэтому не должно быть узкого места в кэшировании.Я успешно воспроизвел проблему в нескольких прогонах в течение 4 дней.

Вопрос

Что я здесь не так делаю?Если есть какие-то разделы / кластеры, которые я могу сделать во время CREATE TABLE, как мне это сделать?

1 Ответ

0 голосов
/ 24 февраля 2019

Я догадываюсь, что у вас небольшая проблема с файлами, поскольку вы обрабатываете только 15 ГБ.Я бы объединял маленькие файлы в большие файлы каждый раз.Размер 250 МБ.Поскольку ваш набор данных еще мал, вы можете сделать это на драйвере.Следующий код показывает, как выполняется слияние на узле драйвера (без учета оптимального размера файла):

1.Скопируйте файлы из Blob в локальную файловую систему и сгенерируйте скрипт для слияния файлов:

# copy files from mounted storage to driver local storage
dbutils.fs.cp("dbfs:/mnt/blobstorage/records/", "file:/databricks/driver/temp/records", recurse=True)  

unzipdir= 'temp/records/'
gzipdir= 'temp/gzip/'

# generate shell-script and write it into the local filesystem
script = "cat " + unzipdir + "*.xml > " + gzipdir + """all.xml gzip """ + gzipdir + "all.xml"
dbutils.fs.put("file:/databricks/driver/scripts/makeone.sh", script, True)

2.Запустите скрипт оболочки

%sh
sudo sh ./scripts/makeone.sh

3.Скопируйте файлы обратно в смонтированное хранилище.

dbutils.fs.mv("file:/databricks/driver/" + gzipdir, "dbfs:/mnt/mnt/blobstorage/recordsopt/", recurse=True) 

Еще один важный момент заключается в том, что библиотека spark-xml использует два этапа:

  1. Она анализирует данные, чтобы вывести схему,Если параметр samplingRatio не изменяется, он делает это для всего набора данных.Зачастую этого достаточно сделать только для небольшой выборки, или вы можете предварительно определить схему (используйте для этого схему параметров), тогда вам не понадобится этот шаг.
  2. Чтение данных.

Наконец, я бы порекомендовал хранить данные в паркете, поэтому выполняйте более сложные запросы в формате на основе столбцов, чем непосредственно в xmls, и используйте lib-xml lib для этого этапа предварительной обработки.

...