Spark поддерживает четыре языка программирования из коробки Python/Scala/Java/R через API. Сам Spark написан на Scala, поэтому мы выбрали его. В таком случае мы сможем свободно читать исходный код Spark и даже исправлять ошибки в нем (это нам не пригодилось). Кроме того, Scala принадлежит к семейству JVM-языков, что очень удобно при работе с файловой системой Hadoop напрямую через API, так как он написан на Java.
Ниже приведено сравнение языков программирования для Spark, где + и – означают плюсы и минусы языка соответственно.
Scala:
+ функциональный, поэтому очень удобный для обработки данных любых объемов;
+ родной для Spark, это важно, если нужно понимать работу Spark изнутри;
+ основан на JVM, что делает его совместимым с Hadoop;
+ строгая типизация, тогда компилятор поможет найти часть ваших ошибок;
– труден в освоении, нам пришлось разработать свою программу обучения для новичков [19];
– сложный наем – разработчики на Scala дороже, чем на Java или Python;
– сам язык не настолько распространен, как Java или Python.
Python:
+ популярный;
+ простой;
– динамическая типизация, ошибки, которые мог обнаружить компилятор;
– производительность хуже, чем Scala;
– нет чистой функциональности, как у Scala.
Java:
+ популярный;
+ родной для Hadoop;
+ строгая типизация;
– нефункционален (на самом деле после Java 8 все стало намного лучше).
Когда писалась вторая версия рекомендательного движка Retail Rocket, решение выбрать Scala основным языком программирования далось непросто, потому что этот язык никто не знал. Если бы мне пришлось выбирать сейчас, возможно, я бы посмотрел в сторону Java 8 и выше (версий), поскольку Java-разработчиков проще найти, чем тех, кто знает Scala.
Сейчас Spark идет в сторону дата-фреймов и дата-сетов (dataframe dataset), моду на которые сделала библиотека pandas [42] для Python – самая популярная для анализа данных. На них проще писать, но есть один нюанс. Компилятор не может проверить корректность работы с внутренними переменными, что не очень хорошо для больших проектов.
Оптимизация скорости работы
Если пользователя не устраивает скорость ответа аналитической системы, значит, пора оптимизировать скорость работы хранилища. Решение может быть как простым, так и сложным. Первое, что необходимо понять, – можно ли обойтись малой кровью. В реляционных базах данных можно добавить индексы. В базах данных для этого имеются так называемые профайлеры, которые на основе запросов пользователей предложат, какие именно индексы добавить. В колоночной базе данных Clickhouse можно сменить схему партицирования или сделать сэмплирование, когда запрос будет использовать только часть данных. В Hadoop можно выделить больше ресурсов или даже оптимизировать программный код. Я практиковал оба способа.
Кроме этого, есть способ, работающий практически везде. Главный враг скорости запросов – это соединение данных (join) в разных таблицах. Например, у нас есть две таблицы: клиенты и заказы, обе таблицы соединяются через id клиента, имеющийся в обеих таблицах. Почему бы не делать это соединение данных периодически, например по ночам, и сохранять результат обратно в хранилище? Это называется материализованным представлением (materialized view). Тогда клиенты будут обращаться не к данным напрямую, а к их представлению. Скорость будет на порядок выше – это плюс. Минус такого решения – усложнение всей конструкции. Если что-то пойдет не так и таблицы-доноры будут иметь неверные данные, эти представления придется пересчитать. Это нужно будет всегда держать в голове.
Более радикальное решение – сменить технологию на ту, которая больше соответствует задачам. Я уже приводил пример, когда с Hadoop мы перевели пользователей на колоночную базу ClickHouse и получили ускорение в десятки и сотни раз.
Еще один дорогой способ ускорить работу аналитической системы – сделать апгрейд «железа сервера». В системах Hadoop и Clickhouse это легко сделать, просто добавив дополнительные машины. Такая операция называется горизонтальным партицированием (sharding) – данные разбиваются по записям и распределяются по серверам. Запросы к данным будут выполняться параллельно на нескольких серверах, а затем результаты объединяются. Такая схема теоретически может дать линейное ускорение: два сервера будут работать быстрее в два раза по сравнению с одним сервером и т. д.
Архивация данных и устаревание
Следующая часто возникающая проблема с хранилищами данных – большой рост данных, который приводит к нехватке свободного места. Этим нужно постоянно заниматься. Много данных не бывает, но бывает мало бюджета. Какие стратегии существуют?