- PVSM.RU - https://www.pvsm.ru -
Всем привет!
Сегодня я расскажу вам, как мы в hh.ru [1] считаем ручную статистику по экспериментам. Мы посмотрим откуда появляются данные, как мы их обрабатываем и на какие подводные камни натыкаемся. В статье я поделюсь общими архитектурой и подходом, реальных скриптов и кода будет по минимуму. Основная аудитория — начинающие аналитики, которым интересно, как устроена инфраструктура анализа данных в hh.ru. Если данная тема будет интересна — пишите в комментариях, можем углубиться в код в следующих статьях.
О том, как считаются автоматические метрики по АБ-экспериментам, можно почитать в нашей другой статье [2].
Мы анализируем access-логи и любые кастомные логи, которые пишем сами.
95.108.213.12 — - [13/Aug/2018:04:00:02 +0300] 200 «GET /employer/2574971 HTTP/1.1» 12012 "-" «Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)» "-" «gardabani.headhunter.ge» «0.063» "-" «1534122002.858» "-" «192.168.2.38:1500» "[0.064]" {15341220027959c8c01c51a6e01b682f} 200 https 1 — "-" — - [35827][0.000 0]
178.23.230.16 — - [13/Aug/2018:04:00:02 +0300] 200 «GET /vacancy/24266672 HTTP/1.1» 24229 «hh.ru/vacancy/24007186?query=bmw [3]» «Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8» "-" «hh.ru» «0.210» «last_visit=1534111115966::1534121915966; hhrole=anonymous; regions=1; tmr_detect=0%7C1534121918520; total_searches=3; unique_banner_user=1534121429.273825242076558» «1534122002.859» "-" «192.168.2.239:1500» "[0.208]" {1534122002649b7eef2e901d8c9c0469} 200 https 1 — "-" — - [35927][0.001 0]
В нашей архитектуре каждый сервис пишет логи локально, а затем через самописный клиент-сервер логи (в том числе и access-логи nginx) собираются на центральное хранилище (далее logging). К этой машине имеют доступ разработчики и могут вручную грепать логи при необходимости. Но как же в разумное время погрепать несколько сотен гигабайт логов? Конечно, залить их в hadoop!
В hadoop хранятся не только логи сервисов, но и выгрузка prod-базы. Ежедневно в hadoop мы выгружаем некоторую часть таблиц, которые необходимы для аналитики.
Логи сервисов попадают в hadoop тремя путями.
Рассмотрим каждый подход более подробно.
Крон запускает обычный bash-скрипт.
#!/bin/bash
LOGGING_DATE_PATH_PART=$(date -d yesterday +%Y/%m/%d)
HADOOP_DATE_PATH_PART=$(date -d yesterday +year=%Y/month=%m/day=%d)
ls /logging/java/${LOGGING_DATE_PATH_PART}/hh-banner-sync/banner-versions*.log | while read source_filename; do
dest_filename=$(basename "$source_filename")
/usr/bin/rsync --no-relative --no-implied-dirs --bwlimit=12288 ${source_filename} rsync://hadoop2.hhnet.ru/hdfs-raw/banner-versions/${HADOOP_DATE_PATH_PART}/${dest_filename};
done
Как мы помним, в хранилище логов все логи лежат в виде обычных файлов, структура папок примерно такая: /logging/java/2018/08/10/{service_name}/*.log
Hadoop хранит свои файлы примерно в такой же структуре папок hdfs-raw/banner-versions/year=2018/month=08/day=10
year,month,day мы используем в качестве партиций.
Таким образом, нам надо только сформировать правильные пути (строки 3–4), а затем выбрать все нужные логи (строка 6) и с помощью rsync залить их в hadoop (строка 8).
Плюсы этого подхода:
Минусы:
Поскольку мы заливаем логи в хранилище самописным скриптом, то логично было прикрутить возможность лить их не только на сервер, но и в kafka.
Плюсы
Минусы
Отличается от модного только отсутствием kafka. Поэтому наследует все минусы и только некоторые плюсы предыдущего подхода. Отдельный сервис (ustats-uploader) на java периодически читает нужные файлы, делает их предобработку и заливает в hadoop.
Плюсы
Минусы
И вот данные попали в hadoop и готовы к анализу. Давайте немного остановимся и вспомним, что же такое hadoop и почему на нем можно погрепать сотни гигабайт гораздо быстрее, чем обычным grep.
Hadoop — это распределенное хранилище данных. Данные не лежат на каком-то отдельном сервере, а распределены между несколькими машинами, а так же хранятся не в одном экземпляре, а в нескольких — это сделано для обеспечения надежности. Основа скорости обработки данных лежит в изменении подхода в сравнении с обычными базами данных.
В случае обычной БД мы извлекаем данные из нее и отправляем клиенту, который делает какой-то анализ и возвращает аналитику результат. Таким образом, чтобы быстрее считать нам надо иметь много клиентов и параллелить запросы (к примеру, поделить данные по месяцам — и каждый клиент может считать данные за свой месяц).
В hadoop все наоборот. Мы отправляем код (что именно мы хотим посчитать) к данным, и этот код выполняется на кластере. Как мы знаем, данные лежат на многих машинах, таким образом, каждая машина выполняет код только над своими данными и возвращает результат клиенту.
Многие, наверное, слышали о map-reduce [4], но писать код для аналитики не очень удобно и быстро, в то время как писать на SQL гораздо проще. Поэтому появились сервисы, которые умеют превращать SQL в map-reduce прозрачно для пользователя, причем аналитик может и не подозревать, как на самом деле считается его запрос.
В hh.ru для этого мы используем hive и presto. Hive был первым, но мы постепенно переходим на presto, т. к. он гораздо быстрее для наших запросов. В качестве GUI мы используем hue и zeppelin.
Мне удобнее считать аналитику на python в jupyter, это позволяет считать ее одним кликом и на выходе получать правильно оформленные excel-таблицы, что сильно экономит время. Пишите в комментариях, эта тема тянет на отдельную статью.
Вернемся к самой аналитике.
Мы отсылаем email-рассылку, в которой присылаем подходящие вакансии для соискателя (всем же нравятся такие рассылки?). Мы решили немного поменять дизайн письма и хотим понять стало ли лучше. Для этого будем считать:
Напомню, что все, что у нас есть, — это access log и база. Нам надо в терминах переходов по ссылкам сформулировать наши метрики.
Переход — это GET-запрос на hh.ru/vacancy/26646861 [5]. Для понимания, откуда был переход, мы добавляем utm-метки вида ?utm-source=email_campaign_123. Для GET-запросов в access log будет информация о параметрах, и мы можем отфильтровать переходы только из нашей рассылки.
Тут мы могли бы просто посчитать количество откликов на вакансии из рассылки, но тогда статистика была бы неверной, потому что на отклики могло повлиять что-то еще, кроме нашего письма, к примеру, на вакансию была куплена реклама в ClickMe, и поэтому количество откликов сильно выросло.
У нас есть два варианта, как сформулировать количество откликов:
Выбор подхода определяется бизнес-требованиями, обычно достаточно первого варианта, но все зависит от того, что ждет продакт-менеджер.
Лично мне удобнее первый вариант, но, бывает, нужно сделать временную таблицу, зависит от ситуации.
Напоследок поговорим о том, как должен выглядеть результат подсчетов.
В нашем примере есть 2 группы и 2 метрики, которые формируют воронку.
Рекомендации по оформлению результатов:
Считать различные метрики приходится довольно часто, потому что мы практически каждую задачу выпускаем в рамках АБ-эксперимента. Сложного в расчетах ничего нет, после 2-3 экспериментов приходит понимание, как это делать. Помните, что access-логи хранят много полезной информации, которая может сэкономить компании деньги, помочь вам продвинуть свою идею и доказать, какой из вариантов изменений лучше. Главное — суметь эту информацию добыть.
Автор: llev
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/analitika/295262
Ссылки в тексте:
[1] hh.ru: http://hh.ru
[2] другой статье: https://habr.com/company/hh/blog/321386/
[3] hh.ru/vacancy/24007186?query=bmw: https://hh.ru/vacancy/24007186?query=bmw
[4] map-reduce: https://ru.wikipedia.org/wiki/MapReduce
[5] hh.ru/vacancy/26646861: https://hh.ru/vacancy/26646861
[6] hh.ru/applicant/vacancy_response/popup?vacancy_id=26646861: https://hh.ru/applicant/vacancy_response/popup?vacancy_id=26646861
[7] hh.ru/vacancy/26646861?utm-source=email_campaign_123: https://hh.ru/vacancy/26646861?utm-source=email_campaign_123
[8] калькуляторами: http://www.evanmiller.org/ab-testing/sample-size.html
[9] Источник: https://habr.com/post/424251/?utm_campaign=424251
Нажмите здесь для печати.