Январь 2016

Том 31 номер 1

Большие данные - Упрощение пакетного анализа больших данных с помощью U-SQL

Майкл Рис | Январь 2016

Продукты и технологии:

Azure Data Lake Tools for Visual Studio, U-SQL, C#, Azure Data Lake

В статье рассматриваются:

  • преимущества языка U-SQL для работы с большими данными (Big Data);
  • простота добавления пользовательского кода в U-SQL с помощью C#;
  • применение Visual Studio для ускорения разработки на U-SQL;
  • способы использования U-SQL для сохранения и анализа структурированных и неструктурированных данных.

Новые сервисы Microsoft Azure Data Lake для аналитики в облаке (bit.ly/1VcCkaH) включают репозитарий с высочайшей масштабируемостью (hyper-scale), новый сервис анализа, созданный в YARN (bit.ly/1iS8xvP) (позволяет разработчикам и специалистам по обработке данных анализировать все данные), и HDInsight (bit.ly/1KFywqg), полностью управляемый сервис Hadoop, Spark, Storm и HBase. Azure Data Lake Analytics также включает U-SQL — язык, унифицирующий SQL с выразительностью вашего кода. Масштабируемые распределенные запросы U-SQL дают возможность эффективно анализировать данные в хранилище и в таких реляционных хранилищах, как Azure SQL Database. В этой статье я обрисую мотивацию разработки U-SQL, некоторые проектировочные решения, заложенные в этот язык, а также дам несколько примеров основных аспектов этого языка.

Почему U-SQL?

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

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

Насколько существующий стек языков для обработки больших данных отвечает этим требованиям?

Языки на основе SQL, например Hive (hive.apache.org), предоставляют декларативный подход, который естественным образом обеспечивает масштабирование, параллельное выполнение и оптимизации. Это делает их простыми в использовании, они знакомы широкому кругу разработчиков и весьма эффективны для многих стандартных типов аналитики и управления хранилищами данных. Однако их модель расширения и поддержка неструктурированных данных и файлов зачастую ограниченны и труднее в применении. Например, даже если вы лишь хотите быстро исследовать данные в файле или удаленном источнике данных, вам нужно создавать объекты каталога для схематизации файловых данных или удаленных источников, прежде чем вы сможете запрашивать их, а это уменьшает гибкость. И хотя в языках на основе SQL обычно есть несколько точек расширения для пользовательских средств форматирования (formatters), пользовательских функций (user-defined functions, UDF) и агрегаторов, они довольно сложны в создании, интеграции и сопровождении и имеют разную степень согласованности в моделях программирования.

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

Удовлетворение требований с помощью U-SQL

Принимая во внимание недостатки процедурных языков на основе SQL, U-SQL был спроектирован с нуля в развитие декларативного SQL-языка с «родной» расширяемостью через пользовательский код, написанный на C#. U-SQL унифицирует следующее:

  • декларативную и императивную парадигмы кодирования;
  • возможность расширения языковых средств пользовательским кодом;
  • обработку всех данных — неструктурированных и структурированных;
  • обработку данных в Azure Data Lake и других источниках данных Azure, используя федеративные запросы (federated queries).

U-SQL опирается на внутренний опыт Microsoft в отношении декларативного и расширяемого скриптового языка (bit.ly/1OGUNIY), называемого Structured Computations Optimized for Parallel Execution, или SCOPE, и существующих языков, таких как T-SQL, ANSI SQL и Hive. Например, мы базируем интеграцию SQL и языка программирования, а также инфраструктуру выполнения и оптимизации для U-SQL на SCOPE, который в настоящее время выполняет в компании сотни тысяч задач ежедневно. Мы также приводим в соответствие систему метаданных (базы данных, таблицы и т. д.), синтаксис и языковую семантику SQL с T-SQL и ANSI SQL — языками запросов, с которыми знакомо большинство наших клиентов SQL Server. И мы используем типы данных C# и языковые выражения C# так, чтобы вы могли бесшовно писать предикаты и выражения на C# внутри выражений SELECT и применять C# для добавления собственной логики. Наконец, мы стремимся к тому, чтобы Hive и другие языки для больших данных выявляли закономерности и соответствовали требованиям к обработке данных, и интегрируем их в нашу инфраструктуру.

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

Покажите мне U-SQL

Предположим, что я скачал с Twitter всю историю своих твитов, ретвитов и упоминаний (mentions) в виде CSV-файла и поместил его в хранилище Azure Data Lake. Для предварительного просмотра этого файла и понимания его структуры можно использовать Azure Data Lake Tools for Visual Studio. Я могу делать это с помощью Server Explorer для поиска моих учетных записей Data Lake Storage и открыть обозреватель (explorer) в своей учетной записи хранилища по умолчанию. На рис. 1 показан поток данных (в моем случае это CSV-файл). В U-SQL я использую формат позднего связывания со своими потоками данных, так что это предварительное представление просто отображает столбцы на основе разделителя, но не сообщает их типы.

Предварительное представление потока данных в Visual Studio
Рис. 1. Предварительное представление потока данных в Visual Studio

В этом случае я знаю схему данных, которые мне нужно обрабатывать, и для начала я хочу просто подсчитать количество твитов для каждого автора в «сети» твитов. Скрипт на U-SQL (рис. 2) демонстрирует три основных этапа обработки данных с помощью U-SQL.

  1. Извлекаем данные из источников в наборы строк (rowsets). Заметьте, что вы просто схематизируете это в своем запросе выражением EXTRACT. Типы данных основаны на типах данных C#, и скрипт использует встроенную библиотеку Extractors для чтения и схематизации CSV-файла.
  2. Преобразуем наборы строк с помощью U-SQL или пользовательских операторов серией манипуляций над такими наборами, каждый из которых зачастую формируется на основе одного или более прошлых наборов строк. В примере на рис. 2 это знакомое SQL-выражение, которое выполняет агрегацию GROUP BY над набором строк, сгенерированным выражением EXTRACT.
  3. Выводим полученные наборы строк либо в файлы, либо в таблицы U-SQL, чтобы сохранить их для дальнейшей обработки.

Рис. 2. Скрипт на U-SQL для извлечения твитов из CSV

1 @t = EXTRACT date string
2            , time string
3            , author string
4            , tweet string
5      FROM "/input/MyTwitterHistory.csv"
6      USING Extractors.Csv();
7
8 @res = SELECT author
9             , COUNT(*) AS tweetcount
10        FROM @t
11        GROUP BY author;
12
13 OUTPUT @res TO "/output/MyTwitterAnalysis.csv"
14 ORDER BY tweetcount DESC
15 USING Outputters.Csv();

Заметьте, что ключевые слова SQL в U-SQL пишутся в верхнем регистре, чтобы обеспечить синтаксическую дифференциацию от синтаксиса выражений C# с теми же ключевыми словами, но другим смыслом. Распространенная ошибка в первых скриптах — использование as вместо AS для назначения псевдонима столбцу в выражении SELECT. Правильная форма — AS буквами верхнего регистра, что соответствует синтаксису выражений SQL. Конечно, эта ошибка будет отловлена при компиляции.

Также заметьте, что каждое выражение назначено какой-либо переменной (@t и @res). Это позволяет U-SQL постепенно шаг за шагом преобразовывать и комбинировать данные потоком выражений, используя функциональную композицию лямбд (подобно тому, что вы найдете в языке Pig [pig.apache.org]). Инфраструктура выполнения потом составляет выражения в единое выражение. Это единое выражение потом можно глобально оптимизировать и горизонтально масштабировать таким способом, который был бы немыслим, если бы выражения выполнялись построчно. На рис. 3 приведен граф выполнения, сгенерированный для одного из запросов в этой статье. Он отражает глобально оптимизированные стадии выполнения с участием компилятора и оптимизатора.

Граф выполнения задачи в Visual Studio
Рис. 3. Граф выполнения задачи в Visual Studio

Интеграция C# в U-SQL

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

Опора U-SQL на систему типов C# и язык скалярных выражений дает автору запроса доступ к богатству библиотек классов, методов, функций, операторов и типов C# и CLR. В скалярных выражениях U-SQL допустимы все C#-операторы, кроме операторов присваивания (=, += и т. д.). В частности, поддерживаются все операторы сравнения, такие как ==, !=, <, >, ??, трехчленное сравнение «условие ? true-выражение : false-выражение». Даже лямбда-выражения, использующие using =>, можно помещать в выражения U-SQL.

Такая очень тесная интеграция и возможности бесшовного программирования поддерживаются и за счет интеграции U-SQL с C#-компилятором Roslyn (bit.ly/1BsPced). По сути, интеграция U-SQL, по-видимому, является самой сложной областью применения платформы C#-компилятора Roslyn.

Код на C# можно использовать для расширения выражений U-SQL несколькими способами.

  • Писать подставляемые (inline) C#-выражения в скрипте U-SQL: это обычно имеет смысл, если нужно применить небольшой набор C#-методов к обработке одного из скалярных значений — метод строкового типа или математическую функцию.
  • Создавать пользовательские функции в C#-сборке и ссылаться на них в скрипте U-SQL: этот вариант предпочтителен для более сложных функций, если логика этих функций требует всей мощи C#, выходящей за рамки его языка выражений, например процедурной логики или рекурсии.
  • Писать пользовательские агрегаторы в C#-сборке и ссылаться на них в скрипте U-SQL: с их помощью можно подключать пользовательскую логику агрегации в логику обработки агрегации U-SQL, применяя блок GROUP BY.
  • Создавать пользовательские операторы (user-defined operators, UDO) в C#-сборке и ссылаться на них в скрипте U-SQL: они являются в U-SQL операторами набора строк, содержащими пользовательский код. Они пишутся на C# и позволяют генерировать, обрабатывать и использовать наборы строк.

Для пользовательских функций, агрегаторов и операторов C#-сборку потребуется загрузить в каталог метаданных U-SQL с помощью CREATE ASSEMBLY (U-SQL), а затем ссылаться в скрипте, используя REFERENCE ASSEMBLY. Azure Data Lake Tools for Visual Studio облегчает процесс регистрации и даже предоставляет поддержку так называемого отделенного кода (codebehind), где вы просто пишете код в специальном C#-файле, подключаемом к данному скрипту; после передачи инструментарий берет на себя всю заботу об инфраструктурном коде.

На рис. 4 показан расширенный запрос для только что упомянутой агрегации. Я использую выражение SELECT с подставляемым C#-выражением LINQ для извлечения «упоминаний» из каждого твита в ARRAY в строках 8–10. Переменная @m (строка 8) теперь содержит набор строк всех ARRAY. Функция EXPLODE в строке 14 преобразует каждый массив в набор строк, содержащий по одной строке на каждый элемент массива. Эта EXPLODE является частью CROSS APPLY, а значит, она будет применяться к каждой строке набора строк @m. Полученный новый набор строк (строка 12) содержит по одному «упоминанию» на строку. Заметьте, что я повторно использую то же имя @m. Мне нужно отбросить ведущий символ @ для соответствия существующим значениям author. Это выполняется другим C#-выражением, где я принимаю Substring с начальной позицией 1 в строке 12. Наконец, я объединяю авторов с упоминаниями в строках 16–21 и расширяю свою агрегацию COUNT для группирования по нечувствительным к регистру букв описателю и категории Twitter (строки 23–27) до вывода результата, упорядоченного по убыванию счетчика твитов в строках 29–31.

Рис. 4. Манипуляции над твитами C#-методами

1 @t = EXTRACT date string
2            , time string
3            , author string
4            , tweet string
5      FROM "/input/MyTwitterHistory.csv"
6      USING Extractors.Csv();
7
8 @m = SELECT new SQL.ARRAY<string>(
9   tweet.Split(' ').Where(x => x.StartsWith("@"))) AS refs
10      FROM @t;
11
12 @m = SELECT r.Substring(1) AS r
13           , "referenced" AS category
14      FROM @m CROSS APPLY EXPLODE(refs) AS t(r);
15
16 @t = SELECT author, "authored" AS category
17      FROM @t
18      UNION ALL
19      SELECT *
20      FROM @m
21      WHERE r != null && r != "";
22
23 @res = SELECT author.ToLowerInvariant() AS author
24             , category
25             , COUNT( * ) AS tweetcount
26        FROM @t
27        GROUP BY author.ToLowerInvariant(), category;
28
29 OUTPUT @res TO "/output/MyTwitterAnalysis.csv"
30 ORDER BY tweetcount DESC
31 USING Outputters.Csv();

Давайте рассмотрим некоторые части подробнее, чтобы понять всю мощь тесной интеграции C# в U-SQL.

Выделенные полужирным части скрипта на рис. 4 указывают некоторые из мест, где U-SQL ожидает и принимает C#-выражения. Как видите, C#-выражения можно использовать в EXTRACT и блоке OUPUT USING, в блоках SELECT и WHERE, в блоках GROUP BY и ORDER BY, а также в функции EXPLODE, хотя в этом примере я просто ссылаюсь на имя столбца в двух последних случаях.

Важный аспект интеграции в том, что C#-выражения имеют полный и бесшовный доступ к скалярным значениям в выражении запроса благодаря тому факту, что они состоят из C#-типов. Например, столбцы tweet в строке 9, r в строках 12, 21 и author в строках 23, 27 — все они бесшовно интегрируются в C#-выражение без необходимости в дополнительном синтаксисе обертывания.

Блоки EXTRACT и OUPUT USING в строках 6 и 31 принимают C#-выражения, дающие в результате экземпляр пользовательского оператора. Два встроенных выражения являются вызовами методов фабрики, которые возвращают экземпляры экстрактора и средства вывода (outputter) соответственно.

Обсудим C#-выражение из строк 8–9 чуть детальнее:

new SQL.ARRAY<string>(tweet.Split(' ').Where(x =>
  x.StartsWith("@")))

Это отличный пример использования C#: встроенный в U-SQL тип SQL.ARRAY<T> на самом деле является C#-типом объекта, который обеспечивает ожидаемые возможности SQL/Hive без функций обновления с побочными эффектами существующего C#-типа Array. Вы лишь используете C#-оператор new для создания нового экземпляра. Экземпляр создается простым применением одной из множества строковых операций над столбцом tweet, который был определен с типом string, для его разбиения на отдельные слова. Больше незачем интересоваться, где вы получаете определенную функциональность строкового типа, как в обычных диалектах SQL: к вашим услугам вся мощь CLR.

Более того, метод Split возвращает IEnumerable<string>. Поэтому любая дальнейшая обработка вроде фильтрации для получения упоминаний из слов в столбце tweet может выполняться с помощью LINQ-выражения и применения лямбда-выражения в качестве предиката. А теперь попробуйте сделать это на стандартном языке SQL!

Также рассмотрим блок WHERE в строке 21. И вновь я могу просто предоставить C#-выражение, которое ссылается на столбцы в наборе строк. Это выражение в данном контексте должно давать результат в виде значения типа bool. Теперь в C# имеется двузначная логика, а не трехзначная в той форме, в какой она реализована в SQL. Таким образом, сравнение r != null вернет true, если r не равна null, или false, если она равна null. Используя логический оператор && из C#, я получаю гарантию сохранения порядка выполнения C# и, что важнее, сокращение, которое не будет выполнять сравнение справа, если первое даст false. U-SQL также поддерживает конъюнкции AND и OR на основе SQL, которые не предоставляют сокращения, но позволяют переупорядочивать предикаты для большей производительности. Все это дает разработчику выбор между более эффективным выполнением и семантической безопасностью.

U-SQL, отделенный код и сборки Visual Studio

Как следующий шаг я могу использовать Azure Data Lake Tools for Visual Studio для рефакторинга C#-кода в C#-функции, задействовав функциональность отделенного кода этого инструментария (рис. 5). После этого, когда я передаю скрипт, он автоматически развертывает код из сопоставленного файла .cs в сервисе. Чтобы иметь возможность ссылаться на методы, типы и функции в U-SQL, классы должны быть определены как открытые, а объекты — как статические открытые.

Отделенный код в Visual Studio
Рис. 5. Отделенный код в Visual Studio

Инструментарий выполняет следующие три операции.

  1. Файл .cs file компилируется в файл сборки.
  2. Пользовательский скрипт U-SQL дополняется заголовком, который добавляет выражение CREATE ASSEMBLY, создающее двоичный контент файлов сборок в вашем каталоге метаданных U-SQL.
  3. Добавляет очистку в конце скрипта для удаления зарегистрированной сборки с помощью выражения DROP ASSEMBLY.

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

По аналогии с реляционными базами данных, например SQL Server, U-SQL предоставляет каталог метаданных и поддерживает стандартные объекты, такие как базы данных, схемы, таблицы и др. Один из них — объект метаданных сборки. С помощью выражения CREATE ASSEMBLY можно зарегистрировать сборку в базе данных. Сборки являются объектами уровня базы данных; DLL-файл сборки помещается в папку сборок внутрь релевантной папки базы данных в папке catalog по вашей основной учетной записи хранилища Azure Data Lake.

Помимо сохранения своей сборки, вы можете указать дополнительные файлы, которые будут сохраняться вместе со сборкой и включаться в код при ссылке на сборки. Синтаксис выражения CREATE ASSEMBLY выглядит примерно следующим образом (подробнее см. справочную документацию по языку U-SQL на bit.ly/1HWw0cc):

Create_Assembly_Statement :=
  'CREATE' 'ASSEMBLY' ['IF' 'NOT' 'EXISTS'] Assembly_Name
  'FROM' Assembly_Source
  ['WITH' 'ADDITIONAL_FILES' '='
    '(' Assembly_Additional_File_List ')'].
Assembly_Name := Quoted_or_Unquoted_Identifier.
Assembly_Source :=
  Static_String_Expression | lexical_binary_value.

Что касается ссылки на сборку, то в U-SQL имеется небольшой набор заранее загружаемых сборок и пространств имен System, включая System и System.Linq. Этот набор поддерживается малым, чтобы уменьшить время компиляции и использование ресурсов. Если вам нужно сослаться на другую системную сборку, вы можете включить ее следующим выражением, которое в данном случае добавляет System.Xml:

REFERENCE SYSTEM ASSEMBLY [System.Xml];

Как только сборка, содержащая функции анализа твитов, зарегистрирована под именем TweetAnalysis, на нее можно ссылаться и использовать ее, как на рис. 6. Мне нужно выполнять несколько больший объем очистки вокруг упоминаний (mentions), помимо простого удаления символа @; ввиду этого сборка содержит и функцию cleanup_mentions, которая не только удаляет @, но и выполняет дополнительную обработку.

Рис. 6. Ссылки на сборку в U-SQL

1 REFERENCE ASSEMBLY TweetAnalysis;
2
3 @t = EXTRACT date string
4            , time string
5            , author string
6            , tweet string
7      FROM "/input/MyTwitterHistory.csv"
8      USING Extractors.Csv();
9
10 @m = SELECT Tweets.Udfs.get_mentions(tweet) AS refs
11      FROM @t;
12
13 @t = SELECT author, "authored" AS category
14      FROM @t
15      UNION ALL
16      SELECT Tweets.Udfs.cleanup_mentions(r) AS r,
               "mentioned" AS category
17      FROM @m CROSS APPLY EXPLODE(refs) AS Refs(r);
18
19 @res = SELECT author.ToLowerInvariant() AS author
20             , category
21             , COUNT(*) AS tweetcount
22        FROM @t
23        GROUP BY author.ToLowerInvariant(), category;
24
25 OUTPUT @res
26 TO "/output/MyTwitterAnalysis.csv"
27 ORDER BY tweetcount DESC
28 USING Outputters.Csv();

U-SQL унифицирует структурированные и неструктурированные данные

Как мы видели до этого момента, U-SQL позволяет очень легко схематизировать файл при чтении, используя выражение EXTRACT. Однако, как только подготовка данных достигает стадии, где их схема известна, имеет смысл обернуть EXTRACT либо в представление, либо в функцию с табличным значением (table-valued function), которая предоставляет параметризованное представление с несколькими выражениями.

На рис. 7 показан новый код. Выражение CREATE FUNCTION в строке 2 создает функцию с табличным значением Tweet_Authors_Mentions, встроенную в U-SQL, с параметром @file, в котором передается значение по умолчанию (строка 4) и возвращается набор строк @res табличного типа с тремя столбцами (полями): author, category и tweetcount (строки 6–11). На этот параметр есть ссылка из строки 20 и последнее присваивание @res результата в строке 34, который будет возвращен этой функцией.

Рис. 7. Параметризованная функция с табличным значением

1 DROP FUNCTION IF EXISTS Tweet_Authors_Mentions;
2 CREATE FUNCTION Tweet_Authors_Mentions
3 (
4   @file string = "/Samples/Data/MyTwitterHistory.csv"
5 )
6 RETURNS @res TABLE
7 (
8    author string
9 ,  category string
10 ,  tweetcount long?
11 )
12 AS BEGIN
13 REFERENCE ASSEMBLY TweetAnalysis;
14
15 @t =
16   EXTRACT date string
17         , time string
18         , author string
19         , tweet string
20   FROM @file
21   USING Extractors.Csv();
22
23 @m =
24   SELECT AzureConDemo.Udfs.get_ref(tweet) AS refs
25   FROM @t;
26
27 @t =
28   SELECT author, "authored" AS category
29   FROM @t
30   UNION ALL
31   SELECT AzureConDemo.Udfs.cleanup(r) AS r,
            "referenced" AS category
32   FROM @m CROSS APPLY EXPLODE(refs) AS t(r);
33
34 @res =
35   SELECT author.ToLowerInvariant() AS author
36        , category
37        , COUNT( * ) AS tweetcount
38   FROM @t
39   GROUP BY author.ToLowerInvariant(), category;
40 END;

Заметьте, что функции U-SQL с табличными значениями всегда подставляются в скрипт запроса, поэтому оптимизатор U-SQL может анализировать и оптимизировать сразу все выражения. Такую функцию можно вызвать, например, так:

1 OUTPUT Tweet_Authors_Mentions(DEFAULT)
2 TO "/Samples/Data/Output/MyTwitterAnalysis.csv"
3 ORDER BY tweetcount DESC
4 USING Outputters.Csv();

Но зачастую подготовленные данные будут храниться как структурированные в таблице U-SQL, которая обеспечивает дополнительные оптимизации хранилища, такие как кластерный индекс и возможность разбиения данных на разделы. Выражения ниже показывают, насколько легко в U-SQL создать таблицу, используя выражение запроса CREATE TABLE AS:

1 DROP TABLE IF EXISTS TweetAuthorsAndMentions;
2 CREATE TABLE TweetAuthorsAndMentions(INDEX idx
3   CLUSTERED(author ASC)
4   PARTITIONED BY HASH(author) INTO 5
5 )
6 AS Tweet_Authors_Mentions(DEFAULT);

Строка 3 указывает, что индекс таблицы кластеризуется по полю author в порядке возрастания, а в строке 4 будет выполнено горизонтальное разбиение внутреннего представления с применением хеширования значений в author на пять разделов. После этого выражения вывод функции Tweet_Authors_Mentions будет сохраняться как новая таблица с этими характеристиками. U-SQL также поддерживает разбиение на разделы по принципу карусели (round robin partitioning) по диапазонам (range partitioning) для схем с горизонтальным разбиением на разделы, а также вертикальное разбиение на разделы, что позволяет управлять разделами на индивидуальной основе.

Теперь таблицу могут использовать другие люди для запроса данных и дальнейшего анализа. Запрос на рис. 8 использует встроенную в U-SQL функцию ранжирования совместно с выражениями диапазонов (windowing expressions) для вычисления медианного количества твитов по автору и категории. Он также вычисляет относительную и абсолютную позицию ранжирования для автора и категорий, в которых есть более 50 твитов.

Рис. 8. Анализ с помощью выражений диапазонов U-SQL

1 @res =
2  SELECT DISTINCT
3     author, category, tweetcount
4   , PERCENTILE_DISC(0.5) WITHIN GROUP (
          ORDER BY tweetcount ASC)
5     OVER (PARTITION BY category) AS
          median_tweetcount_perhandle_category
6   , PERCENT_RANK() OVER
7     (PARTITION BY category ORDER BY tweetcount ASC)
          AS relative_rank
8   , ROW_NUMBER() OVER
9     (PARTITION BY category ORDER BY tweetcount DESC)
          AS absolute_rank
10 FROM TweetAuthorsAndMentions
11 WHERE tweetcount > 50;
12
13 OUTPUT @res
14 TO "/Output/Demo/tweeter_ranking.csv"
15 ORDER BY absolute_rank, category ASC
16 USING Outputters.Csv();

Чтобы лучше понять выражения диапазонов на основе SQL, давайте внимательнее рассмотрим выражения в строках 4–9. Выражение OVER применяет две аналитические функции PERCENTILE_DISC и PERCENT_RANK, а также функцию ранжирования ROW_NUMBER к разделам набора строк (так называемым окнам), указываемым блоком PARITION BY (во всех трех случаях одно и то же разбиение на разделы на основе категории). Две функции, которые вычисляют ранги, также требуют задания порядка данных в каждом окне, чтобы знать, куда помещается значение. PERCENT_RANK подсчитывает относительный ранг строки внутри группы строк, указанной выражением диапазона. ROW_NUMBER вычисляет позицию строки в группе строк. Функция PERCENTILE_DISC рассчитывает конкретный процентиль (percentile) для отсортированных значений в заданном окне на основе дискретного распределения значений полей. PERCENTILE_DISC(0.5) вычислит 50-й процентиль (т. е. медиану) внутри каждого окна с упорядочением количества твитов в порядке возрастания.

Вот почему U-SQL!

Надеюсь, что вы получили некоторое представление о том, почему U-SQL облегчает запросы и обработку больших данных, и что вы поняли идею этого языка. Он обеспечивает много дополнительных возможностей, например:

  • обрабатывает превышение набора (overset) файлов с помощью шаблонов;
  • использует таблицы с вертикальными разделами;
  • выдает федеративные запросы к Azure SQL Database, SQL Data Warehouse и SQL Server в виртуальных машинах Azure;
  • инкапсулирует ваш код U-SQL с помощью представлений и процедур;
  • предоставляет больше SQL-функций диапазонов (windowing functions);
  • позволяет программировать с применением пользовательских операторов на C# (собственные экстракторы, обработчики);
  • поддерживает более сложные типы (MAP, ARRAY).

Пожалуйста, обращайтесь за деталями к справочной документации U-SQL на bit.ly/1HWw0cc.

Подведем итог. U-SQL упрощает обработку больших данных потому, что:

  • унифицирует декларативные запросы с выразительностью вашего пользовательского кода;
  • унифицирует запрашиваемые структурированные и неструктурированные данные;
  • унифицирует локальные и удаленные запросы;
  • увеличивает продуктивность труда и гибкость с первого же дня.

Заключение

U-SQL — это просто один из способов, над которыми Microsoft работает, чтобы сделать сервисы Azure Data Lake самой продуктивной средой для разработки, отладки и оптимизации аналитики в любых масштабах. Благодаря богатой поддержке создания и мониторинга заданий Hive, модели на основе C# для создания заданий Storm (storm.apache.org) для потоков данных в реальном времени и поддержке каждого этапа жизненного цикла задания (от разработки до эксплуатации) сервисы Azure Data Lake позволяют вам в большей мере сосредоточиться на поиске ответов на интересующие вас вопросы, а не на отладке распределенной инфраструктуры. Цель — сделать технологии больших данных максимально проще и доступнее для специалистов в области обработки больших данных, инженеров, аналитиков и разработчиков приложений.


Майкл Рис (Michael Rys) — главный менеджер программ в Microsoft. Занимается языками обработки данных и запросов с 1980-х. Представлял Microsoft в комитетах проектирования XQuery и SQL и вывел SQL Server за рамки обычной реляционной СУБД с помощью XML, геопространственного и семантического поиска. В настоящее время работает над языками запросов к большим данным, такими как SCOPE и U-SQL. Следите за его заметками в Twitter @MikeDoesBigData.

Выражаю благодарность за рецензирование статьи экспертам Microsoft Омиду Афнану (Omid Afnan) и Эду Триё (Ed Triou).