В статье "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 миллисекунды. Много это или мало? Для начала вспомним, что происходит при обращении к странице в скрипте нашего сервиса.
- Скрипт видит обращение нового клиента и пытается прочитать куки, которых нет
- В Тарантуле генерируется уникальный uuid
- В Тарантуле выполняется команда записи upsert, которая записывает uuid, время, IP и User-Agent клиента
- Скрипт вызывает команду Тарантула, которая выбирает Топ-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).
Самое главное в этом всём то, что параметры тестирования были подобраны именно под нашу конфигурацию хостинга и тестового сервера. Для тестирования другой связки, например, в локальной сети, нужно подобрать другие параметры.
Теперь давайте посмотрим на график, а потом я покажу пару интересных моментов.
Итак, вместо обращений к Тарантулу в нашей программе мы воткнули заглушку, вот так, по-крестьянски:
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)
DjOnline
21.02.2017 12:20+1>> Когда я увеличивал количество параллельных запросов wrk, то увеличивалось процессорное время потребляемое nginx-ом. Из-за этого не хватало ресурсов для php-fpm и в логах возникали ошибки 499 и 502. В этом месте было бы правильным переписать приложение, заменив Тарантул на какую-нибудь обычную SQL базу данных – MySQL или PostgreSQL и сравнить результаты.
Да, хорошо бы сравнить с Mysql InnoDB и TokuDB в Percona. Но этот абзац я не понимаю, ошибки возникают в php-fpm, а предложение о замене базы.Begetan
21.02.2017 12:59Замена базы нужна, что бы показать разницу производительности между Тарантулом и MySQL, например. Но мы понимаем же, что ожидается заведомый регресс.
Я бы поступил наоборот — переписал бы какое то приложение с MySQL на Тарантул.
Про ошибки nginx относилось к первой части статьи о методике тестирования.DjOnline
21.02.2017 13:33+2Пока не понимаем. Потому что неплохо бы проверить и с TokuDB. И с доступом к MySql через HandlerSocket, и через Memcached в обход протокола SQL.
И вот тогда уже с этими цифрами всем будет всё очевидно.
saw_tooth
21.02.2017 14:06Не прицепиться ради, но вот:
saw_tooth
21.02.2017 14:12Jmeter test report
ping 41mc
average — 260ms
50 users, 7 repeats, thread up — 10 secVest
21.02.2017 19:48Я решил немного проверить и по возможности дополнить ваше измерение своим тестом. Обычно в таких случаях мне всегда нравится постепенное увеличение нагрузки с целью понять предел системы и определить то, за чем нужно следить во время выкатывания нового релиза.
Надеюсь вы меня не будете строго судить, потому как я лишь любитель :)
Суть моего теста состояла в том, чтобы увеличивать количество параллельных пользователей попеременно опрашивающих четыре страницы с сервера (без всяких там голосований и пр… можно конечно и такой тест написать, но я не хочу флудить в базу). Наличие т.н. Think Time я решил опустить, потому что любая пауза в действиях пользователя снижает одновременное количество запросов, которые сервер вынужден обработать. Это всё равно что — «мы обслуживаем сто миллионов пользователей в день, где каждый пользователь в среднем открывает одну страницу в сутки». В таких выражениях важно знать некоторую конкурентность пользователей (среднюю, минимальную, максимальную) и время ответа.
В моей любительской теории я просто оцениваю параллельные запросы, которые потом можно наблюдать в проекте и уже решать помогло ли нам увеличение числа ядер/серверов/бутылок_пива_в_админке или нет.
Самым важным параметром, на который я опирался — это количество т.н. транзакций в единицу времени. То есть сколько запросов может обслужить сервер если параллельное число запросов равно N:
Transactions vs ThreadsBegetan
21.02.2017 20:15+1Интересный график, но было бы намного интереснее делать их с отключенной директивой limit_conn 64 в настройках nginx. Мои тесты выполнялись с отключенными ограничениями, естественно.
Я думаю, что от 20 до 70 потоков работал лимит соединений, а после 70 видимо достаточно сильно выросла нагрузка на nginx и он подавил остальные процессы.
Про рост времени ожидания вы просто еще раз подтвердили выводы из статьи Константина Осипова, ссылка на которую есть наверху.
pkuutn
21.02.2017 16:32Объясните мне, пожалуйста — неужели практически для всех веб-сайтов база нужна исключительно для хранения key-value?
Слышу везде про NoSQL. Сложные структуры данных, получается, не нужны? связи там…Begetan
21.02.2017 16:36Рейтинги или сессии пользователей достаточно распространенная задача для K/V хранилища.
Сложные связи конечно нужны, но допустим, одна из задач микросервисной архитектуры состоит в упрощении или переносе этой сложности из базы данных на уровень связи компонентов.sergey_prokofiev
21.02.2017 19:25… а потом с болью и кровью имплементировать «ACID между компонентами». Заодно узнать про eventual consitency. По ходу дела научиться правильно инвалидировать кеши. А затем удивиться перфомансу.
PS Я не к тому что микросервисы или NoSQL — это плохо. Это просто инструмент которым надо аккуратно пользоваться, понимая что для забивания гвоздей лучше походит молоток чем отвертка.
PPS Раз уж сравнили тарантул с mysql, поинтересуюсь: а тарантул обеспечивает ACID и декларативный язык запросов сравнимый с SQL?Begetan
21.02.2017 20:20Тарантул это молоток и есть. Для SQL есть свои отвертки с хитрой резьбой.
Только приглядитесь к логотипу!
z3apa3a
22.02.2017 00:49+2Ответы на ваши вопросы есть прямо на первой странице tarantool.org in large friendly letters:
а тарантул обеспечивает ACID
да.
декларативный язык запросов сравнимый с SQL
Да. Тарантул вообще декларируется не как NoSQL хранилище данных, NoSQL это скорей побочная черта архитектуры, а вообще это сервер приложений LUA. LUA в том числе можно использовать в декларативном стиле, но совсем не обязательно, т.к. это гораздо более удобный и гибкий язык, чем TSQL или PL/SQL. А поддержка SQL в анонсирована в разрабатываемых версиях Tarantool.sergey_prokofiev
22.02.2017 11:34Почитал доку.
Транзакции: уровней изоляции не нашел, только оптимистические блокировки.
Судя по ответу ниже, SQL в процессе.
Я правильно понимаю, что в отличии от многих NoSQL решений, tarantool не обеспечивает «из коробки» инструментов построения отказоустойчивого распределенного кластера, как то шардирования, синхронизации после сетевого разделения? Репликация не в счет.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 машины, шардим на уровне приложения и это работает на долгие годы впероед.
danikin
22.02.2017 02:12+1ACID обеспечивает. SQL в процессе. Если интересен недекларативный язык запросов, то это Lua, работает в виде хранимых процедур внутри Tarantool.
stavinsky
Кажется лежим… а так интересно было что там
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
Begetan
Прошу прощения! Исправлял один секретный баг и добавил новый. Можно снова пробовать.
За любознательность — плюс!