Tarantool — это СУБД с открытым исходным кодом. Кто угодно может скачать её с GitHub и использовать как в коммерческих приложениях, так и в некоммерческих. Сегодня технический директор Почта@Mail.ru Денис Аникин расскажет о примерах использования этой базы данных. Материал подготовлен по мотивам выступления на конференции FailOver Conference.
Tarantool разрабатывается в Mail.Ru Group уже больше семи лет. Эта СУБД рассчитана на высоконагруженные системы. Её основное отличие: это база данных, которая сочетает в себе свойства настоящей БД — транзакции, репликации, всё что касается надежности, — но при этом она такая же быстрая, как и кэши, например, Memcached или Redis.
В Mail.Ru Group добрая половина продуктов работает на Tarantool. Ему отдаётся предпочтение в тех случаях, когда от СУБД требуются свойства кэша, то есть она должна уметь делать 100 тыс. апдейтов в секунду, у нее должно быть очень хорошее latency — 1 мс или меньше — и так далее. Многие СУБД не удовлетворяют этим критериями. Если используется много шардов, то это не идёт на пользу: перестают работать транзакции, теряется целостность и возникают прочие проблемы. А кэши, в свою очередь, не обладают многими полезными свойствами БД: надежностью хранения данных на диске, транзакциями и так далее. Например, в кэшах обычно нет такой важной штуки, как хранимые процедуры. Они позволяют переносить логику на сторону хранилища данных.
БД + кэш = ?
Конечно, можно жить на высоконагруженных проектах и без Tarantool. Допустим, вам нужны свойства и базы, и кэша в одном флаконе. Тогда можно применить очень популярную схему: поместить кэш поверх БД. Тогда все запросы идут сначала в кэш, если в нём есть нужные данные, то они отдаются пользователю. Если их там нет, то запрос переадресуется в БД. Все обновления идут сразу и в БД, и в кэш, ведь мы не можем что-то хранить в кэше, не сохранив это в базе, иначе эти данные могут быть потеряны.
Такая схема позволяет получить часть свойств СУБД. Например, транзакций и хранения процедур уже не будет, потому что когда появляются две системы, особенно кэш, то ни о каких транзакциях речь не идёт. В каком-то смысле теряется и репликация, потому что данные в базе реплицируются, а в кэш — как бы не совсем. Также теряются хранимые процедуры и прочие свойства БД. Свойства кэша сохраняются тоже частично. Такая система работает быстрее, потому что увеличивается скорость обработки обращений на чтение, но при этом, например, обращения на запись быстрее не становится. Если база данных затормозила, потому что у нее происходит вакуум таблицы или что-то ещё, то система будет тормозить на запись, потому что без БД запись не работает.
В целом, это рабочая схема. Она позволяет получить часть свойств и базы, и кэша в одной системе. Если вам этого достаточно, то такую схему и надо использовать.
Правда, в этом случае возникают две частые проблемы — несогласованные данные и холодный старт. «Несогласованность» означает, что данные в кэше и базе могут оказаться разными, потому что кэш и база не являются репликой друг друга, это просто две отдельные сущности. «Холодный старт» — это ситуация, когда при старте кэша он ещё пустой, данных в нём нет, поэтому все запросы летят в базу, и производительность системы оставляет желать лучшего.
Если вас не смущают эти моменты, то схема «кэш поверх БД» — вполне рабочий вариант. В противном случае целесообразно обратить внимание на Tarantool, потому что в нём все эти проблемы решены изначально. Одна из причин его разработки и заключается в том, чтобы не городить такие сложные гетерогенные системы, состоящие из нескольких хранилищ, а спокойно обходиться одним и хранить в нём все горячие данные.
Движки
У Tarantool есть два движка хранения данных. Один из них — это in-memory движок. Устроен он так: все данные хранятся в памяти, копии данных есть на диске. На диск пишется каждая транзакция просто в log, и время от времени на диск сбрасывается целиком snapshot всей базы. Сбрасывается асинхронно в фоновом режиме. Пока он сбрасывается, база работает, потому что все новые обновления идут в отдельные места. То есть все работает вообще без тормозов. Log транзакций пишется всегда.
Второй движок дисковый. Он позволяет всё хранить на диске. Причем можно использовать как SSD, так и винчестеры. Этот движок вырос из гуглового LevelDB, который был оптимизирован.
Преимущества и недостатки
Tarantool присущи свойства, характерные для кэша:
- Горячие данные.
- Доступность 99,99%.
- Оптимальная работа при высокой параллельной нагрузке.
- Latency:
- 99% запросов < 1 мс
- 99,9% запросов < 3 мс
- Нагрузка на запись — до 1 миллиона транзакций в секунду на одном ядре ЦПУ.
- Не нужно много серверов.
- Оптимальное использование памяти.
- Система работает постоянно, не нужно делать перерыв на профилактические работы.
В основном здесь всё связано с высокой скоростью работы. Традиционные СУБД лишены этих свойств. При этом Tarantool обладает и свойствами классических СУБД:
- Персистентность (надёжность хранения данных на диске).
- Транзакции ACID.
- Репликация (master-slave и master-master).
- Хранимые процедуры.
- Неблокирующие серверные сценарии.
- Удобные бэкапы.
- Выполнение запросов Cursors, Range и Full scan.
- Первичные и вторичные индексы.
- Таблицы.
Опять же, у кэша этих свойств нет, а в Tarantool они присутствуют.
Одни современные БД нацелены на высокую надёжность работы, другие делают упор на скорость работы. Это два разных мира, которые, в основном, не пересекаются. Tarantool — это достаточно успешная попытка объединить оба мира в одном решении.
К недостаткам Tarantool можно отнести следующее:
- У него пока не очень большое сообщество. Прежде чем применять какую-то технологию, каждый человек всегда думает: «Что если она не будет работать? У кого я спрошу?». Разработчики Tarantool стараются быть и отвечать на вопросы везде: в Facebook, на Stack Overflow и так далее. Но с точки зрения ряда пользователей есть риск не получить ответ из-за немногочисленности сообщества.
- Отсутствует консистентный шардинг. Над этим сейчас ведётся работа, чтобы он уже из коробки был нормальный, с поддержкой транзакций.
- Проприетарный протокол.
Примеры использования Tarantool
Все примеры взяты из опыта работы проектов Mail.Ru Group. На самом деле их очень много, но мы рассмотрим только три: систему аутентификации, систему push-уведомлений и систему показа рекламы. Обычно они самые высоконагруженные.
Система аутентификации
Она должна обладать рядом, казалось бы, противоречивых требований.
- Высочайшая востребованность: аутентификация должна проверяться при каждом хите на веб-сайте или в мобильном приложении. Нужно проверить пароль, куки, токен, что угодно, ведь нельзя просто так впускать пользователя. На портале Mail.Ru и в мобильных приложениях количество запросов к системе аутентификации исчисляется миллионами в секунду.
- Latency — время между запросом и ответом от базы данных. Оно должно быть как можно меньше, иначе всё будет тормозить, в том числе и веб-сервер, использующий аутентификацию. Пока он ждёт ответа от БД, он занимает какой-нибудь поток или процесс, данные висят в памяти, и это тоже потребляет серверные ресурсы. То есть медленная система работы аутентификации может потащить за собой кучу проблем, поэтому она должна работать просто моментально.
- Высокая доступность. Если система аутентификации не будет работать в 1% случаев, то и весь сайт не будет работать в 1% случаев.
- Постоянные обращения к хранилищу. Каждый хит в системе аутентификации — это проверка сессии, пароля, токена.
- Защита от brute-force атак и мошенничества. Систему аутентификации постоянно пытаются сломать.
- Почти каждое обращение связано с выполнением транзакции, то есть с необходимостью изменения каких-то данных. Например, при выполнении аутентификации нужно проверить введённые данные, обновить место и время аутентификации, прочие параметры для системы защиты от brute-force. Всё это — прямая транзакция в БД. Эту запись нельзя терять.
- Много неизбежной лишней работы. Когда все пытаются сломать систему, то за кулисами происходит очень много обращений, которые генерируются не пользователями, а средствами взлома. Эти обращения не несут полезный трафик или прибыль. Это лишняя нагрузка. Но их приходится обрабатывать и проверять.
- Большой размер данных. Естественно, в системе аутентификации должна храниться вся пользовательская база.
- Наличие срока действия введённых пользователем данных (expiration). По соображениям безопасности, если пользователь не активен какое-то время, его сессия завершается. Для этого необходимо проверять время начала сессии и наличие активности.
- Надёжность хранения данных (persistence). Очевидно, что если система аутентификации «забудет» часть пользователей в результате потери учётных данных, то это прямой ущерб репутации.
В целом этот набор свойств может выглядеть противоречивым. Какие-то из них обычно реализуются кэшами, а какие-то — базами данных. Система аутентификации должна быть надёжная и долговечная как грузовик, но при этом такая же быстрая, как спортивная машина. И Tarantool пришёлся здесь как нельзя кстати.
Схема работы системы аутентификации Mail.Ru по логину и паролю:
Только при проверке логинов и паролей в Mail.Ru выполняется 50 тыс. транзакций в секунду. Защита от brute-force и система аутентификации каждый раз читают и пишут в Tarantool. Эта суммарная нагрузка достигает примерно миллиона запросов в секунду: от всего портала, от всех мобильных приложений, от всех Ajax- и неAjax-запросов.
При этом сессии обслуживает всего 4 сервера, а профили пользователей — 8 серверов. Не какие-то брендовые, специальные серверы, а самые обычные, с обычными процессорами. Ничего космического.
Система push-уведомлений
Как вы знаете, мобильные приложения любят присылать пользователям push-уведомления, чтобы они оставались в них подольше. То есть это такая хорошая и благодарная штука.
Как устроена система уведомлений в Mail.Ru?
Когда на сервере происходят какие-то события — пришло письмо, сообщение в мессенджер, появилась новость — нужно отправить уведомление на мобильные телефоны конечных пользователей. Напрямую это сделать невозможно. Поэтому Apple и Google предоставляют API для iOS и Android, посредством которых можно дергать мобильные приложения.
Но обращаться к этим API напрямую с сервера нельзя. Почему — об этом чуть ниже. Кроме того, при каждом генерировании события нужно сходить в хранилище, чтобы понять, какому пользователю доставлять это событие. И не забыть прочитать токен, потому что API работают с токенами. И всё это нужно сделать очень быстро.
Также крайне важно сохранить очень низкое latency, поскольку события генерируются из большого количества разных контекстов и серверных окружений. Никогда нельзя тормозить на сервере, ждать секунду-две, потому что иначе все остальные участники процесса по цепочке начнут работать медленно. По этой причине мы к API не напрямую с сервера, а через очередь, тоже работающую на Tarantool. Эта СУБД может предоставлять и сервис очередей, причем персистентных и реплицируемых. То есть при падении машины, при выходе диска из строя, при перезагрузке сервера никто ничего не замечает.
Получается, что сервер работает напрямую только с быстрым хранилищем, а всё медленное расположено дальше, за очередью. Такая схема позволяет быстро всё обрабатывать: суммарное количество запросов в этой системе порядка 200 тыс. в секунду.
Система показа рекламы
Это, наверное, самый высоконагруженный вариант использования Tarantool, и это самая большая ферма, которая есть в Mail.Ru Group. Система отвечает за показ рекламы почти на всех страницах огромного портала, причём рекламных блоков на странице обычно не менее 10.
Чтобы показать каждый рекламный блок, нужно понять, что вообще нужно показывать пользователю. Для этого данные о нём собираются из нескольких разных источников. Всё это агрегируется и формируется результат. Всё это делается для каждого блока, и на это надо потратить меньше одной миллисекунды. Откуда такое требование? Потому что пользователи не захотят, чтобы сервис тормозил из-за рекламы, она и так всех раздражает, но является необходимым злом.
Нагрузка на систему показа рекламы — порядка 3 млн запросов в секунду. Причем 1 млн из них — это изменения, потому что показ рекламы часто приводит к обновлению профиля пользователя.
Краткий вывод
Если вам нужно сочетать свойства БД и кэша, — надежность и скорость, — и если какими-то простыми методами, которыми вы обычно это делаете, не получается этого добиться, то присмотритесь к Tarantool. Скорее всего, он эту проблему решит.
Комментарии (54)
Roman_Kh
01.09.2016 14:29Расскажите, пожалуйста, более подробно, как именно хранятся профили и сессии в базе: это два поля — ключ и профиль целиком, например, как json, или же несколько атрибутов профиля хранятся в кортеже или все атрибуты? А что вы предлагаете делать, когда структура атрибутов меняется?
danikin
01.09.2016 15:01+3Ключ и профиль в виде упакованного value. Так сделано, потому что в те старые времена Тарантул не умел еще работать эффективно с JSON. Сейчас можно заливать JSON. Он хранится внутри эффективно. Кроме того, можно заранее разбить на поля, если уверены, что структура никогда не поменяется. Плюс, сейчас у нас в разработке проект Bastida — это система поверх Тарантула, которая позволяет на том же JSON описывать преобразование полей, так, чтобы на лету менять структуру и чтобы старые приложения бы не надо было переделывать. Bastida обрабатывает 2 миллиона преобразований в секунду на одном ядре процессора, т.е. готова к почти любым нагрузкам.
Roman_Kh
01.09.2016 17:34Денис. спасибо. А когда примерно появится в открытом доступе Bastida?
danikin
01.09.2016 18:09+2Я думаю, что это вопрос месяца-двух.
danikin
01.09.2016 19:48+2Но вы можете начинать использовать упрощенный вариант Bastida. Вот он: https://github.com/tarantool/avro-schema. Начните с ним играться. Если появятся вопросы, то дайте знать — и я вас добавлю в чатик разработчиков и кастомеров Тарантула. Там помогут.
Rogdaik
01.09.2016 15:37-2К недостаткам надо добавить поддержку только одной ОС — Linux. Винда не поддерживается.
danikin
01.09.2016 16:16+2Free BSD еще поддерживается и OS X. Винда в процессе :) Вообще, список всего основного, подо что собраны бинарники, вот тут: https://tarantool.org/download.html. Кстати, поддерживается ARMv7, т.е. можете ставить Tarantool на IoT устройства. И это активно уже используется.
Rogdaik
01.09.2016 16:23FreeBSD, OS X. Все из той же оперы. Есть такие организации, где WIndows Server корпоративный стандарт. Так что накрывайте всех. Берите пример с Redis.
Greendq
01.09.2016 16:59+1А в качестве языка хранимых процедур и запросов что? Своя разработка или что-то существующее? Насколько отличается от SQL?
danikin
01.09.2016 17:38+1Кроме того, можно писать хранимки прямо на C. Это хардкор, конечно. Но такая возможность есть. И мы сами ее используем для расширения функционала. Кроме того, SQL в процессе. Скоро будет.
asdf87
02.09.2016 10:42Я понимаю, что это прозвучит сейчас странно, но все же.
Извините, я в С'ях не силен, а можно на Rust'e?
Scf
01.09.2016 17:55-3Мутновато. В аутентификации у вас мухи с котлетами перемешаны — а требования к хранению профилей сильно отличаются от требований к хранению аутентификационных токенов (или айдишников сессий).
К профилям обращаются редко — только при входе на сайт. А токены можно кешировать локально (на несколько секунд), токены можно терять, токены можно вообще не хранить, если нет нужды в досрочной инвалидации сессий. И даже в этом случае может оказаться предпочтительнее хранить именно досрочно инвалидированные токены.
danikin
01.09.2016 18:05+1Что есть вход на сайт? Ввод логина и пароля? Это крайне редкое действие. Кука живет почти вечно, особенно, если пользователь часто пользуется. К профилям обращаются на каждый хит к любой странице сайта — надо шапку нарисовать, где счетчики, данные про мультиавторизацию и проч. Ключи к сессиям надо хранить на сервере, чтобы на каждый хит сверять. Кэшировать локально где? На фронт-сервере? А что делать, когда запрос пришел на другой фронт-сервер? Опишите, если не сложно, детальнее вашу архитектуру аутентификации, о которой вы говорите.
Scf
01.09.2016 18:30есть база профилей пользователей, которая часто вычитывается и редко обновляется
есть сессионный ключ, который записывается в куку, и фронтенду требуется по этому ключу получить userId, а по нему — профиль для рендера страницы.
Я верно изложил требования?
Теперь — что можно сделать по этому поводу:
- кеш над базой профилей — можно на выделенном железе, можно прямо на фронтенде, если позволяет объем оперативной памяти и механизм инвалидации.
- база профилей имеет специфический профиль нагрузки и легко шардируется — так что, быть может, кеш и не нужен.
- Можно использовать локальный кеш со временем инвалидации порядка 1 секунды — пользователь не заметит (хотя возможны варианты), но бэкенд гарантированно получит не больше одного запроса в секунду с этого пользователя
- sticky sessions — т.е. балансировка по IP. Так можно избежать дублирования информации в локальных кешах, а также проблемы инвалидации.
- если нужны "долгоиграющие" кукисы — то можно подсмотреть у OAuth2 механизм двух токенов. Т.е. по длительным кукисам, которые хранятся в базе, выдается еще один короткоживущий токен (скажем, минута) на основе HMAC userId+expirationTime. При логауте можно просто стереть его из кукисов браузера. Либо хранить короткоживущие токены в in-memory кеше.
danikin
01.09.2016 18:48+1Давайте закроем на секунду глаза на то, что вы непонятно откуда выдумали вот это требование «которая часто вычитывается и редко обновляется», потому что как минимум, счетчики в шапке обновляются часто.
А дальше просто — дело вкуса.
Кто-то предпочитает зоопарки из баз с шардингом и кэшей, а кто-то ставит 8 самых дешевых supermicro сервера с Тарантулом, которые сразу и база и кэш в одном лице обслуживают по профилям весь огромный портал и едят по 15% CPU каждый.
При том, что с зоопарками кто-то получает дополнительные различные чудеса в виде «инвалидации порядка 1 секунды — пользователь не заметит».
А теперь давайте из зоопарка уберем требование «которая часто вычитывается и редко обновляется» и все станет совсем грустно.Scf
01.09.2016 19:02+1С авторизацией разобрались, а вот "нам нужна база, в которую много пишем, много читаем, и чтобы быстро работало" — это уже главный вопрос жизни, вселенной и всего такого :-)
Если не секрет, чем платит тарантул за свою скорость? Вконтакте, например, использует интересную архитектуру СУБД — из read-only "основной базы" + in-memory cache + лог транзакций. Плюсы — работает быстро, минусы — основную базу надо регулярно перестраивать (до того, как закончится память во встроенном кеше) и очень длинный холодный старт, если база перестраивалась давно.
danikin
01.09.2016 19:28+1Просто правильная архитектура. Копия данных хранится в памяти. При этом все изменения пишутся на диск в лог транзакций, плюс периодически сбрасывается снэпшот. Сброс снэпшота ничего не тормозит, делается в фоне. Холодный старт происходит максимально быстро, потому что это просто линейное чтение снэпшота и лога транзакций. По сути, база и кэш в одном лице.
olegator99
01.09.2016 19:02Привет,
Сходу не нашел в доках: есть ли в тарантуле возможность индексации и быстрого поиска по вложеным в строчки массивам?
Сейчас поясню:
Есть набор сущностей типа: {name: "a", "genres": ["genre1","genre2","genre3",...],"packages":["pack1","pack2",...], "year": "2015",...}
Сущностей в системе порядка 100к. В каждой сущности около 5 вложенных массивов, в каждом порядка 10 элементов.
Нужно уметь отдавать 10k rps с одной машинки ответы на запросы типа
"отдай все записи у которых в genres есть "genre1" и в "packages" есть "package1" или "package2" и т.д. + сортировка + пэйджинг.
olegator99
01.09.2016 19:3710 миллиардов записей получится. Жалко рам так тратить… А планы по такой фиче есть?
danikin
01.09.2016 19:44Есть, да. А длина каждой записи какая в байтах?
olegator99
01.09.2016 19:51+1Json — 2к, в бинарном виде в 1к ± влезет.
Если не секрет, когда планируете?
danikin
01.09.2016 19:53+110 терабайт. Многовато для in-memory :-)
Точно по срокам сказать не могу. У нас в приоритетах — дисковое хранилище (vinyl), SQL и полноценное кластерное решение. Кстати vinyl решит вашу проблему, когда надо много памяти.olegator99
01.09.2016 20:00Именно! Конечно, на практике записей там поменьше, но порядки такие, да.
Пока все идет к написанию своего кэша. Протип на гошке с ц++ по рпс в принципе устраивает, но очень не хочется изобрести новый велик :)
solver
02.09.2016 12:16+1>Второй движок дисковый. Он позволяет всё хранить на диске. Причем можно использовать как SSD, так и винчестеры.
О чем эта фраза? Просто маркетинг или он как-то на низком уровне накопители может использовать?danikin
02.09.2016 12:33+3Смотрите, у Тарантула есть два движка — memtx и vinyl.
memxt — умолчательный движок, хранит копию датасета в памяти, пишет каждую транзакцию на диск
vinyl — дополнительный движок, хранит датасет на диске в log structured tree, пишет каждую транзакцию на диск
Основное отличие vinyl и традиционных B+ tree based движков таких как InnoDB в MySQL или Postgres в том, что любая изменяющая операция приводит лишь к записи в журнал, но не меняет сам dataset. Сам dataset перестраивается в фоне. Движок вырос из LevelDB by Google и RocksDB by Facebook, построенных на тех же принципах — максимизируем скорость записи. В vinyl учли ошибки обоих и сделали более производительный движок.
Благодаря такой архитектуре, вы можете хранить большие наборы данных на обычных магнитных дисках (HDD), которые в 10 раз дешевле твердотельных (SSD), потому что запись всегда линейная, а HDD оптимизирован как раз под линейную запись. При этом, чтение можно будет кэшировать в памяти, а в последствии и на SSD.solver
02.09.2016 12:49+1Спасибо.
Получается это техническое преимущество.
Об этом и надо было авторам в статье писать, а не маркетинговыми фразами раскидываться)
pavlyuts
02.09.2016 16:14Денис, совершенно ламерский вопрос: а если объем данных превышает доступный Тарантулу объем памяти, то что с ним делает memtx? И как это отражается на производительности?
danikin
02.09.2016 16:59Выдает ошибку. Ровно также, как если заканчивается диск в любой не in-memory базе данных.
lehha
03.09.2016 20:42Какие есть инструменты в Tarantool для ручной работы? Например, просмотр текущего состояния, работы с таблицами, статистикой? Например, как phpmyadmin или HeidiSQL — менеджеры БД для некоторых штучных операциях и визуального контроля.
danikin
03.09.2016 21:05Есть консоль tarantoolctl, в которой можно сделать все что угодно руками. Есть еще https://github.com/tarantool/prometheus — это для мониторинга и метрик.
Mirie
07.09.2016 03:00Довольно странно, что нет ни слова о сравнении с Berkeley DB. Есть такая информация?
danikin
09.09.2016 01:42+1У нас в Почте@Mail.Ru раньше сессии и профили хранились в Berkeley DB. После перевода на Тарантул доступ к ним ускорился в десятки раз. Опубликованных бенчмарков нет, давно было дело уже. Но самая главная проблема BDB на мой вкус не только в скорости, а в том, что у нее нет репликации (возможно, сейчас есть, но на тот момент не было) и после рестарта файлики BDB превращаются в тыкву (или в мясо — как кому удобней). Все потому, что для обновления файлов используется mmap, который применяет изменения в неопределенные моменты времени. Соответственно, после жесткого рестарта (который иногда случается) данные частично теряются. Причем, теряются не последние изменения, и даже не случайные изменения, а просто куски файликов базы данных, которые переводят базу в неконсистентное состояние и ее надо восстанавливать из бэкапа.
napa3um
Loc — это log, видимо (а лучше по-русски — журнал).
1cbitrix
Спасибо за внимательность, поправили!