Доменно специфичные базы данных и рассылка @aviasalesКаплуновский Борис
● Что делает aviasales ● Что делает рассылка aviasales● Зачем нам Domain Specific Database● Детали реализации фильтрующего дерева● Ограничения решения● Масштабирование решения
Agenda
Что делает 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 “”
children – hash {} sorted_keys - array []
property – string “”
Устройство дерева
leaf
callback_url – string “”parent - *
Устройство дерева
Дерево поиска
orig =
MOW LED HKT
HKT
BKK
Дерево поиска
price <
10000 20000 30000 40000
22000
Результаты поиска
Курсы Валют
Информация о OTA
Информация о Аэропортах
Предложения
1 Пересадка 3 Пересадки 0 Пересадок
Количество пересадок <
0 1 2 3
Фильтрующие свойства
Фильтрующие свойства
Результаты поиска
Курсы Валют
Информация о OTA
Информация о Аэропортах
Предложения
1 Пересадка 3 Пересадки 0 Пересадок
Количество пересадок <0 1 2 3
Небольшие уловки
● Все подписки имеют ссылку на родителя для ускорения операции удаления
● Рядом с деревом хранится сортированный массив сслылок на все листья дерева для ускорения операции удаления из дерева
● Результат вычисления свойств документа кешируется
TRFfast in-memory DSD
RailsAppsubscription interface
RailsAppmail composer & sender
SMTP Server
MySQL
Как выглядит система в сборе
Масштабирование по кличеству обрабатываемых документов
Tree1 Tree2
Rules Documents
● Правила вставляются в оба дерева● Обрабатываемый документ отправляется в одно из
деревьев
Масштабирование по кличеству предикатов в дереве
● Правила вставляются в одно из деревьев● Обрабатываемый документ отправляется в оба дерева
Tree1 Tree2
Rules Documents
Ограничения решения
● Алгоритм становится неэффективным если один документ подходит для значительной части дерева
● Дерево хранит данные в памяти, сохранность данных обеспечивается внешними средствами
● Для добавления новых свойств документа нужен перезапуск дерева
Что получилось: технологии
● Два процесса● Два ядра CPU● 1.5gb Памяти● 1 000 000 документов в сутки● 150K Писем в день● Время ответа базы в среднем 28ms
Что получилось: бизнес
● Продукт был запущен через два месяца после начала разработки
● Проект окупился в течении двух месяцев после запуска
● Технология открыла дорогу новым фичам продукта
The End :(