Приветствую У меня есть скрипт, который из одной таблицы берет select SUM(amount) as n from billing И если сумма положительная вставляет задачу на выплату с полученной суммой и обновляет факт выплаты в таблицы billing Но возник вопрос - а предположим, получиться так, что случайно 2 пользователя одновременно нажмут кнопку выплаты, и запрос на select SUM() для двоих произойдут одновременно, до того, как у первгого случится обновление факта выплаты в таблице billing. Как правильно от таких ситуаций защишаться? Благодарю!
Очень крайне маловероятно, хоть пару микросекунд да будет разница. А вообще такие вещи в транзакции оборачиваеются.. Ни странно как то все всегда всю сумму могут вывести? Обычно у каждого пользователя свой счёт и выплаты с него идут
@ADSoft есть таблица billing - где идут начисления. Когда менеджер проводит выплату, каждому начислению ставиться флаг что выплачено. Так вот между вытаскиванием строк, которые доступны для выплаты И проставлением флага доли микросекунл. И получается, может случиться так, что второй запрос на вытаскивание доступных к выплате строк, пройдет быстрей, чем проставятся по 1 запросу метки выплачено. Нашел хорошую статью https://habr.com/ru/post/238119/ SELECT * FROM requests WHERE id=5 FOR UPDATE Автоматом для полученных строк сделает LOCK Вопрос, правильный алгоритм такой(уточняю?) 1 - set autocommit=0; //отключаем autocommit 2 - start transaction; (также, можно написать BEGIN; ) 3 - Выполняем запросы SELECT * FROM requests WHERE id=5 FOR UPDATE (НАДО ЛИ ДОБАВЛЯТЬ ЭТО?) UPDATE 4 - commit; //Фиксация действий, запись их в физическую БД Но еще наткнулся на https://habr.com/ru/post/46542/ Впринципе, блокировки решают задачу, которую я хочу решить. Не понятно, в каком случае лучше использовать транзакции а в какой блокировки?
Попробовал использовать транзакции. Вопрос - а если ли mysql команда - commit IF no error - типа закомитеть, только если в транзакции не было ошибок? Либо нужно каждый запрос проверять если НЕ ОК делать rollback?
В базах данных есть такое понятие "уровень изоляции". Для MySQL по умолчанию действует уровень REPEATABLE READ. Это значит, что пока длится твоя транзакция, ты будешь видеть данные такими, какими они были в начале транзакции, даже если что-то изменилось. Как это может отразиться на твоих "параллельных" расчетах выплат? Давай условно будем называть сеансы расчёта как "первый" и "второй", потому что всегда кто-то начинает раньше. - если первый записал данные раньше чем стартовал второй, то нет проблемы, очевидно. - если второй начал читать данные до того, как закончил работу первый, то второй увидит исходные данные и может повторить тот же расчёт, что и первый. второй должен прийти к тем же цифрам что и первый, так? проблема может возникнуть только если он создаст ещё одну запись с итогами, а не перезапишет существующую. Это можно разрулить с помощью уникальных индексов. Надо создать условия в которых попытка записать расчёт за тот же период для того же подразделения/работника и т.д. должна заканчиваться ошибкой! БД может отказать тебе, если нарушается уникальность. Придумай какое сочетание полей в твоём billing должно быть ункальным и неповторимым и создай индекс. Например пара (ид_работника, расчетный_месяц). Гуглить: MySQL ISOLATION LEVEL MySQL ADD UNIQUE INDEX MySQL INSERT IGNORE MySQL INSERT ON DUPLICATE KEY UPDATE MySQL REPLACE --- Добавлено --- P.S. Дополнительно можно использовать блокировку в начале расчёта, чтобы второй не смог его начать, если первый не закончил. Гуглить: MySQL NAMED LOCKS