Tarantool: нагрузочное тестирование

93df8c8eee4a44d0a6e999c9e802e3da.png

В статье «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, начинала быстро расти задержка. В конечном счете, увеличение количества параллельных запросов приводило к появлению потерь. Впрочем, на этот счет есть прекрасная иллюстрация:

321e0fdf66e744c6bbeb8ca2c2253796.png

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

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


Сетевой пинг между измеряемым сервером и генератором трафика составлял 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 тысяч записей, используя индекс по рейтингу

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

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

b91eec1cce4443dab49f752b2b735db0.png

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

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

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


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

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

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

5cffacf469dd48c09a09f19889b1e69f.png

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

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-решений, база перестает быть узким местом в приложении.

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

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

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

  • 21 февраля 2017 в 10:12

    +1

    Кажется лежим…, а так интересно было что там
    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

© Habrahabr.ru