Как стать автором
Обновить

Hive vs Pig. На что мне столько ETL?

Время на прочтение8 мин
Количество просмотров19K
image

Лучше день потерять, но потом за пять минут долететь (с)



Привет коллеги.
Хочу поделиться с вами соображениями о том, чем отличаются фреймворки Hive и Pig, входящие в экосистему Hadoop. По сути, это два очень похожих продукта, цель у которых одна — взять на себя всю техническую реализацию MapReduce, предоставив взамен возможность описывать процесс обработки данных на более абстрактном уровне. В этой статье мы увидим как выглядят выборки в этих двух системах, и попытаемся понять, в каких случаях надо использовать то или иное решение.

Hive


Итак, начнем с Hive. Его основная фишка — это SQL-подобный язык запросов HQL (Hive query language). Он позволяет работать с данными привычным нам способом, так, как если бы мы работали с обычной реляционной базой. Скрипты можно запускать как через консоль, так и с помощью командной строки.

Hive это:
  • SQL-подобный язык HQL
  • Интерактивная консоль
  • Встроенные функции агрегации
  • Поддержка пользовательских функций (UDF)
  • Данные — как таблица

Hive умеет работать:
  • с текстовыми файлами (можно задать разграничительный символ)
  • с сжатыми текстовыми файлами (Gzip, Bzip)
  • с массивами, словарями, объединениями (union)
  • имеет огромное количество встроенных функций для работы с: коллекциями, датами, строками, JSON-ми
  • с математическими функциями (округление, логарифмы, корни, тригонометрия)
  • с функциями агрегации (sum, min, max, avg...)
  • Если всего перечисленного выше не хватило, то можно использовать кастомные функции, а также мэпперы и редьюсеры (python, java)


Простой пример:
--Создадим внешнюю таблицу. (Описание структуры лога)
CREATE EXTERNAL TABLE win_bids_log (
date_field string,
request_id string,
user_ssp_id string,
dsp_id string,
win_price int
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LOCATION 'hdfs://inpit/bid-logs';

CREATE EXTERNAL TABLE win_bids_by_dsp (
dsp_id string,
win_bids_cout int,
win_price int
) 
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION ''hdfs://output/win-bids-by-dsp'';

INSERT OVERWRITE TABLE win_bids_by_dsp
SELECT dsp_id, COUNT(dsp_id), SUM(win_price) FROM win_bids_log  GROUP BY dsp_id;

Как видите, всё довольно просто и понятно. Довольно таки приятно писать запросы, на знакомом языке. Но продолжается это счастье до тех пор, пока не приходится столкнуться с более сложными запросами.

Пример посложнее:
INSERT OVERWRITE TABLE movieapp_log_stage 
SELECT * FROM ( 
	SELECT custid, movieid, 
	 CASE WHEN genreid > 0 THEN genreid ELSE -1 END genreid, time, 
	 CAST((CASE recommended WHEN 'Y' THEN 1 ELSE 0 END) AS INT) recommended, activity, 
	 CAST(null AS INT) rating, price 
	 FROM movieapp_log_avro 
	 WHERE activity IN (2,4,5,11) 
	UNION ALL 
	SELECT 
	 m1.custid, m1.movieid, 
	 CASE WHEN m1.genreid > 0 THEN m1.genreid ELSE -1 END genreid, m1.time, 
	 CAST((CASE m1.recommended WHEN 'Y' THEN 1 ELSE 0 END) AS INT) recommended, 
	 m1.activity, m1.rating, 
	 CAST(null as float) price 
	 FROM movieapp_log_avro m1 
	 JOIN (
		 SELECT custid,movieid, 
		 CASE WHEN genreid > 0 THEN genreid ELSE -1 END genreid,MAX(time) max_time, 
		 activity 
		 FROM movieapp_log_avro 
		 GROUP BY custid, movieid, genreid, activity 
	 ) m2 
	 ON ( 
		 m1.custid = m2.custid 
		 AND m1.movieid = m2.movieid 
		 AND m1.genreid = m2.genreid 
		 AND m1.time = m2.max_time 
		 AND m1.activity = 1 
		 AND m2.activity = 1 
	 ) 
) union_result;


Разобраться конечно можно, но всё же стоит признать, что в данном случае определенно не хватает какой-то упорядоченности. Разложить бы это всё по полочкам, да с комментариями. Не так ли?

Итого:

Hive плюсы:
  • Старый, добрый SQL — хорош для описания выборок. Да и просто все его знают.
  • MapReduce под капотом. Уходит много оверхеда, связанного с обвязкой вокруг MR. Описание моделей данных, входных и выходных форматов, цепочек MR задач.
  • Интерактивность. Хорош для анализа данных в разных срезах.
  • Быстрота разработки
  • Отсутствие зависимостей, компиляции, сборки (всё это скрыто)


Hive Минусы:
  • Не всё можно уложить в парадигму HQL
  • Это хорошо ложится в голову, при простых выборках. Но с ростом сложности становится всё труднее и труднее их понимать. Особенно если выборку писали не вы


Pig



Поговорим теперь о Pig. Он основан на процедурном языке Pig Latin. Чтобы в нем разобраться нужно потратить какое то время.
Давайте разберемся и походу выясним отличия от Hive

Pig это:
  • язык Pig Latin
  • Интерактивная консоль
  • Встроенные функции агрегации
  • Поддержка пользовательских функций (UDF)
  • Данные — в виде структур (Tuple, Bag)

Pig умеет работать:
  • с текстовыми файлами (можно задать разграничительный символ)
  • с сжатыми текстовыми файлами (Gzip, Bzip)
  • с массивами, словарями, объединениями (union)
  • имеет огромное количество встроенных функций для работы с: датами, строками, структурами
  • с математическими функциями (округление, логарифмы, корни, тригонометрия)
  • с функциями агрегации (sum, min, max, avg...)
  • Если всего перечисленного выше не хватило, то можно использовать кастомные функции (jython, java)

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

Рассмотрим Pig подробнее.
Данный фреймфорк работает со специальными структурами данных — Tuple и Bag.
  • Tuple — упорядоченный набор полей. Структура, к полям которой можно обращаться по индексу и/или имени.
  • Bag — коллекция (множество) Tuple.


Pig Latin базовые функции:
  • LOAD
  • STORE
  • GENERATE
  • JOIN
  • GROUP
  • FILTER
  • UNION
  • DISTINCT
  • ORDER

Давайте рассмотрим на примере, как можно трансформировать данные в процессе работы с Pig. Работать будем с log файлом RTB биржи. Данные представлены в следующем виде:
  • time — врмемя
  • bid_id — идентификатор ставки,
  • user_id — идентификатор пользователя,
  • dsp_id — идентификатор биддера (игрока)
  • bid — ставка

Pig — загрузка данных (LOAD)

Для загрузки используется функция LOAD, также мы указываем разделительный символ '\t' и сигнатуру данных (при необходимости можно указывать тип).
--почистим выходную директорию HDFS (Pig поддерживает команды Hadoop)
fs -rm -f -r -skipTrash /data/pig/out

--загрузим данные в переменную 'raw_data'
raw_data = LOAD '/data/pig/example/' USING PigStorage('\t') AS (time, bid_id, user_id, dsp_id, bid:int);

На выходе мы получим вот такую структуру (Tuple). В запросах к её полям можно обращаться через точку. Например: raw_data.dsp_id
raw_data -> tuple с именованными полями.
------------------------------------------------------------------------------------------- 
time,  bid_id,  user_id,  dsp_id,  bid
------------------------------------------------------------------------------------------- 
(2014.02.14 14:08:27.711,  56949,  45234534553459,  DSP-2,  12)
(2014.02.14 14:08:28.712,  61336,  45221696259999,  DSP-1,  56)
(2014.02.14 14:08:29.713,  74685,  45221699381039,  DSP-2,  89)
(2014.02.14 14:08:30.714,  56949,  45221695781716,  DSP-1,  21)
(2014.02.14 14:08:25.715,  27617,  45221682863705,  DSP-3,  22)

Pig — итеративная обработка данных (FOREACH — GENERATE)
FOREACH — GENERATE позволяет итеративно «бежать» по набору данных и применять к каждой записи какие-либо операции, или просто отдать на выход определенные поля, убрав всё не нужное.
--Нормализуем данные. Обрежем timestamp с помощью SUBSTRING
norm_data = FOREACH raw_data GENERATE SUBSTRING(time, 0,10) AS date, dsp_id, bid;

На выходе получаем то же самое множество, но с обрезанной датой, и только двумя полями: dsp_id, bid.

norm_data -> tuple с именованными полями и обрезанной датой
---------------------------------------
date,   dsp_id,   bid
---------------------------------------
(2014.02.14,   DSP-2,   12)
(2014.02.14,   DSP-1,   56)
(2014.02.14,   DSP-2,   89)
(2014.02.14,   DSP-1,   21)

Pig — группировка данных (GROUP)
GROUP — позволяет группировать данные, при этом выдавая на выход нетривиальную структуру.
--Сгруппируем по dsp_id и date 
group_norm_data = GROUP norm_data BY (dsp_id, date);

На выходе имеем:
группу в качестве ключа. К ней можно обращаться через префикс group.
и коллекцию агрегатов с префиксом norm_data
group_norm_data -> (группа как ключ) : [ (norm_data), (norm_data) ]
----------------------------------------------------------------------------------
 ( group),  array of norm_data
----------------------------------------------------------------------------------
( (DSP-1,  2014.02.14),  {(2014.02.14,  DSP-1,  56),  (2014.02.14,  DSP-1,  21)} )
( (DSP-1,  2014.02.17),  {(2014.02.17,  DSP-1,  34),  (2014.02.17,  DSP-1,  24)} )
( (DSP-2,  2014.02.14),  {(2014.02.14,  DSP-2,  89),  (2014.02.14,  DSP-2,  12)} )

Pig — развертка агрегатов (FLATTEN)
Иногда необходимо развернуть агрегаты в линейную структуру («выпрямить»).
Для этого существует функция FLATTEN
-- Разворачиваем агрегаты в линейную структуру
ft_group_norm_data = FOREACH group_norm_data GENERATE FLATTEN(group), FLATTEN(norm_data);

Из сложной сгруппированной структуры мы получаем прямолинейный набор Tuples.
ft_group_norm_data -> tuple с именованными полями
----------------------------------------------------------------------
dsp_id, date date dsp_id bid
-----------------------------------------------------------------------
(DSP-1, 2014.02.14, 2014.02.14, DSP-1, 56)
(DSP-1, 2014.02.14, 2014.02.14, DSP-1, 21)
(DSP-1, 2014.02.15, 2014.02.15, DSP-1, 15)
(DSP-1, 2014.02.15, 2014.02.15, DSP-1, 31)

Pig — функции агрегации (SUM)
Давайте что-нибудь посчитаем. Например, сумму дневных ставок, сделанных каждым биддером.
--Вычислим сумму дневных ставок, сделанных каждым биддером
sum_bids_dsp = FOREACH group_norm_data GENERATE group, SUM(norm_data.bid) AS bids_sum;


sum_bids_dsp -> группа : bids_sum
------------------------------------------------------
   group,   bids_sum
------------------------------------------------------
( (DSP-1, 2014.02.16),    82)
( (DSP-1, 2014.02.17),    58)
( (DSP-2, 2014.02.14),    101)
( (DSP-2, 2014.02.16),    58)

Pig — GROUP ALL
Часто необходимо посчитать количество «записей» в выборке. Просто применить COUNT к выборке — не удастся. Данные надо свернуть в одну группу и уже затем применить функции агрегации.
--Вычислим общую сумму, и количество групп.
--Для этого свернем всё в одну группу.

group_all = GROUP sum_bids_dsp ALL;


На выходе имеем группу — «all» и коллекцию всех предыдущих агрегатов.
( all, { ((DSP-1,2014.02.14),77), ((DSP-1,2014.02.15),67), ((DSP-1,2014.02.16),82),((DSP-1,2014.02.17),58),((DSP-2,2014.02.14),101),((DSP-2,2014.02.16),58),((DSP-2,2014.02.17),123),((DSP-3,2014.02.14),22),((DSP-3,2014.02.15),109),((DSP-3,2014.02.16),136),((DSP-3,2014.02.17),81) } )

теперь вычислим количество и сумму
summary = FOREACH group_all GENERATE COUNT(sum_bids_dsp), SUM(sum_bids_dsp.bids_sum);

Выход
------------------------------------------------------
  count,   sum
------------------------------------------------------
(11,         914)

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

Итого:

Pig плюсы:
  • Процедурный подход. Упорядоченность! Язык позволяет разбивать логику на блоки, каждый шаг можно развернуто описывать комментариями.
  • Формирование MapReduce под капотом. Уходит много оверхеда, связанного с обвязкой вокруг MR. Описание моделей данных, входных и выходных форматов, цепочек MR задач.
  • Интерактивность. Хорош для анализа данных в разных срезах.
  • Быстрота разработки. Отсутствие зависимостей, сборки

Pig Минусы:
  • Не всё можно уложить в язык Pig Latin
  • Pig Latin вместе со структурами данных более сложен, в отличии от HQL
  • Для UDF используется Jython. Это может ограничить в использовании некоторых библиотек.

Резюме:

  • Hive хорош для небольших и несложных выборок. HQL похож на SQL, поэтому можно очень быстро начать работать с данным фреймворком.
  • Pig Требует изучения языка и структур данных. Но зато, разобравшись один раз, вы получаете более мощный инструмент, в котором легче реализовывать сложные и многоступенчатые выборки. Вы получаете простой и упорядоченный код, с доступными и уместными комментариями.

Если вы и ваши коллеги отлично знаете SQL, работаете с ним ежедневно, и вас не смущают зубодробительные запросы, то Hive это отличное решение. Однако, если вы работаете с SQL эпизодично и ваш data workflow не укладывается в простые запросы, то однозначно стоит потратить день и разобраться с Pig. В дальнейшем это может сэкономить много времени вам, и вашим коллегам.
Теги:
Хабы:
+19
Комментарии38

Публикации

Истории

Работа

Data Scientist
63 вакансии

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн