Обзор

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

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

В этой статье я напишу о ключевой логике разделения в потоках данных инструментов ETL. Эта логика немного отличается от атомарной многопоточной пакетной обработки в многопоточной разработке на Java или C #. Обычно в многопоточном пакетном процессе разработанный код обрабатывает атомарные единицы и применяет бизнес-правила к каждой из них за раз. Если для каждой атомарной единицы требуется процесс поиска в таблице базы данных, это означает, что все запущенные параллельные потоки будут выполнять независимые запросы к одной и той же таблице базы данных или будут выполнять один и тот же цикл над данными независимо (каждый поток может выполняться на разных этапах процесса). Конечно, этот подход к потоку данных можно разработать с помощью Java или C #.

Логика, связанная с разделением данных, в любом случае не очень сложна, она требует хорошего планирования потока данных и глубокого понимания необходимого количества поисков на каждом этапе. Прежде всего, я должен упомянуть, что в некоторых инструментах этот процесс разделения данных прозрачен для конечных пользователей, в то время как в других он должен выполняться вручную. Как обычно, нужно хорошо знать возможности инструментов. Я из мира IBM и SSIS, несколько лет назад IBM Datastage потребовала, чтобы этот подход был реализован вручную с использованием так называемых общих контейнеров, поскольку версия 8 IBM datastage обрабатывает разбиение данных гораздо более удобным для пользователя способом (но в фоновом режиме он использует этот тип логики). В Microsoft SQL Server TSQL единственный вариант, который я использовал для имитации многопоточности, - это параллельный запуск задания с помощью агента SQL Server, в Oracle PL-SQL у вас есть аналогичный подход к запуску заданий и отличный пакет DBMS_PARALLEL_EXECUTE. В любом многопоточном языке программирования (Java, C #) вам нужны производитель потоков и средство запуска, а также механизм, который отслеживает их состояние (возможно даже, что каждый поток может поддерживать свое состояние в таблице базы данных, а другой поток отслеживает состояние этой таблицы) . В SSIS можно использовать либо компоненты сценария, использующие классы C #, либо главный пакет, который можно вызывать параллельно, или даже параллельный вызов процедур TSQL. Так что вариантов много.

Что касается разделения данных, у меня есть три золотых правила: распределение обработки данных, время между объединением процессов и количество максимально доступных параллельных потоков (связанных с ядрами машины). Если внимательно посмотреть на картинку ниже

Легко понять, что базовый шаблон проектирования, который должен использовать каждый разработчик, обеспечивает обработку независимых наборов потоков данных. Это означает, что они не могут пересекаться (это очень важно), и все они должны обрабатывать почти одинаковое количество строк, отсюда и распределение обработки данных. От одного шага поиска к следующему каждый разработчик должен оценить, должна ли логика разделения быть такой же или должна измениться при просмотре существующих ключей поиска. Читатель также должен учитывать, что разделение данных может также (в некоторых случаях действительно…) распространяться на поисковые наборы (зеленые прямоугольники), поскольку также может быть огромный выигрыш, если поисковые наборы также будут разделены (представьте таблицу поиска на пятьдесят миллионов строк! Если разбить его на N меньших таблиц в памяти, поиск будет быстрее). Также возможно использовать логическое разбиение обработки и отобразить его на физическом уровне, рассмотреть методы разбиения таблиц SQL Server или Oracle. Кроме того, большинство новейших механизмов обработки данных, таких как hadoop, используют методы глубокого разделения данных. Как можно разделить данные? На мой взгляд, если рассматривать каждую строку данных как независимое статистическое событие, следует искать единообразное распределение данных, так как это обеспечит сбалансированность обработки данных. Это означает, что вероятность каждого события должна быть почти одинаковой. Если представить это схематически, то получим:

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

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

Возможная логика разделения состоит в том, чтобы получить код ascii для каждой последней буквы города и оттуда вычислить остаток от деления на 3. В последнем столбце мы получим возможные значения 0, 1 и 2. В правой таблице я представляю абсолютные частоты для этого очень небольшого набора данных. Впечатляет, что они различаются только одной цифрой, что означает, что если каждая из них соответствует количеству строк, обрабатываемых каждым потоком данных, этот простой подход обеспечит обработку почти одинакового количества записей и сбалансированность потоков данных. По мере увеличения размера базового набора распределение стремится к равномерному. В любом случае возможное распределение данных должно быть проверено до принятия решения о внедрении.

Заключение

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

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