Мы в PushAll обрабатываем несколько тысяч запросов в секунду для получения статистики доставки и открытия уведомлений и для передачи контента оповещений. Обычная БД вроде MySQL не справляется с таким потоком запросов и не может так быстро отвечать.
Стараясь все больше операций перенести на быстрые NoSQL хранилища вроде Redis, мы хотим знать как эффективнее его использовать и не будет ли у нас проблем с большим количеством соединений.
Также для работы мы используем форки PHP и нам было интересно, а как поведет себя Redis, если мы будем делать несколько тысяч соединений в одновременно в нескольких потоках. Мы решили поделиться с сообществом нашими тестами.
Железо
Мы тестируем на одном из VPS PushAll:
CPU: Intel Xeon E5-1650v2 3.5 Ghz — 2 ядра.
RAM: 3 Gb DDR3 1866Mhz
PHP7.
Redis 3.0.7
Условия тестирования
Мы написали многопоточного бота PHP, который:
- Делает форки в цикле — 100 форков без каких либо задержек
- Каждый форк в своем цикле, 1000 раз создает соединение с Redis и производит инкремент
- Родительский процесс ждет 3 секунды и берет значение, если не ждать — Redis вернет не полное значение инкеремента
Также мы протестировали вариант с 1000 форками и как будут отличаться результаты при использовании UNIX-сокета и TCP.
100 форков, 1000 соединений в каждом, TCP
# time php benchmark.php
End:100000
real 0m8.666s
user 0m0.063s
sys 0m0.073s
100 форков, 1000 соединений в каждом, UNIX-сокет
# time php benchmark.php
End:100000
real 0m6.021s
user 0m0.023s
sys 0m0.067s
TCP-сокет в среднем на 30% медленнее. (напомню, тут испытывается больше не производительность работы самого Redis, а то, как он обрабатывает соединения)
1000 форков, 1000 соединений в каждом, TCP+UNIX
Повышаем ставки
TCP:
# time php benchmark.php
End:903505
real 1m7.659s
user 0m0.073s
sys 0m0.753s
За 3 секунды Redis не успел их у себя до конца обработать — это одна из деталей, которую надо учитывать. Если слишком быстро считывать значения, можно поймать момент, когда они еще старые.
Что самое интересное, при проведение того же самого теста, но для unix-сокета, мы получаем ошибки:
Fatal error: Uncaught RedisException: Redis server went away in ....
То есть, unix-сокет не смотря на то, что он быстрее, может обрабатывать несколько меньшее количество запросов. Либо как вариант — возможно, что из за того что он такой быстрый не справляется уже сам сервер Redis'а.
Мы проводили подобные тесты и для php-fpm — там также TCP-сокет давал меньше ошибок со связкой с NGINX чем UNIX-сокет. Разница в скорости была там незначительна.
Прикладываю скрипт:
<?php
declare(ticks = 1);
for($i=0; $i < 1000; $i++){
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
//parent
} else {
//child
for ($a=0; $a < 1000; $a++) {
$redis = new Redis();
//$redis->connect('127.0.0.1:6379');
$redis->connect('/run/redis/redis.sock');
$redis->incr('pushall:benchmark');
$redis->close();
}
exit;
}
}
pcntl_wait($status); //wait
sleep(3);
$redis = new Redis();
$redis->connect('/run/redis/redis.sock');
echo 'End:'.$redis->get('pushall:benchmark')."\r\n";
$redis->setTimeout('pushall:benchmark', 1);
$redis->close();
UPD pconnect
Оказывается pconnect работает в форках (странно)
Взял случай 100 процессов TCP:
pconnect
# time php benchmark.php
End:100000
real 0m4.679s
user 0m0.023s
sys 0m0.080s
connect
# time php benchmark.php
End:100000
real 0m9.100s
user 0m0.037s
sys 0m0.103s
Для сравнения UNIX сокет на 100 форках:
pconnect
# time php benchmark.php
End:100000
real 0m4.393s
user 0m0.023s
sys 0m0.073s
connect
# time php benchmark.php
End:100000
real 0m6.002s
user 0m0.027s
sys 0m0.057s
Причем, что интересно, при использовании pconnect — разница между TCP и UNIX сокетом не такая уж и большая 5-10%. При этом даже сделав все, что мне предлагали в комментариях — мне не удалось заставить работать unix-сокеты при 1000 форках.
UPD 2 Pconnect + 1000 форков
UNIX Socket
pconnect
# time php benchmark.php
End:1000000
real 0m35.445s
user 0m0.050s
sys 0m0.637s
connect — падает в Fatal error: Uncaught RedisException: Redis server went away in…
TCP
pconnect
# time php benchmark.php
End:989596
real 0m43.711s
user 0m0.050s
sys 0m0.623s
# time php benchmark.php
End:903505
real 1m7.659s
user 0m0.073s
sys 0m0.753s
Разница в 20%.
PS. Хабр, а почему хаб MongoDB и MySQL есть, а Redis нет?
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какой метод вы используете, или использовали бы?
40.67% TCP (т.к. он стабильнее, или нет? опишите в комментарии)170
59.33% UNIX-socket (т.к. он быстрее)248
Проголосовали 418 пользователей. Воздержались 270 пользователей.