Задача: На проекте есть конкурсы для рекламодателей, каждый потраченная копеечка рекламодателем идет в users_statistics.money_spent_ad тип данных поля (double 14,6), задача состоит в следующем: на проекте есть конкурсы, за каждый потраченный рубль, система должна начислить 0.5 балла в таблицу в поле contest_points.points.
Допустим человек сначала потратил 0.40 руб, а потом еще 0.61, в общем он уже потратил 1.01 руб.
Как начислять баллы ?
Решение такое: Сначала мы читаем данные из таблицы и поля users_statistics.money_spent_ad далее рассчитываем кол-во баллов, расчет происходит так, нам за каждый потраченный рубль нужно начислять 0.5 балла.
Берём ту сумму которая уже потрачена, допустим потратил он уже 0.80 руб, далее берём ту сумму которая будет прибавлена к тем потраченным, допустим это 4.30, берём прибавляем уже у имеющейся 0.80 + 4.30 = 5.10
Затем от уже имеющейся цифр берём только целые цифры, то есть 5 и 0. 5 - 0 = 5. Далее 5 * 0.5 балла = 2.5 балла.
Думаю тут все понятно, проблема в том что, когда все эти расчеты происходят данные могут поменяться, потрачено было 0.80 изначально, но в момент расчетов которые производим выше, уже поменяется на допустим 2.30 и прибавится 5.50. 2.30 + 5.71 = 8.01, далее высчитываем 8 - 2 = 6. 6 * 0.5 = 3 балла. Итого получается, что в когда 1 расчет происходит могут поменяться данные, то есть затраченные деньги на рекламу, в первом расчеты мы получим на зачисление баллов 2.5, в 2 расчете 3 балла, 2.5 + 3 = 5.5 балла. Даже если эти запросы выполнятся паралелльно, то кол-во баллов вроде бы не поменяется, но меня это настораживает.
Алгоритм должен быть таким.
1. Взять данные из SELECT users_statistics
2. Посчитать сколько баллов нужно дать юзеру.
3. Зачислить баллы в таблицу contest_data.
4. Прибавить к уже потраченным деньгам сумму в таблице users_statistics
Я пошел в гугл, поискал про блокировки мускула.
Подходящая вещь как мне кажется это - SELECT… LOCK IN SHARE MODE - Я понял это след. образом, что происходит первый расчет и когда происходит SELECT мы блочим конкретную запись на UPDATE в таблице users_statistics, далее если другие запросы пытаются ее поменять но там стоит блок, они ждут окончания завершения транзакции и выполняются. То есть создается список из очередей, как я понимаю.
Также из этой таблицы users_statistics может прочитать данные любой другой клиент. SELECT… FOR UPDATE - все тоже самое, между переключениями блокировок может вклиниться транзакция и получит deadlock, это я так понимаю блокировка записи бесконечность.
Данном случае блокировка будет ставиться дважды, сначала совместная блокировка при чтении, затем исключительная при записи. Так как блокировок две, то есть теоретический шанс проскочить третьей между ними и вызвать deadlock. Также читать данные не получиться потом что блокировка, это не подходит.
Вообщем не знаю как верно поступить и туда ли я вообще иду.
Даже если применить SELECT… LOCK IN SHARE MODE я даже не понимаю как правильно это сделать.
Кто может помочь, подсказать направить в нужное русло, будут очень признателен, может мне вообще не нужны эти блокировки и транзакции.