Skip to main content
Доменно специфичные базы 
данных и рассылка 
@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 :(