Доменно специфичные базы 
данных и рассылка 
@aviasales 
Каплуновский 
Борис
Agenda 
● Что делает aviasales 
● Что делает рассылка aviasales 
● Зачем нам Domain Specific Database 
● Детали реализации фильтрующего дерева 
● Ограничения решения 
● Масштабирование решения
Что делает aviasales
Фильтры рассылки 
Предпочитаемые 
авиакомпании 
Пункты 
вылета/назначения 
Стоимость 
билетов 
Даты 
вылета/прилёта 
Страна 
назначения 
Месяца 
вылета/прилёта 
Длительность 
прибывания 
Количество 
пересадок 
Номера 
рейсов 
Аэропорты 
пересадок
Движок рассылки должен 
● 1 000 000 раз в сутки 
● Проверить 1mb документ 
● На соответствие ~1 000 000 предикатам 
И не вспотеть!
Структура документа 
Результаты поиска 
Курсы Валют 
Информация 
о OTA 
Информация 
о Аэропортах 
Предложения 
Предложение 
Цена OTA 1 Цена OTA 2 
Сегмент 1 
Перелёт 1 
HKT HKG 
Перелёт 1 
HKG DME 
Сегмент 2 
Перелёт 1 
DME BKK 
Перелёт 1 
BKK HKT
Структура подписок 
orig='MOW' and dest='HKT' and price<40000 
orig='MOW' and dest_country='US' and price<60000 
orig='LED' and switch_count<2 and price<2000 
orig='LED' and 'CDG' in switch_airports 
И подобных выражений сотни тысяч...
Требования к базе данных 
● Проверка на соответствие документа всем 
предикатам за <50ms 
● Низкое потребление ресурсов 
● Простота интеграции 
● Расширяемость 
● Масштабируемость
Почему не подходит SQL 
● Неоднородные наборы предикатов 
● Нетипичные для SQL входные данные – 
огромное дерево 
● Инверсия логики: 
● В SQL хранят данные и выбирают данные 
соответствующие предикатам запроса 
● Для рассылки в базе надо хранить предикаты 
и проверять соответсвие пришедших данных 
предикатам
Ловушки: Протокол взаимодействия 
● Программисты любят придумывать новые 
протоколы mysql/postgres/redis/memcached 
● Изобретение протокола - трата времени 
● Протоколы на все случаи уже изобретены
Протокол взаимодействия HTTP 
● Не надо писать клиент и сервер 
● Удобно дебажить и тестировать 
● Проксируется балансируется 
кешируется 
● Существуют реализации для 
всего
Ловушка: низкоуровневый язык 
● Реализация на Java/C/Go/Erlang чтобы 
быстро работало! 
● Скорость - следствие хороших 
алгоритмов, а не языка 
● Низкоуровневые языки громоздки 
● Их сложно отлаживать
Язык реализации Python 
( ) 
● Можно быстро написать прототип и 
проверить эффективность алгоритмов 
● Много сторонних библиотек 
● Компактный и читаемый код
Ловушка: одно решение для всего 
● Программисты любят разрабатывать 
комбайны решающие все проблемы на 
земле 
● Потеря фокуса 
● Сложно заставить комбайн решать все 
задачи одинаково хорошо 
● Создание комбайнов ВСЕГДА приводит 
к долгострою
Структура подписки 
orig='MOW' and dest='HKT' and price<4000 
Дизьюнкт Свойство 
документа 
Константа 
Булевый оператор
Структура подписки 
orig='MOW' and dest='HKT' and price<4000 
● У свойств документа разная сложность вычисления 
● Оператор = проще операторов <,> 
● Порядок вычисления дизьюнктов не оказывает 
влияния на результат
Структура подписки 
if (dest='HKT')then 
if(orig='MOW') then 
if(price<4000) 
match 
● Свойства документа вычисляются лениво 
● Пытаемся как можно раньше отсеять как можно 
больше поддеревьев 
● Переупорядочивание дизьюнктов позволяет экономить 
ресурсы
Приоритеты операций 
Дешевле проверка выше приоритет 
= < > 
Больше урезается дерево выше 
приоритет 
ORIGIN, DATE 
Проще расчитывается значение 
свойства выше приорит 
ORIGIN, PRICE
свойства + предикаты = узлы дерева 
ЦЕНА < 
10000 15000 15500 15700 
Свойства 
Оператор 
ВРЕМЯ ПЕРЕСАДКИ > 
80мин 95мин 110мин 200мин 
Константы
Устройство дерева 
node 
op – string “” 
property – string “” 
children – hash {} sorted_keys - array []
Устройство дерева 
leaf 
parent - * callback_url – string “”
Дерево поиска 
orig = 
MOW LED HKT 
HKT 
BKK
Дерево поиска 
price < 
10000 20000 30000 40000 
22000
Фильтрующие свойства 
Количество пересадок < 
0 1 2 3 
Результаты поиска 
Курсы Валют 
Информация 
о OTA 
Информация 
о Аэропортах 
Предложения 
1 Пересадка 3 Пересадки 0 Пересадок
Фильтрующие свойства 
Количество пересадок < 
0 1 2 3 
Результаты поиска 
Курсы Валют 
Информация 
о OTA 
Информация 
о Аэропортах 
Предложения 
1 Пересадка 3 Пересадки 0 Пересадок
Небольшие уловки 
● Все подписки имеют ссылку на родителя для 
ускорения операции удаления 
● Рядом с деревом хранится сортированный массив 
сслылок на все листья дерева для ускорения 
операции удаления из дерева 
● Результат вычисления свойств документа кешируется
Как выглядит система в сборе 
TRF 
fast in-memory DSD 
RailsApp 
subscription interface 
RailsApp 
mail composer & sender 
SMTP Server 
MySQL
Масштабирование по кличеству 
обрабатываемых документов 
● Правила вставляются в оба дерева 
● Обрабатываемый документ отправляется в одно из 
деревьев 
Rules Documents 
Tree1 Tree2
Масштабирование по кличеству предикатов в 
дереве 
Rules Documents 
Tree1 Tree2 
● Правила вставляются в одно из деревьев 
● Обрабатываемый документ отправляется в оба дерева
Ограничения решения 
● Алгоритм становится неэффективным если один 
документ подходит для значительной части дерева 
● Дерево хранит данные в памяти, сохранность данных 
обеспечивается внешними средствами 
● Для добавления новых свойств документа нужен 
перезапуск дерева
Что получилось: технологии 
● Два процесса 
● Два ядра CPU 
● 1.5gb Памяти 
● 1 000 000 документов в сутки 
● 150K Писем в день 
● Время ответа базы в среднем 28ms
Что получилось: бизнес 
● Продукт был запущен через два 
месяца после начала разработки 
● Проект окупился в течении двух 
месяцев после запуска 
● Технология открыла дорогу новым 
фичам продукта
The End :(

Доменно специфичные базы данных и рассылка Aviasales, Борис Каплуновский (Aviasales)

  • 1.
    Доменно специфичные базы данных и рассылка @aviasales Каплуновский Борис
  • 2.
    Agenda ● Чтоделает aviasales ● Что делает рассылка aviasales ● Зачем нам Domain Specific Database ● Детали реализации фильтрующего дерева ● Ограничения решения ● Масштабирование решения
  • 3.
  • 4.
    Фильтры рассылки Предпочитаемые авиакомпании Пункты вылета/назначения Стоимость билетов Даты вылета/прилёта Страна назначения Месяца вылета/прилёта Длительность прибывания Количество пересадок Номера рейсов Аэропорты пересадок
  • 5.
    Движок рассылки должен ● 1 000 000 раз в сутки ● Проверить 1mb документ ● На соответствие ~1 000 000 предикатам И не вспотеть!
  • 6.
    Структура документа Результатыпоиска Курсы Валют Информация о OTA Информация о Аэропортах Предложения Предложение Цена OTA 1 Цена OTA 2 Сегмент 1 Перелёт 1 HKT HKG Перелёт 1 HKG DME Сегмент 2 Перелёт 1 DME BKK Перелёт 1 BKK HKT
  • 7.
    Структура подписок orig='MOW'and dest='HKT' and price<40000 orig='MOW' and dest_country='US' and price<60000 orig='LED' and switch_count<2 and price<2000 orig='LED' and 'CDG' in switch_airports И подобных выражений сотни тысяч...
  • 8.
    Требования к базеданных ● Проверка на соответствие документа всем предикатам за <50ms ● Низкое потребление ресурсов ● Простота интеграции ● Расширяемость ● Масштабируемость
  • 9.
    Почему не подходитSQL ● Неоднородные наборы предикатов ● Нетипичные для SQL входные данные – огромное дерево ● Инверсия логики: ● В SQL хранят данные и выбирают данные соответствующие предикатам запроса ● Для рассылки в базе надо хранить предикаты и проверять соответсвие пришедших данных предикатам
  • 10.
    Ловушки: Протокол взаимодействия ● Программисты любят придумывать новые протоколы mysql/postgres/redis/memcached ● Изобретение протокола - трата времени ● Протоколы на все случаи уже изобретены
  • 11.
    Протокол взаимодействия HTTP ● Не надо писать клиент и сервер ● Удобно дебажить и тестировать ● Проксируется балансируется кешируется ● Существуют реализации для всего
  • 12.
    Ловушка: низкоуровневый язык ● Реализация на Java/C/Go/Erlang чтобы быстро работало! ● Скорость - следствие хороших алгоритмов, а не языка ● Низкоуровневые языки громоздки ● Их сложно отлаживать
  • 13.
    Язык реализации Python ( ) ● Можно быстро написать прототип и проверить эффективность алгоритмов ● Много сторонних библиотек ● Компактный и читаемый код
  • 14.
    Ловушка: одно решениедля всего ● Программисты любят разрабатывать комбайны решающие все проблемы на земле ● Потеря фокуса ● Сложно заставить комбайн решать все задачи одинаково хорошо ● Создание комбайнов ВСЕГДА приводит к долгострою
  • 15.
    Структура подписки orig='MOW'and dest='HKT' and price<4000 Дизьюнкт Свойство документа Константа Булевый оператор
  • 16.
    Структура подписки orig='MOW'and dest='HKT' and price<4000 ● У свойств документа разная сложность вычисления ● Оператор = проще операторов <,> ● Порядок вычисления дизьюнктов не оказывает влияния на результат
  • 17.
    Структура подписки if(dest='HKT')then if(orig='MOW') then if(price<4000) match ● Свойства документа вычисляются лениво ● Пытаемся как можно раньше отсеять как можно больше поддеревьев ● Переупорядочивание дизьюнктов позволяет экономить ресурсы
  • 18.
    Приоритеты операций Дешевлепроверка выше приоритет = < > Больше урезается дерево выше приоритет ORIGIN, DATE Проще расчитывается значение свойства выше приорит ORIGIN, PRICE
  • 19.
    свойства + предикаты= узлы дерева ЦЕНА < 10000 15000 15500 15700 Свойства Оператор ВРЕМЯ ПЕРЕСАДКИ > 80мин 95мин 110мин 200мин Константы
  • 20.
    Устройство дерева node op – string “” property – string “” children – hash {} sorted_keys - array []
  • 21.
    Устройство дерева leaf parent - * callback_url – string “”
  • 22.
    Дерево поиска orig= MOW LED HKT HKT BKK
  • 23.
    Дерево поиска price< 10000 20000 30000 40000 22000
  • 24.
    Фильтрующие свойства Количествопересадок < 0 1 2 3 Результаты поиска Курсы Валют Информация о OTA Информация о Аэропортах Предложения 1 Пересадка 3 Пересадки 0 Пересадок
  • 25.
    Фильтрующие свойства Количествопересадок < 0 1 2 3 Результаты поиска Курсы Валют Информация о OTA Информация о Аэропортах Предложения 1 Пересадка 3 Пересадки 0 Пересадок
  • 26.
    Небольшие уловки ●Все подписки имеют ссылку на родителя для ускорения операции удаления ● Рядом с деревом хранится сортированный массив сслылок на все листья дерева для ускорения операции удаления из дерева ● Результат вычисления свойств документа кешируется
  • 27.
    Как выглядит системав сборе TRF fast in-memory DSD RailsApp subscription interface RailsApp mail composer & sender SMTP Server MySQL
  • 28.
    Масштабирование по кличеству обрабатываемых документов ● Правила вставляются в оба дерева ● Обрабатываемый документ отправляется в одно из деревьев Rules Documents Tree1 Tree2
  • 29.
    Масштабирование по кличествупредикатов в дереве Rules Documents Tree1 Tree2 ● Правила вставляются в одно из деревьев ● Обрабатываемый документ отправляется в оба дерева
  • 30.
    Ограничения решения ●Алгоритм становится неэффективным если один документ подходит для значительной части дерева ● Дерево хранит данные в памяти, сохранность данных обеспечивается внешними средствами ● Для добавления новых свойств документа нужен перезапуск дерева
  • 31.
    Что получилось: технологии ● Два процесса ● Два ядра CPU ● 1.5gb Памяти ● 1 000 000 документов в сутки ● 150K Писем в день ● Время ответа базы в среднем 28ms
  • 32.
    Что получилось: бизнес ● Продукт был запущен через два месяца после начала разработки ● Проект окупился в течении двух месяцев после запуска ● Технология открыла дорогу новым фичам продукта
  • 33.