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

    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. Надалі це може заощадити багато часу вам, і вашим колегам.
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.