Для работы с данными применяются два типа решений: а) системы с хранением данных в оперативной памяти, задержка при обращении к которым не превышет одной миллисекунды на запрос, обеспечивающие возможность обработки сотен тысяч запросов в секунду на ядро и оптимизации под высокую нагрузку в случае большого числа параллельных запросов (Memcached, Redis); б) системы с хранением данных на диске, предоставляющие возможности полноценных открытых СУБД (MySQL, PostgreSQL и др.), обеспечивающих постоянное хранение данных, а также поддержку транзакций, сохранность, доступность и т. д. Однако для реальных проектов все чаще требуются возможности обоих типов решений. Например, при создании сервиса авторизации и аутентификации и работы с профилями пользователей в условиях множества одновременно выполняемых запросов нужно обеспечить сохранность данных, поддержку вторичных и композитных индексов и хранимых процедур для обнаружения попыток взлома и любой злонамеренной активности, причем одновременно требуется минимизировать задержку при выполнении запроса. Может показаться логичным использовать совместно несколько продуктов: решение для обеспечения кэширования и дисковую СУБД, когда все запросы на чтение обрабатываются в кэш-памяти, а запросы на запись выполняет дисковая СУБД с последующей синхронизацией с кэшем. Но при таком подходе возникает проблема рассинхронизации данных в кэше и основной базе данных, а описать логику синхронизации достаточно трудно — эта задача обычно делегируется разработчику, которому приходится вручную написать код, учитывающий все возможные ситуации. Необходимо также предоставить большее количество серверов для одновременной поддержки двух продуктов. В итоге ни кэш, ни дисковая СУБД не работают так, как ожидается.

Сегодня разработчики обращаются к разным продуктам для разных типов запросов, и реально используются далеко не все возможности каждого из решений. При смешанной конфигурации чтение происходит из кэша, поэтому при запросах на чтение теряются возможности основной СУБД. При запросах на запись невозможно сделать, например, транзакции, которые распространялись бы и на кэш, и на дисковую СУБД. Таким образом, система, состоящая из двух продуктов, не будет поддерживать транзакции. А значит, возможны ситуации, когда в кэше и дисковой СУБД будут данные в неконсистентном состоянии. При этом сложность разработки и поддержки только возрастает.

СУБД in-memory

Полноценная интеграция продуктов от разных поставщиков — нетривиальная задача, и идеальной представляется возможность использования одного продукта, имеющего все требуемые свойства. К таким продуктам относится, например, система Tarantool, представляющая собой документоориентированную СУБД с хранением в оперативной памяти (in-memory) и сервер приложений с открытым исходным кодом, совмещающий свойства кэша и «полноценных» СУБД для постоянного хранения данных. Эта система разрабатывается в компании Mail.ru с 2008 года и сегодня используется в Avito, Yota, Badoo, Qiwi и ряде других организаций.

Данные в Tarantool хранятся в оперативной памяти, благодаря чему запросы на чтение выполняются быстро, а в случае запросов на запись, перед тем как обновить данные в оперативной памяти, изменения записываются на диск в журнал транзакций (Write Ahead Log, WAL), поэтому данные на диске и в оперативной памяти всегда синхронизированы. При этом скорость запросов на запись достаточно высока: запись представляет собой лишь последовательное добавление транзакций в конец журнала. Даже классические жесткие диски (а также SSD) имеют высокую скорость последовательной записи — до 100 Мбайт/с, которая выполняется на несколько порядков быстрее, чем при произвольном доступе и произвольной записи. Таким образом, если при одной транзакции пересылается в среднем 100 байт, то Tarantool на сервере с жестким диском может заполнять журнал со скоростью 100 Мбайт/с, выполняя при этом до миллиона транзакций в секунду. Узким местом здесь становится уже не диск, а процессор.

С другой стороны, классические дисковые СУБД используют специальную организацию данных в памяти, блокировки и структуры, ориентированные именно на дисковый (блочный) сценарий работы. Поэтому даже при размещении образа диска в памяти производительность дисковых СУБД оказывается хуже, чем у СУБД с хранением в памяти, изначально предполагающих, что все данные хранятся в оперативной памяти. При работе на обычных дисках, даже в случае использования кэшей (buffer pool), количество произвольных чтений и обращений к диску в дисковых СУБД очень велико. Например, для работы со структурами типа дерево B/B+ (СУБД MySQL, PostgreSQL) требуется произвольный доступ к диску для операций записи, хотя при этом производится последовательная запись в журнал транзакций. И даже относительно новые LSM-деревья (СУБД Google RocksDB, Facebook LevelDB), не требующие произвольного доступа к диску для операций записи, проблему с произвольным доступом на чтение не снимают.

В ситуации, когда все данные помещаются в оперативную память, Tarantool показывает более высокую производительность запросов на чтение и на запись по сравнению с традиционными дисковыми СУБД, при этом соблюдаются все требования ACID (atomicity, consistency, isolation, durability — «атомарность, согласованность, изоляция и долговечность») [1]. При этом для хранения на диске данных, которые не помещаются в оперативную память, Tarantool предлагает специализированную технологию, основанную на LSM-деревьях. А если Tarantool используется исключительно как кэш и не требуется постоянное хранение, то журнал WAL можно вообще отключить. Учитывая то, что Tarantool поддерживает работу по протоколу Memcached, эта СУБД может заменить СУБД Memcached. Таким образом, можно реализовать любой сценарий работы: данные хранятся только на диске, на диске и в памяти, только в оперативной памяти.

Кроме ведения журнала транзакций, СУБД Tarantool периодически делает полные снимки состояния баз данных (snapshot), что помогает при перезапуске (например, после выключения питания), когда в оперативную память сначала загружаются все данные из последнего снимка, а потом последовательно выполняются транзакции из журнала, осуществленные после создания снимка. В итоге значительно возрастает скорость восстановления после сбоев — например, «поднятие» из снимка базы данных размером 20 Гбайт занимает около пяти минут. На тестах YCSB (Yahoo! Cloud Serving Benchmark) Tarantool превосходит по результатам некоторые другие СУБД с хранением в памяти: Memcached, Redis, Aerospike и VoltDB.

В качестве иллюстрации возможностей Tarantool можно привести показатели ее быстродействия при кэшировании данных на платформе Microsoft Azure на виртуальной машине c двумя ядрами и 14 Гбайт оперативной памяти (см. таблицу). Результаты тестирования представлены на рис. 1.

Таблица. Сравнительные усредненные показатели быстродействия Tarantool
Таблица. Сравнительные усредненные показатели быстродействия Tarantool

 

Рис. 1. Сравнение производительности СУБД в сценарии кэширования
Рис. 1. Сравнение производительности СУБД в сценарии кэширования

 

Отдельного рассмотрения заслуживает поддержка асинхронной репликации master-master и master-slave. Часто в гетерогенных физически распределенных ЦОД возникает ситуация, когда нужно обеспечить масштабируемость выполнения операций и чтения, и записи. СУБД Tarantool позволяет добавлять и обновлять данные на серверах двух ЦОД (естественно, если обновлять одни и те же данные, часть изменений может быть перезаписана), а затем автоматически асинхронно реплицировать изменения между ЦОД. Поддерживается также конфигурация master-slave, когда источником изменений может выступать только один сервер, причем имеется даже сценарий репликации из MySQL в Tarantool.

Сервер приложений

Tarantool — это не только СУБД, но и сервер приложений, создаваемых на языке Lua, с возможностью вызова процедур, написанных на Cи или Rust. Язык Lua достаточно прост, освоение его синтаксиса и основных возможностей обычно не вызывает затруднений. Однако, несмотря на простоту, это полноценный язык программирования высокого уровня, используемый для создания скриптов в играх (например, World of Warcraft), клиентских приложениях (Adobe Lightroom, VLC, Wireshark) и веб-серверах (Apache HTTP Server, Nginx).

Tarantool для Lua — это примерно то же самое, что Node.js для JavaScript. В Tarantool есть: неблокирующий ввод/вывод, зеленые потоки в пользовательском пространстве (управление потоками выполняет не ОС, а виртуальная машина), кооперативная многозадачность и т. д. И хотя имеются различия в реализации, все, что можно сделать на Node.js, можно сделать и на Tarantool, но обратное неверно. Например, Node.js, в отличие от Tarantool, не имеет встроенной СУБД и не может в памяти обрабатывать в реальном времени сотни гигабайт данных, выполняя вычисление сложных корреляций и рейтингов, обнаруживая аномалии и т. п. Данные и код их обработки размещаются максимально близко друг к другу. При этом Tarantool в процессе обработки может выдавать запросы к сторонним хранилищам на базе MySQL и PostgreSQL, выступая единой точкой входа, а результаты работы выдавать наружу через сервисы RESTful.

Очень часто современные серверные приложения работают как прослойка между базой данных и клиентом (браузером, мобильным приложением или клиентом API), то есть формируют запросы к базе, преобразуют результат в JSON и выдают его клиенту. Для всех таких случаев достаточно возможностей системы Tarantool, которая позволяет в одной среде выполнять обработку и преобразование данных.

На сегодняшний день для Tarantool создано множество модулей — например, модуль, реализующий очереди, благодаря которому Tarantool выступает не только как СУБД и сервер приложений, но и как сервер очередей, что позволяет создавать ключевые производственные системы. С другой стороны, имеются проекты, где Tarantool играет только одну роль — например, выступая в качестве замены Memcached или как кэш для MySQL. Такая гибкость позволяет выбрать только то, что действительно нужно для конкретного проекта.

Tarantool IIoT

Tarantool может работать как на мощных серверах с сотнями гигабайт оперативной памяти, так и на стандартных виртуальных машинах, предлагаемых провайдерами облаков. Но самое главное — Tarantool работает и на устройствах с ARM-процессорами, что делает эту систему пригодной для поддержки конфигураций Интернета вещей и Промышленного интернета (Industrial Internet of Things, IIoT). Имеется специальная версия для использования на устройствах IIoT — Tarantool IIoT. В ней поддерживаются основные протоколы для работы с датчиками (MQTT и MRAA), генерирующими большие объемы данных, которые требуется обрабатывать в реальном времени. Инструменты Tarantool IIoT позволяют создавать скрипты, описывающие процессы получения показателей с датчиков различных промышленных устройств, их обработки и сохранения в локальной базе данных. Встроенные средства Tarantool обеспечивают репликацию на концентраторы IIoT и далее на один или несколько ЦОД. В итоге разработчику не нужно думать о ненадежности каналов между устройствами и ЦОД, заботиться о выстраивании дисциплины взаимодействия клиент-сервер и т. п., он может концентрироваться на бизнес-логике.

 

Работа с Tarantool

Начать работу с Tarantool можно из браузера, запустив обучающий скрипт на официальном сайте tarantool.org/en/try.html или получив рекомендации по установке и запуску системы в macOS, популярных дистрибутивах Linux и FreeBSD. Самый удобный способ запуска Tarantool в среде Linux, macOS или Windows — это Docker:

 

docker run — d tarantool/tarantool:1.8

 

Далее нужно подключиться к консоли системы Tarantool, запущенной в контейнере с именем mytarantool, и выполнить команду console в интерактивном режиме.

 

docker exec -it mytarantool console

 

После этого требуется создать пространство (space) для хранения данных (рис. А) — аналог коллекций в MongoDB или таблиц в традиционных реляционных СУБД.

 

tarantool> box.schema.space.create('customers')

 

Данные хранятся в виде кортежей (tuple) в пространствах (space), а для полей кортежей можно создавать индексы.

Рис. А. Модель данных Tarantool
Рис. А. Модель данных Tarantool

 

Box — основной модуль, отвечающий за функциональность СУБД Tarantool и предоставляющий полноценный интерпретатор языка Lua, в котором можно задавать переменные и функции, а также загружать и вызывать сторонние Lua-пакеты. Запросы к Tarantool можно делать и из других языков программирования: на данный момент поддерживаются коннекторы для Node.js, PHP, Go, Java,. NET, R, Erlang, Cи, Perl и Python. В версии 1.8 появилась поддержка коннекторов ODBC и JDBC.

 

***

Сегодня во многих случаях требуется обеспечить приложениям высокую скорость доступа к данным. Сделать это путем репликации дисковых СУБД затруднительно, поскольку это приводит к увеличению количества серверов, усложнению поддержки и росту затрат. Поэтому разработчики все чаще отдают предпочтение СУБД класса in-memory. Однако большинство таких систем нельзя использовать в качестве основной базы данных. Документоориентированная СУБД и сервер приложений Tarantool позволяют строить решения, обладающие возможностями нескольких продуктов (кэш и традиционная СУБД), и работает как на мощных серверах, так и на массовых устройствах, созданных на платформе ARM. Данные в памяти и на диске синхронизированы, что обеспечивает надежность. Средства Tarantool позволяют писать код, обрабатывающий данные непосредственно в точке их размещения.

Литература

  1. Наталья Дубова. Все про промежуточное ПО // Открытые системы.СУБД. — 1999. — № 7–8. — С. 41–50. URL: http:www.osp.ru/os/1999/07-08/179871 (дата обращения: 18.05.2017).

Денис Аникин (anikin@corp.mail.ru) — технический директор почтовых и облачных сервисов, Сергей Пугачев (spugachev@gmail.com) — консультант по разработке ПО, компания Mail.Ru (Москва).