Как настроить сервер и код на многочисленные одновременные запросы?

Распишу по порядку. Три пункта: Задача, Мое костыльное решение(проблемы) и Вопрос.
Задача:
Спойлер открывать только умникам и умницам
Чтобы не углубляться и не расписывать свою задачу со всеми деталями которые не имеют отношения к вопросу, я привожу просто абстрактный пример, сторонников отвечать "зачем это нужно?", "это бесполезно" попрошу не оставлять комментарии, это очень важно для меня. Спасибо за понимание.

Есть сайт, в котором в определенное время будет нагрузка. Например: каждый день в 19:00 около 2000 пользователей нажимают на кнопку. В базе создается "цепь" в таблице:
id|parent_record_id|user_id
2 |_______________1|_____45
3 |_______________2|_____32
4 |_______________3|_____98

Помимо этой записи в базе еще создаются записи в других таблицах и проводятся определенные расчеты. Скрипт выполняется примерно от 0.1 до 0.6 человеческих секунд.
Цепь должна быть не разрывной.
Мое костыльное решение:
Сервер:
Процессор 8 X QEMU Virtual CPU version 1.5.3 2099.998 MHz
Оперативная память 12019 MiB
ПО сервера: Apache+FastCGI
СУБД: MySQL
DB::beginTransaction(); // начинается неразрывная транзакция
$checkLine = Users::where('line_is_busy', '=', 1)->count(); // проверяем занята ли линия
if ($checkLine) { // если да то кидаем ошибку
     return "Линия занята, кликните еще раз, вдруг вам повезет попасть, а то разработчик не знает как тут реализовать грамотно очередь";
}
// если линия свободна то занимаем ее
Users::whereId($user->id)->update(['line_is_busy' => 1]);
//
// здесь выполняется тот скрипт на 0.1-0.6 секунд
//
Users::whereId($user->id)->update(['line_is_busy' => 0]); // освободили линию
DB::commit();

Решение выше меня не устраивает тем, что если первый пользователь нажал на кнопку. Остальные кликают дальше пока не "попадут".
Вопросы:
1) Как реализовать так, чтобы пользователь не получал ошибку, а просто ждал ответа от сервера.
2) В ходе исследования появились мысли, что Nginx подойдет для этой задачи лучше чем Apache. Так ли это? Или в данном контексте это не имеет значения?
3) Можно ли на сервере с такими данными реализовать эту задачу чтобы сервер и СУБД не отваливались?
4) Как можно это синтетически протестировать?

P.S. Я не прошу решить задачу за меня. Я просто сбился немного с пути и хочу подпитаться вашими размышлениями и наводками.
Заранее премного благодарен за Ваши ответы и уделенное Вами время.
  • Вопрос задан
  • 306 просмотров
Решения вопроса 2
ThunderCat
@ThunderCat
{PHP, MySql, HTML, JS, CSS} developer
Чтобы не углубляться и не расписывать свою задачу со всеми деталями которые не имеют отношения к вопросу,
Лучше бы вы задачу расписали, из вашего решения ни цель, ни проблема не видны. Вообще вопрос скорее всего возник из непонимания процесса работы в целом, что можно было бы решить 2 словами.

Как реализовать так, чтобы пользователь не получал ошибку, а просто ждал ответа от сервера.
Какой ответ? Нужно посчитать и отдать результат или достаточно чтобы ответ был "все ок"?

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

Можно ли на сервере с такими данными реализовать эту задачу чтобы сервер и СУБД не отваливались?
Вообще не вижу проблем, Даже если 100 человек одновременно кликнет - особой беды не будет. Во первых, на каждого будет выделен отдельный трит пхп, во вторых - такие мелкие записи вообще ниачем, мускуль их может херачить сотнями в секунду.

В целом: Решение с транзакциями вообще спорное, и не понятно зачем здесь воткнуто (интересно ваше решение - откудо оно и зачем? И в догонку - почему они так редко используются, а в вашем случае "очень нужны"?).

Как можно это синтетически протестировать?
apache jmeter или yandex tank.

Вывод
Скорее всего переживания ваши ниачем, но если вы по секрету расскажете нам что за уникальную вундервафлю вы строите, возможно ответ будет точнее и осмысленнее.
Ответ написан
saboteur_kiev
@saboteur_kiev
software engineer
А почему бы при нажатии на кнопку не создавать транзакцию, а делать запись в отдельную таблицу с задачей?

А на бэкенде просто в цикле или триггером проверять наличие записей в этой таблице, и выполнять транзакции по очереди?

Ну то есть собсвтенно организовать очередь
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
rozhnev
@rozhnev
Fullstack programmer, DBA, медленно, дорого
Возможный вариант реализации (один из возможных):
При нажатии на кнопку:
- блокируем кнопку
- выводим надпись "ждите ответа"
- AJAX - запрс на сервер
- на сервере ставим запрос в очередь
клиент либо ждёт пока запрос из очереди исполнится либо не ждет а периодически проверяет статус чтобы разблокировать енопку и показать "ОК"

Вместо AJAX можно прикрутить WEB-socket, тогда сервер сможет сам оповестить клиента об окнчании операции.

На сервере предусмотреть проверку запроса на уникальность, чтобы один пользователь не заблокировал систему множеством запросов и т. д.
Ответ написан
Fragster
@Fragster
помогло? отметь решением!
начать транзакцию, заблокировать эксклюзивно таблицу, выполнить операции, зафиксировать транзакцию
https://dev.mysql.com/doc/refman/8.0/en/lock-table...

Все автоматом встанут в очередь и будут выполняться по мере отпускания блокировки предыдущим участником. При таймауте блокировки будет ошибка, так что имеет смысл его значение установить до максимального времени выполнения скрипта php, да и само время выполнения увеличить, если оно не достаточно.

В принципе, блокировка и ожидание на ней будут только на время транзакции, что должно быть сильно меньше общего времени выполнения скрипта
Ответ написан
@darkfriend
Возможно стоит посмотреть в сторону семафоров, тот же mutex например.
Его можно реализовать через бд, а можно через файл или например через redis...
Рассматривали его?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы