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 — очень хороший выбор на сегодняшний день.

Поделиться с друзьями
-->

Комментарии (19)


  1. kefirr
    11.04.2017 22:29

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


    1. aensidhe
      11.04.2017 23:48
      +1

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


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


      1. kefirr
        12.04.2017 06:02
        +3

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


        1. aensidhe
          12.04.2017 11:05

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


    1. danikin
      12.04.2017 09:21
      +2

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


      1. kefirr
        12.04.2017 09:23
        +1

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


        1. danikin
          12.04.2017 10:45
          +1

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


          1. kefirr
            12.04.2017 10:50
            +1

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


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

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


            1. danikin
              12.04.2017 11:05
              +1

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


              1. kefirr
                12.04.2017 11:11
                +1

                набор заготовок

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


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

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


                1. danikin
                  12.04.2017 13:31
                  +1

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


  1. WondeRu
    12.04.2017 09:36
    +2

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


    1. aensidhe
      12.04.2017 10:58
      +4

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


    1. Trixon
      12.04.2017 11:01
      -2

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


      1. aensidhe
        12.04.2017 11:03

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


  1. AlexTheLost
    12.04.2017 10:49
    +1

    Вывод:


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

    Пояснение:


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

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


    1. aensidhe
      12.04.2017 10:59
      +1

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


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


  1. Coocos
    12.04.2017 11:10

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


    1. aensidhe
      12.04.2017 11:11

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