32
Ефим Пышнограев Как устроено API в AppMetrica

Как устроено API в AppMetrica

Embed Size (px)

Citation preview

Page 1: Как устроено API в AppMetrica

Ефим Пышнограев

Как устроено API в AppMetrica

Page 2: Как устроено API в AppMetrica

Вы узнаете:

› как устроена крупная система аналитики

› какие проблемы возникают при построении отчетов

› как выглядит Reporting API и зачем он нужен

Цели доклада

2

Page 3: Как устроено API в AppMetrica

› Система мобильной аналитики

› На рынке с 2013 года

› Более 4 тыс. активных приложений

› Мы установлены более чем на 250 млн. устройств

› Более 12 млрд. событий в сутки

О сервисе

3

Page 4: Как устроено API в AppMetrica

Архитектура сервиса

4

Пользователь

Разработчик Java backend

Приложение с SDK

Веб интерфейс

Ядро (C++)

ClickHouse

Page 5: Как устроено API в AppMetrica

Агрегированные данные

Что хорошо

〉Все данные есть, отчет строится мгновенно

Что плохо

〉Только предопределенные отчеты

Хранение данных

5

Сырые данные

〉Можно построить отчет произвольной сложности

〉Скорость агрегации становится критичной

Page 6: Как устроено API в AppMetrica

Таблица фактов в ClickHouse

› Open source column-oriented DBMS

› Запросы на диалекте SQL

› 48 машин

› 490 Tb сжатых данных

Вспомогательная информация в MySQL

Хранение данных

6

Page 7: Как устроено API в AppMetrica

› Каждый хост реплицирован в разных ДЦ

› Весь кластер разделен на партиции

› Данные одного приложения находятся на одной партиции

Устройство кластера ClickHouse

7

Page 8: Как устроено API в AppMetrica

ClickHouse поддерживает таблицы с движком Distributed

Запрос в локальную таблицу:

SELECT count() FROM events

Запрос в распределенную таблицу:

SELECT count() FROM events_partition

Локальные и распределенные таблицы

8

Page 9: Как устроено API в AppMetrica

~140 столбцов

Пример таблицы фактов

9

Date AppID DeviceID AppPlatform CountryID …

2017-06-01 46 268927106 android 6

2017-06-01 46 1292856823 android 43

2017-06-01 87 5341985450 iOS 0

2017-06-02 22 876028365 windows 43

Page 10: Как устроено API в AppMetrica

Раньше:

› Одна таблица events со столбцом EventType

Сейчас:

Таблица под каждый тип события

› Общие события

› Клиентские события

› Атрибуция — клики и установки

› Рассылки push уведомлений

Эволюция хранения данных

10

Page 11: Как устроено API в AppMetrica

Используется интерфейсом и клиентами напрямую

Формат языка запросов должен позволять

› Покрывать большинство полезных отчетов

› Скрывать сложность SQL запросов

› Скрывать реальную модель данных

› Эффективно использовать ClickHouse

Язык запросов

11

Page 12: Как устроено API в AppMetrica

› Написано на Java

› 6 машин в 3 ДЦ

› Более 200 тыс. запросов в сутки

› Каждый запрос обрабатывает в среднем 100 млн. строк

Reporting API

12

Page 13: Как устроено API в AppMetrica

Пример отчета

Количество пользователей приложения с системами iOS и Android с февраля по май 2017 года 13

0

250

500

750

1000

February March April May

iOS Android

Page 14: Как устроено API в AppMetrica

Количество пользователей

14

SELECT

uniq(DeviceID) AS users

FROM events_partition

WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

id date1 date2 metrics

46 2017-06-01 2017-06-07 e:users

Page 15: Как устроено API в AppMetrica

Количество пользователей и событий

15

SELECT

uniq(DeviceID) AS users,

count() AS events

FROM events_partition

WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

id date1 date2 metrics

46 2017-06-01 2017-06-07 e:users,e:events

Page 16: Как устроено API в AppMetrica

Пользователи по странам

16

SELECT

CountryID AS country,

uniq(DeviceID) AS users

FROM events_partition

WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

GROUP BY country

metrics dimensions

e:users e:country

Page 17: Как устроено API в AppMetrica

Пользователи с ОС Android по странам

17

SELECT

CountryID AS country,

uniq(DeviceID) AS users

FROM events_partition

WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

AND AppPlatform = 'android'

GROUP BY country

metrics dimensions filters

e:users e:country os == ‘android’

Page 18: Как устроено API в AppMetrica

1. Запрос отправляется на произвольный хост партиции

2. Хост отправляет запрос на все локальные таблицы партиции

3. Все хосты возвращают частичные агрегаты из своих локальных данных

4. Первый хост соединяет эти агрегаты и возвращает результат

Запрос в одну распределенную таблицу

18

1

2

3

4

Java backend

Page 19: Как устроено API в AppMetrica

› Партиция линейно масштабируется

› Работают очень эффективно

› Нельзя строить сложные сегменты

Запрос в одну распределенную таблицу

19

Page 20: Как устроено API в AppMetrica

Пользователи, получившие рассылку

20

SELECT CountryID AS country, uniq(DeviceID) AS users FROM events_partition WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’)) AND AppPlatform = 'android' AND DeviceID GLOBAL IN ( SELECT DeviceID FROM push_campaigns_partition WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

AND CampaignID = 888 ) GROUP BY country

metrics dimensions filters

e:users e:country os == ‘android’ and exists pc:device with (campaign == ‘BlackFriday’)

Page 21: Как устроено API в AppMetrica

1. Запрос отправляется на произвольный хост нужной партиции

2-3. Хост вычисляет внутренний подзапрос в распределенную таблицу

4. Отправляет внешний запрос на все хосты вместе с результатом подзапроса

5-6. Получает частичные агрегаты и соединяет их. Возвращает результат

Запрос с GLOBAL IN

21

1

23

4

Java backend

5

6

Page 22: Как устроено API в AppMetrica

› При расширении кластера загружается сеть

› Работают менее эффективно

› Можно строить сегменты любой сложности

Запрос с GLOBAL IN

22

Page 23: Как устроено API в AppMetrica

Локальность данных по DeviceID

23

SELECT CountryID AS country, uniq(DeviceID) AS users FROM events_partition WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’)) AND AppPlatform = 'android' AND DeviceID IN ( SELECT DeviceID FROM push_campaigns WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

AND CampaignID = 888 ) GROUP BY country

metrics dimensions filters

e:users e:country os == ‘android’ and exists pc:device with (campaign == ‘BlackFriday’)

Page 24: Как устроено API в AppMetrica

› Замена глобальных IN и JOIN на локальные ускоряет запросы и разгружает сеть

› Имеет смысл если почти все наши IN и JOIN происходят по полю DeviceID

› Но при этом нужно уметь делать перешардирование

Локальность данных по DeviceID

24

Page 25: Как устроено API в AppMetrica

Что делать если крупное приложение хочет посмотреть сложный отчет за несколько лет?

› Движок таблицы MergeTree поддерживает семплирование

› Ключ семплирования должен быть равномерно распределен

Запросы без семплирования и с семплированием

SELECT count() FROM events

SELECT count() * 10 FROM events SAMPLE 1/10

Семплирование в отчетах

25

Page 26: Как устроено API в AppMetrica

SELECT count() * 3 FROM events WHERE DeviceID % 3 = 0

SELECT count() * 3 FROM events SAMPLE 1/3

Семплирование в отчетах

26

Page 27: Как устроено API в AppMetrica

› Ключ семплирования должен быть одинаковым во всех таблицах

› Ключ должен быть привязан к столбцу, по которому делается IN

Семплирование в отчетах

27

SELECT count() * 10

FROM events

SAMPLE 1/10

WHERE DeviceID IN (

SELECT DeviceID

FROM push_campaigns

SAMPLE 1/10

)

Page 28: Как устроено API в AppMetrica

Адаптивное семплирование

28

SELECT

uniq(DeviceID) * (42/13) AS users

FROM events_partition

SAMPLE 13/42

WHERE AppID = 46 AND (Date BETWEEN toDate(‘2017-06-01’) AND toDate(‘2017-06-07’))

accuracy metrics

medium e:users

Page 29: Как устроено API в AppMetrica

› Оценить сколько строк будет обработано запросом

› Выставить коэффициент семплирования так, чтобы обработанных строк стало не больше N

› Делать это быстро

Адаптивное семплирование

29

Page 30: Как устроено API в AppMetrica

Если запрос не помещается в память

SELECT … FROM events GROUP BY DeviceID

SELECT … FROM events SAMPLE 1/3 OFFSET 0 GROUP BY DeviceID

SELECT … FROM events SAMPLE 1/3 OFFSET 1/3 GROUP BY DeviceID

SELECT … FROM events SAMPLE 1/3 OFFSET 2/3 GROUP BY DeviceID

Разбиение запроса на части

30

Page 31: Как устроено API в AppMetrica

› Мы храним данные в ClickHouse и строим отчеты на лету

› Мы сделали свой DSL для описания отчетов

› Не все отчеты можно выразить через Reporting API

› Есть возможность выгрузки сырых логов

Заключение

31

Page 32: Как устроено API в AppMetrica

Ефим Пышнограев Разработчик

badgersow

[email protected]

telegram

Спасибо за внимание!