company_banner

Tarantool как основное хранилище данных для серверных приложений, написанных на .NET

    image


    Привет, Хабр! Сегодня хочу поделиться с вами текстовой версией доклада, представленного на Tarantool Meetup второго марта 2017-го года в Mail.Ru Group с поправкой на то, что прошёл уже месяц, и кое-что из обещанного уже было реализовано, поэтому текст будет интересен даже тем, кто видел выступление. Я работаю в компании eVote, которая разрабатывает сервис онлайн-опросов. Мы активно применяем Windows и .NET-технологии в наших продуктах, и в этом посте я расскажу про то, как мы добавили к стеку наших технологий СУБД Tarantool.


    Большое спасибо коллегам, друзьям и сотрудникам компании Mail.Ru Group, которые помогали написать эту статью.


    Выбор СУБД


    В жизни каждого проекта рано или поздно возникает переломный момент, когда нужно выбрать СУБД для хранения всех данных. Наш проект с этой точки зрения простой: пользователи, голосования, ответы, какая-то попутно собираемая информация — всё это прекрасно можно держать в key-value хранилище. Поэтому на старте мы рассматривали три варианта: Redis, Tarantool и MySQL с handlersocket. Фаворитом с самого начала был Redis. Он быстро работает, у него замечательный коннектор для .NET, созданный командой Stack Overflow. К слову, сам Stack Overflow написан на .NET, работает на Windows, у них SQL Server от Microsoft, Redis и ещё много интересного. У Redis прекрасная документация. Если мы нанимаем нового программиста, который никогда не работал с Redis, то мы отправляем его туда — и через три дня он знает примерно всё, что ему нужно знать для использования Redis.


    Под вторым номером шёл Tarantool. К сожалению, у него не было такого удобного сайта, как у Redis. Как и коннектора для .NET. По скорости он нас полностью устраивал, так как не сильно отличался от Redis. Если вы включаете write ahead logging, то любая запись окажется на диске. И если у вас не глючит контроллер, диск или прочее железо, то получается вполне надёжно. Также в Tarantool есть вторичные индексы. В некоторых случаях это очень важная фича. В Redis её нет, приходится делать вручную.


    Handlersocket оказался аутсайдером. Он медленнее, чем Redis и Tarantool. Зато доступна ACID-модель, если ваш движок в MySQL её поддерживает. Доступна вся инфраструктура от MySQL: репликация, мониторинг, эксплейн, бэкапы. Можно строить сложные отчёты на обычном SQL. Коннектора под .NET тоже не было.


    image


    В результате мы выбрали Redis и запустили его в свою закрытую альфу. Но через какое-то время выяснилось, что отсутствие вторичных индексов — более серьёзная проблема, чем мы думали. Если нужно выбрать все голосования, созданные каким-либо автором, то приходится заводить дополнительные списки для хранения (можно, конечно, делать полный перебор, но это не наш случай). Есть риск, что в результате разных сбоев данные станут неконсистентными. Попытки исправить это через Lua-скрипты или транзакции привели к тому, что работа Redis замедлилась примерно в три-четыре раза, что перестало нас устраивать. Это не проблема самого Redis, а следствие его нецелевого использования, на мой взгляд.


    image


    Tarantool и Windows


    Тогда мы решили перейти на Tarantool. И перед нами встали сразу две проблемы. У Tarantool до сих пор нет бинарника под Windows (и неизвестно, когда появится) — раз. Не было коннектора для .NET — два. Вообще говоря, это спорный вопрос, можно ли назвать недостатком отсутствие версии нашего хранилища под Windows. Я считаю это преимуществом. Ведь в production будет, скорее всего, Linux, а отсутствие версии под Windows заставит программиста разбираться в том, как всё работает на самом деле. Программист для отладки и мониторинга будет пользоваться теми же инструментами, что и в production. По моим наблюдениям, это повышает вероятность написания качественного кода без ошибок и уменьшает время простоя в случае катастроф.


    Первую проблему мы решили использованием Docker for Windows для разработки. А вот вторая проблема была посложнее.


    Коннектор


    Поскольку готового коннектора для Tarantool не существовало, мы написали свой. Для этого пришлось решить две задачи. Первая: реализовать сериализацию и десериализацию в msgpack, так как это формат обмена данных в Tarantool. У нас она была решена в рамках проекта MsgPack.Light, так как мы тоже храним данные, упакованные в msgpack. Вторая задача сводилась к буквальной реализации протокола обмена данных с Tarantool iProto. Она решена в рамках проекта progaudi.tarantool.


    Мы поддерживаем


    • .NET 4.6 и выше,
    • новый opensource-фреймворк .NET Core: netstandard 1.4 и выше.

    Раньше коннектор назывался tarantool.csharp, но после обратной связи от комьюнити мы его переименовали в progaudi.tarantool. Теперь название не должно вызывать никакой путаницы — можно ли использовать коннектор из F# или нет. Можно было с самого начала.


    Благодаря недавним улучшениям в MsgPack.Light, коннектор научился работать с тарантуловским типом данных scalar. В будущем планируется ещё больше упростить работу с коннектором и уйти от явной конвертации объектов пользователя в TarantoolTuple-структуры.


    Мы не поддерживаем DDL, потому что он выходит за рамки iProto и должен быть реализован обёртками над EVAL-командой. Лично я считаю, что если программист использует Tarantool, то он должен писать схему в Lua, потому что в таком случае можно шарить её с админами и распространять сразу в работающее окружение. Может быть, я неправ, и мы реализуем возможность делать это из .NET.


    На текущий момент у нас есть соединение только с одной нодой Tarantool. Мы не полностью поддерживаем CALL_16, в определённых случаях на стороне Tarantool происходит странная упаковка, и это приводит к ошибкам при десериализации. Новый CALL поддерживается полностью, рекомендуем пользоваться им.


    Особенности разработки коннекторов к Tarantool


    К сожалению, в Tarantool невозможно попросить сервер выводить все запросы в лог. В некоторых ситуациях это очень мешает при отладке, при этом нам обычно не важна производительность сервера Tarantool. Хотелось бы получить какую-то ручку, которая включает запись всех запросов в лог.


    При логировании запросов в лог хочется их как-то отделять друг от друга. Для этого у нас есть connection id (box.session.id) и request id (box.session.sync). К сожалению, box.session.sync расшарен на все запросы в рамках одного соединения, как следствие, он может меняться, если выполнение запроса прерывается из-за достижения yield point (запись в базу, ручная передача управления и так далее). В принципе, это важно только там, где может встретиться несколько точек логирования, например, в хранимых процедурах. В таких случаях следует box.session.sync сохранять до первого yield point в локальную переменную.


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


    Важная часть Tarantool — это сервер приложений. К нему существует множество уже готовых модулей, начиная с простых, таких как автоматическая перезагрузка других модулей, заканчивая шардированием, очередями и драйверами к другим СУБД (1, 2 и многое другое). Все модули, которые мы пробовали, прекрасно работают, неплохо документированы и поддерживаются.


    Из всего этого разнообразия мы используем tarantool/queue для очередей и tarantool/prometheus для сбора метрик. Также у нас есть немного своей логики на Lua. Немного — потому что наша команда из мира .NET. Мы привыкли, что есть статический анализ кода, есть пошаговая отладка, удобные профайлеры. У Lua с этим проблемы, особенно со статическим анализом кода.


    Репликация


    Очень хотелось бы получить синхронную репликацию. Мы уже умеем жить без неё, но очень сильно ждём. Пока что мы пользуемся имеющейся master-master асинхронной репликацией. Один из наших сервисов работает с картинками. Там фигурируют случайные ключи, и мы можем писать в любую ноду, потому что вероятность совпадения ключей крайне мала. Нам нужно, чтобы совпали сгенерированный guid для картинок и SHA256. Так как к этому мы пришли не сразу, то коннектор до сих пор не умеет соединяться с несколькими нодами. В нынешнем году мы это обязательно исправим.


    Сборка кластера в старых версиях


    В очень старом билде 1.7.3-0 может не собраться кластер. Допустим, у вас три мастер-ноды. Указываете им одинаковые конфигурации, запускаете. И пока любая из нод не увидит свои источники, откуда она забирает данные, она не будет принимать запросы от других клиентов. К сожалению, эти источники для них — клиенты. В результате все три ноды не отвечают на запросы и ждут 30 секунд. В это время они ищут свои источники, не находят, пишут в лог: «Источников нет, я выключилась». И кластер не собирается. Приходится собирать вручную. Запускаем одну ноду без общей конфигурации. Она поднимается, подключаем к ней остальные ноды, а потом меняем конфигурацию на единую. В новой версии этот баг уже исправлен.


    Версионирование образа для докера tarantool/tarantool


    Допустим, версия образа 1.7.3. А какая версия Tarantool внутри? Мы знаем, что 1.7.3, но номер сборки неизвестен. Единственный вариант — посмотреть исходники. Допустим, нужна версия Tarantool 1.7.3-115, потому что в 114 ещё была проблема, которая нас больно бьет, а в 116 — мы не знаем, есть она или нет. Какую версию образа взять?


    Мы решили задачу просто: сами собираем свой образ, указываем конкретный номер сборки — и всё прекрасно работает. Но в целом это небольшая проблема. Образ по умолчанию прекрасно работает и покроет бо́льшую часть запросов, туда включены все основные модули, которые нужны для работы с Tarantool: мониторинг, очереди и т. д. Можно брать и пользоваться.


    Интерактивные запросы


    Чтобы написать интерактивный запрос в Tarantool, нужно знать Lua и уметь пользоваться командной строкой. Наши тестеры и админы умеют обращаться с командной строкой, но они не горят желанием разбираться с Lua. Они хотят быстренько написать SQL-скрипт, проверить какой-нибудь счётчик или timestamp. Поэтому мы очень ждём анонсированную поддержку SQLite-диалекта.


    Мониторинг и логирование


    С ними всё прекрасно. Есть модуль tarantool/prometheus, который опубликован на сайте Prometheus. Так как мы используем в production docker, то логи мы с него собираем с помощью Fluentd (1 и 2) и складываем их в ElasticSearch.


    Выводы


    .NET — это далеко не только Windows: .NET Core работает на всех платформах. У нас в production есть .NET-приложение, которое прекрасно функционирует на Linux. Наши opensource-проекты без проблем собираются и работают на Windows, Mac OS и Linux (тестируются только Windows 10, Max OS X, Ubuntu 16.04). При этом вполне возможно для разработки и отладки использовать весь богатый инструментарий, доступный в мире .NET и зачастую бесплатный даже для коммерческой разработки.


    Благодаря кроссплатформенности новых .NET-решений, появляется возможность использовать недоступные ранее инструменты, например, Tarantool. Если вам важна скорость работы, вторичные индексы, возможность использования СУБД в качестве сервера приложений или очереди задач, то Tarantool — очень хороший выбор на сегодняшний день.

    Mail.Ru Group 789,58
    Строим Интернет
    Поделиться публикацией
    Похожие публикации
    Комментарии 19
    • 0

      Рассматривался ли Apache Ignite? Функционал весь на месте и даже больше, полная поддержка .NET (правда, пока без .NET Core).

      • +1

        Нет, не рассматривался, так как на момент выбора хранилища, не существовало его стабильных версий.


        Кстати, если верить их репозиторию, .net core support там уже есть.

        • +3

          Не, это у них просто главный модуль так называется, ядро.

          • 0

            Спасибо. К сожалению, для простого upvote не хватает кармы.

        • +2
          А у Игнайта уже есть синхронная запись на диск всех изменений перед ответом пользователю на транзакцию?
          • +1

            Всегда была в виде write-through cache store: https://apacheignite.readme.io/docs/persistent-store

            • +1
              Ну это же не запись в свой родной лог транзакций, это запись в другую СУБД. Что тут же делает работу игнайта на запись не быстрее чем эта СУБД. Плюс, в случае разрыва сети возможны повторные записи (в СУБД запись прошла, сеть порвалась, игнайт про это не узнал — сделал ритрай, получили повторную запись
              • +1

                Почему именно СУБД, как CacheStore реализуешь, так и будет писаться. Синхронная запись подразумевает "не быстрее чем то, куда пишем", разве нет?


                не запись в свой родной лог транзакций

                Это валидный пункт. Вроде как в будущем появится. Изначально Игнайт — in-memory система.

                • +1
                  Ну вот видимо в этом и кроется ответ. Одно дело — СУБД с родным логом и пирогами. Другое тело — набор заготовок и напильник к ним.
                  • +1
                    набор заготовок

                    Зачем же так сразу?


                    СУБД с родным логом и пирогами

                    Какие уж тут пироги, если даже SQL нету

                    • +1
                      Ну тут дело вкуса уже. Кому то ОК, чтобы был SQL, но без нормального персистенса, а кому то наоборот.
        • +2

          Насколько бы просело по скорости ваше решение, если бы взяли обычную sql db (mssql или postgresql)? Почему именно key-value?

          • +4

            Когда мы два года назад тестировали MS Sql Server, то я смог выжать примерно такой же перфоманс, но с отключенным хранилищем на движке Hekaton. С классическим табличным движком мы проседали в 5 раз примерно. PostgreSql мы не тестировали, потому что не умеем его настраивать.

            • –2

              Из текста следует, что у автора, как минимум, реляционная модель данных, а проблема его сводится к тому как почесать левой рукой правое ухо.

              • 0

                При всём уважении, мне неизвестны проекты, состоящие из полностью независимых сущностей без связей между ними. Можно примеры?

            • +1

              Вывод:


              Handlersocket оказался аутсайдером. Он медленнее, чем Redis и Tarantool.

              Пояснение:


              Зато доступна ACID-модель, если ваш движок в MySQL её поддерживает. Доступна вся инфраструктура от MySQL: репликация, мониторинг, эксплейн, бэкапы. Можно строить сложные отчёты на обычном SQL.

              Неплохо так сравнили kev-value хранилище и плагин к MySQL, т.е. то что он медленнее это сильный минус на фоне все его возможностей?)

              • +1

                Вся суть данного плагина в том, что скорость доступа к данным выше, чем через стандартный движок запросов MySQL, поэтому да, скорость на наших нагрузках была определяющим фактором.


                К тому же, у Tarantool есть практически всё тоже самое, что и у MySql уже.

              • 0
                Redis вы запускали на Windows?
                • 0

                  Когда-то давно — да, но сейчас — нет.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое