В статье "Tarantool: Хороший, Плохой, Злой" был описан простой сервис голосования с действующим примером на PHP. Мы увидели, как легко подключить и использовать эту NoSQL базу данных в своих программах. Однако остался без внимания один важный вопрос – зачем это? Какой выигрыш в производительности дает использование NoSQL по сравнению с обычными базами данных?
Погнали!
Для ответа на этот вопрос я протестирую один из своих серверов. На нём крутится виртуальная машина с 1 Гб памяти и процессорным ядром со следующими параметрами:

processor: Intel(R) Xeon(R) CPU E5-26xx (Sandy Bridge)
cpu MHz: 1999.999
cache size: 4096 KB
bogomips: 3999.99

Дисковая подсистема на SSD неплоха:

hdparm -t /dev/sda1
/dev/sda1:
 Timing buffered disk reads: 484 MB in  3.00 seconds = 161.13 MB/sec

Наш хостер утверждает, что для виртуального сервера доступна полоса 100 Мбит. И, хотя, наши тесты показывали большую скорость сети, примем это ограничение за данность. Версия nginx 1.6.2, версия php/php-fpm 5.6.30, версия Tarantool 1.7.3.

Для тестирования будем использовать утилиту wrk. После нескольких тестов с различными параметрами wrk и небольшого тюнинга nginx, у последнего получилась такая конфигурация:

worker_processes 1;

events {
        worker_connections 1024;
        multi_accept on;
        use epoll;
}

http {
# Timeouts
        keepalive_timeout     60;
# TCP options
        tcp_nodelay on;
        tcp_nopush  on;
# Compression
        gzip              on;
        gzip_comp_level   5;
…
}

Утилита wrk в конечном итоге запускалась с 50 параллельными запросами. Если поставить меньше запросов, то не удается достичь максимальной производительности сервера. Если ставить больше запросов – 100 и выше, то, несмотря на некоторый рост производительности request per second, начинала быстро расти задержка. В конечном счете, увеличение количества параллельных запросов приводило к появлению потерь. Впрочем, на этот счет есть прекрасная иллюстрация:



Из статьи Константина Осипова Принципы и приемы обработки очередей

Результаты тестов Тарантула


Сетевой пинг между измеряемым сервером и генератором трафика составлял 32 мс. Тестовая машина на всякий случай была перезагружена после пробных тестов ради объективности результата. Были получены такие данные:

wrk -c50 -d60s -t4 http://ugly.begetan.me/good
Running 1m test @ http://ugly.begetan.me/good
  4 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    54.48ms   10.15ms 441.17ms   95.62%
    Req/Sec   220.76     19.43   270.00     74.65%
  52760 requests in 1.00m, 320.86MB read
Requests/sec:    878.72
Transfer/sec:      5.34MB

Нам удалось получить чуть менее 900 запросов в секунду при времени отклика 54 миллисекунды. Много это или мало? Для начала вспомним, что происходит при обращении к странице в скрипте нашего сервиса.

  1. Скрипт видит обращение нового клиента и пытается прочитать куки, которых нет
  2. В Тарантуле генерируется уникальный uuid
  3. В Тарантуле выполняется команда записи upsert, которая записывает uuid, время, IP и User-Agent клиента
  4. Скрипт вызывает команду Тарантула, которая выбирает Топ-9 лучших из 16 тысяч записей, используя индекс по рейтингу


И всё это вместе занимает примерно 1 миллисекунду на минимально возможном VPS — дешевле некуда! За время тестов было более миллиона записей в таблице session, и это никак не отражается на производительности сервера. Мне кажется, что это неплохо, не правда ли?

Дополнительную информацию можно почерпнуть из вывода утилиты top. Как уже было сказано, сервер был перезагружен перед началом тестов. Скриншот был сделан во время запуска повторного бенчмарка от момента перезагрузки.


На картинке видно распределение процессорного времени на задачи. Около четверти ресурсов потребляет непосредственно Тарантул. Основным потребителем ресурсов оказались обработчики php-fpm, что было ожидаемо. На третьем месте оказался nginx. Когда я увеличивал количество параллельных запросов wrk, то увеличивалось процессорное время потребляемое nginx-ом. Из-за этого не хватало ресурсов для php-fpm и в логах возникали ошибки 499 и 502.

В этом месте было бы правильным переписать приложение, заменив Тарантул на какую-нибудь обычную SQL базу данных – MySQL или PostgreSQL и сравнить результаты. Однако, автор не любитель бесполезной работы, поэтому придумал другой вариант. Мы будем тестировать связку php-fpm без Тарантула и посмотрим, как изменится производительность.

Результаты тестов без Тарантула


Для начала разберемся, почему мы взяли именно 50 одновременных соединений для тестирования с помощью wrk (параметр -c50)? Когда мы запускаем один поток теста, он начинает загружать страницу, последовательно раз за разом. Из-за наличия сетевой задержки распространения сигнала и ненулевого времени обработки запроса, нагрузка от одного потока получается очень слабая. Поэтому мы начинаем увеличивать количество параллельных запросов и смотреть, что происходит с результатами тестов и нагрузкой на сервер. По мере роста количества одновременных запросов почти равномерно увеличивается %CPU у процесса nginx. Когда нагрузка возрастает слишком сильно, серверу перестает хватать ресурсов процессора и часть тестовых запросов получают ошибку. Они либо завершаются по таймауту со стороны wrk, либо веб-сервер выдает ошибку 502 о недоступности бекенда (в нашем случае php-fpm).

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

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


Итак, вместо обращений к Тарантулу в нашей программе мы воткнули заглушку, вот так, по-крестьянски:

action_good()
function action_good() {
    $title = 'Top of the best stickers for Telegram';
//    $top = get_top(10,Tarantool::ITERATOR_LE);
    $top[0] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/18.png', 1,-1);
    $top[1] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/17.png', 1,-1);
    $top[2] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/16.png', 1,-1);
    $top[3] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/15.png', 1,-1);
    $top[4] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/14.png', 1,-1);
    $top[5] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/13.png', 1,-1);
    $top[6] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/12.png', 1,-1);
    $top[7] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/11.png', 1,-1);
    $top[8] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/10.png', 1,-1);
    $top[9] = array(0, 7, 'Procy', 0, 'https://s.tcdn.co/6fb/382/6fb38239-ce7c-3234-bc8a-e6267086b46a/9.png', 1,-1);
    $active_good ='class="active"';
    $active_bad ='';

    include_once('top.html');
}


Также отключим и уберем вообще все упоминания Тарантула и запустим скрипт. Как видно из графика (ряд nginx+php), при отключенном Тарантуле на 50 потоках мы получили 1150 вместо 879 запросов, т.е. прирост скорости составил всего 30%. Круто? Нет! На самом деле, этот тест ни о чем не говорит. Потому что мы подобрали изначально нагрузку на сайт таким образом, чтобы он был загружен на 100%. Но теперь мы отключили один ресурсоемкий процесс (около 30% общего времени), и наш сайт больше не загружен на-полную. Проверяем?

Видно, что синий график хорошо растет при дальнейшем увеличении нагрузки на сервер. И при 150 тестовых потоках без Тарантула мы получили почти 3 тысячи запросов в секунду, вместо 1 тысячи с Тарантулом. Вот теперь всё верно.

Кстати, почему Тарантул в тесте утилизировал лишь 30% CPU, но при его отключении производительность сервера увеличилась в три раза? Ответ прост: после отключения Тарантула уменьшилась так же нагрузка на воркеры php-fpm. Рост нагрузки произошел из-за роста потребления CPU процессом nginx. Эту нагрузку мы гнали увеличением количества потоков, пока не стали получать ошибки тестирования.

Теперь посмотрим на один интересный момент. Что такое на графике Tarantool+Connect? Как обычно, интересные моменты находятся случайно. Ситуация возникла, когда я сначала отключил все вызовы процедур Тарантула и запустил тест, но увидел, что процесс Тарантул продолжает есть приличный кусок CPU. Оказалось, что я забыл закомментировать процедуру установки соединения, вот эту часть:

# Init database
$tarantool = new Tarantool('localhost', 3301, 'good', 'bad');

try {
        $tarantool->ping();
} catch (Exception $e) {
        echo "Exception: ", $e->getMessage(), "\n";
}

На графике Tarantool+Connect отображается производительность системы только с инициализацией соединения, без каких-либо вычислительных операций. Оказалось, что в нашем демонстрационном приложении большая часть утилизации процесса Тарантула дает процедура установления соединения, а не собственно вычислительные задачи. По долям CPU точный расклад такой: 20% в случае теcта Tarantool+Connect и 28% в случае полноценной работы в роли БД.

Какой тут может быть вывод? Возможно, что процесс подключения к Тарантулу является ресурсоемкой операцией, или драйвер PHP работает не оптимально. Важно то, что при установке постоянного соединения, например, из демона, написанного на C, Java или Go те же операции утилизировали бы CPU в 4 раза меньше. Это нужно учитывать при написании программ.

WAL или не WAL ?


И, наконец, последний тест, который чрезвычайно прост, но очень интересен для разработчика. Как мы уже знаем, Тарантул декларирует сохранность данных благодаря механизму Snapshoot и Write Ahead Log. Первый создает и записывает регулярный слепок базы данных, а второй записывает все изменения в специальный лог изменений. В случае внезапного выключения или падения сервера изменения сохраняются и могут быть восстановлены при перезапуске системы.

Я протестировал нагрузку с выключенным WAL-логом. При 50 потоках количество rps увеличилось с 879 до 963. Понятно, что отключение записи на диск дополнительного лог-файла должно ускорять систему, которая загружена на 100%. Однако видно, что прирост скорости близок к погрешности измерения и совершенно не стоит той цены, которую за это приходится платить. Лучше быть уверенным в сохранности своих драгоценных данных.

Вместо послесловия


Раньше в веб разработке общим местом были рассуждения на тему «зачем, дескать, использовать быстрые языки программирования для написания web-frontend-а, всё равно база данных будет тормозить!». Теперь, по мере роста возможностей железа, виртуализации и NoSQL-решений, база перестает быть узким местом в приложении.

Для меня лично остаётся невыясненным один важный вопрос. Действительно ли Тарантул является настолько надежным хранилищем, как пытаются доказать его разработчики? «А что, блин, если нет?», как поется в одной песенке. Я бы хотел провести тестирование потери данных под названием «Сломать Тарантул», но мне обязательно нужна помощь и советы скептиков, ненавистников, конкурентов и просто хейтеров Mail.ru, чтобы смоделировать жесткие условия по-настоящему! Потому что «Платон мне друг, но истина дороже!»

И не забываем голосовать на демо-сайте: ugly.begetan.me! А то придется выкладывать каждый раз одного и того же енота из раздела ТОП.
Поделиться с друзьями
-->

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


  1. stavinsky
    21.02.2017 10:12
    +2

    Кажется лежим… а так интересно было что там
    curl -v http://ugly.begetan.me/
    * Trying 138.201.246.68...
    * TCP_NODELAY set
    * Connected to ugly.begetan.me (138.201.246.68) port 80 (#0)
    > GET / HTTP/1.1
    > Host: ugly.begetan.me
    > User-Agent: curl/7.51.0
    > Accept: */*
    >
    < HTTP/1.1 500 Internal Server Error
    < Server: nginx
    < Date: Tue, 21 Feb 2017 07:11:13 GMT
    < Content-Type: text/html; charset=UTF-8
    < Transfer-Encoding: chunked
    < Connection: keep-alive
    <
    * Curl_http_done: called premature == 0
    * Connection #0 to host ugly.begetan.me left intact


    1. Begetan
      21.02.2017 11:45

      Прошу прощения! Исправлял один секретный баг и добавил новый. Можно снова пробовать.

      За любознательность — плюс!


  1. DjOnline
    21.02.2017 12:20
    +1

    >> Когда я увеличивал количество параллельных запросов wrk, то увеличивалось процессорное время потребляемое nginx-ом. Из-за этого не хватало ресурсов для php-fpm и в логах возникали ошибки 499 и 502. В этом месте было бы правильным переписать приложение, заменив Тарантул на какую-нибудь обычную SQL базу данных – MySQL или PostgreSQL и сравнить результаты.

    Да, хорошо бы сравнить с Mysql InnoDB и TokuDB в Percona. Но этот абзац я не понимаю, ошибки возникают в php-fpm, а предложение о замене базы.


    1. Begetan
      21.02.2017 12:59

      Замена базы нужна, что бы показать разницу производительности между Тарантулом и MySQL, например. Но мы понимаем же, что ожидается заведомый регресс.

      Я бы поступил наоборот — переписал бы какое то приложение с MySQL на Тарантул.

      Про ошибки nginx относилось к первой части статьи о методике тестирования.


      1. DjOnline
        21.02.2017 13:33
        +2

        Пока не понимаем. Потому что неплохо бы проверить и с TokuDB. И с доступом к MySql через HandlerSocket, и через Memcached в обход протокола SQL.
        И вот тогда уже с этими цифрами всем будет всё очевидно.


  1. corr256
    21.02.2017 12:55

    Очередной недотестер… Кто вас учил делать тесты производительности в вм?


    1. Begetan
      21.02.2017 13:01
      +2

      У вас есть замечательная возможность научить всех правильно делать тесты производительности. Напишите как надо, например, в комментариях тут.


  1. saw_tooth
    21.02.2017 14:06

    Не прицепиться ради, но вот:


    1. saw_tooth
      21.02.2017 14:12

      Jmeter test report
      ping 41mc
      average — 260ms
      50 users, 7 repeats, thread up — 10 sec


      1. Begetan
        21.02.2017 15:44

        Можно уточнить, где у вас в отчете rps?


        1. saw_tooth
          21.02.2017 15:58

          Throughput можно в принципе считать как rpc.


          1. Begetan
            21.02.2017 16:32

            42 запроса в секунду. Понятно. Значит модуль ограничения коннектов nginx работает как надо :)


      1. Vest
        21.02.2017 19:48

        Я решил немного проверить и по возможности дополнить ваше измерение своим тестом. Обычно в таких случаях мне всегда нравится постепенное увеличение нагрузки с целью понять предел системы и определить то, за чем нужно следить во время выкатывания нового релиза.

        Надеюсь вы меня не будете строго судить, потому как я лишь любитель :)

        Суть моего теста состояла в том, чтобы увеличивать количество параллельных пользователей попеременно опрашивающих четыре страницы с сервера (без всяких там голосований и пр… можно конечно и такой тест написать, но я не хочу флудить в базу). Наличие т.н. Think Time я решил опустить, потому что любая пауза в действиях пользователя снижает одновременное количество запросов, которые сервер вынужден обработать. Это всё равно что — «мы обслуживаем сто миллионов пользователей в день, где каждый пользователь в среднем открывает одну страницу в сутки». В таких выражениях важно знать некоторую конкурентность пользователей (среднюю, минимальную, максимальную) и время ответа.

        В моей любительской теории я просто оцениваю параллельные запросы, которые потом можно наблюдать в проекте и уже решать помогло ли нам увеличение числа ядер/серверов/бутылок_пива_в_админке или нет.

        Самым важным параметром, на который я опирался — это количество т.н. транзакций в единицу времени. То есть сколько запросов может обслужить сервер если параллельное число запросов равно N:

        Transactions vs Threads


        1. Begetan
          21.02.2017 20:15
          +1

          Интересный график, но было бы намного интереснее делать их с отключенной директивой limit_conn 64 в настройках nginx. Мои тесты выполнялись с отключенными ограничениями, естественно.

          Я думаю, что от 20 до 70 потоков работал лимит соединений, а после 70 видимо достаточно сильно выросла нагрузка на nginx и он подавил остальные процессы.

          Про рост времени ожидания вы просто еще раз подтвердили выводы из статьи Константина Осипова, ссылка на которую есть наверху.


  1. pkuutn
    21.02.2017 16:32

    Объясните мне, пожалуйста — неужели практически для всех веб-сайтов база нужна исключительно для хранения key-value?
    Слышу везде про NoSQL. Сложные структуры данных, получается, не нужны? связи там…


    1. Begetan
      21.02.2017 16:36

      Рейтинги или сессии пользователей достаточно распространенная задача для K/V хранилища.

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


      1. sergey_prokofiev
        21.02.2017 19:25

        … а потом с болью и кровью имплементировать «ACID между компонентами». Заодно узнать про eventual consitency. По ходу дела научиться правильно инвалидировать кеши. А затем удивиться перфомансу.

        PS Я не к тому что микросервисы или NoSQL — это плохо. Это просто инструмент которым надо аккуратно пользоваться, понимая что для забивания гвоздей лучше походит молоток чем отвертка.

        PPS Раз уж сравнили тарантул с mysql, поинтересуюсь: а тарантул обеспечивает ACID и декларативный язык запросов сравнимый с SQL?


        1. Begetan
          21.02.2017 20:20

          Тарантул это молоток и есть. Для SQL есть свои отвертки с хитрой резьбой.

          Только приглядитесь к логотипу!

          image


          1. sergey_prokofiev
            21.02.2017 20:26
            -1

            То есть вы сравнили молоток с отверткой.


        1. z3apa3a
          22.02.2017 00:49
          +2

          Ответы на ваши вопросы есть прямо на первой странице tarantool.org in large friendly letters:

          а тарантул обеспечивает ACID

          да.
          декларативный язык запросов сравнимый с SQL

          Да. Тарантул вообще декларируется не как NoSQL хранилище данных, NoSQL это скорей побочная черта архитектуры, а вообще это сервер приложений LUA. LUA в том числе можно использовать в декларативном стиле, но совсем не обязательно, т.к. это гораздо более удобный и гибкий язык, чем TSQL или PL/SQL. А поддержка SQL в анонсирована в разрабатываемых версиях Tarantool.


          1. sergey_prokofiev
            22.02.2017 11:34

            Почитал доку.
            Транзакции: уровней изоляции не нашел, только оптимистические блокировки.
            Судя по ответу ниже, SQL в процессе.
            Я правильно понимаю, что в отличии от многих NoSQL решений, tarantool не обеспечивает «из коробки» инструментов построения отказоустойчивого распределенного кластера, как то шардирования, синхронизации после сетевого разделения? Репликация не в счет.


            1. danikin
              22.02.2017 18:39
              +1

              Про Транзакции почитайте тут: http://kostja.github.io/misc/2017/01/24/tarantool-design-principles.html и тут: https://tarantool.org/doc/book/box/atomic.html

              По поводу второго вашего вопроса — правильно понимаете. Но скорее всего у вас нет и не будет таких нагрузок, чтобы одна машина с Тарантулом не справилась. Даже у мейла редко бывают такие нагрузки — и мы ставим 4 машины, шардим на уровне приложения и это работает на долгие годы впероед.


        1. danikin
          22.02.2017 02:12
          +1

          ACID обеспечивает. SQL в процессе. Если интересен недекларативный язык запросов, то это Lua, работает в виде хранимых процедур внутри Tarantool.