Это mysql потокобезопасно? - PullRequest
1 голос
/ 30 марта 2020

У меня есть таблица со следующими столбцами: id(int), wallet_id (int), amount (int), recharge_date (timestamp), status (int). status представляет ожидающий (0), успешный (1) или неудачный (2). Прежде чем пополнять счет, я должен убедиться, что общая сумма пополнения за текущий месяц не превышает 10 000. Мне также нужно покрыть сценарий ios двумя или более одновременными запросами на перезарядку.

Я думаю, что следующие шаги могут помочь избежать условий гонки:

  1. Вставить в таблицу со статусом 0 (в ожидании) для wallet_id, скажем "1234"
  2. Выберите сумму (сумму) из таблицы, где wallet_id = 1234 И месяц (recharge_date) = текущий месяц и статус в (0,1)
  3. Если эта сумма превышает 10 000, обновите статус до 2 (не удалось) и вернитесь.
  4. Выполните некоторые операции на уровне приложения, а затем обновите статус до 1 (успешно) или 2 (не удалось)

Эти шаги гарантируют безопасность потока? Кроме того, есть ли еще какой-нибудь более простой способ mysql обеспечения безопасности потока для этого варианта использования?

Редактировать: Объяснение фактического потока для большей ясности:

  1. Сервер получает запрос на добавьте сумму 500 для определенного wallet_id (скажем, 99).
  2. Вставьте новый ордер в статус ожидания: INSERT INTO Заказы ЗНАЧЕНЫ (wallet_id = 99, сумма = 500, статус = в ожидании)
  3. Необходимо убедиться, что вы можете сделать не более 10000 пополнения счета в месяц. Таким образом, ВЫБЕРИТЕ СУММУ (количество), ГДЕ wallet_id = 99 И месяц = ​​текущий_монт И статус IN (в ожидании, успех)
  4. Если сумма на шаге 3>> = 10 000, то обновить статус заказа, созданного на шаге 2, не удалось. Возврат
  5. Если сумма <10 000, то выполните другие действия, вернитесь и обновите заказ, созданный на шаге 2, до успешного или неудачного. </li>

Путаница возникает, если более одного одновременного запроса получил за этот поток.

1 Ответ

1 голос
/ 30 марта 2020

Хороший улов параллелизма. Для этой цели выполняются транзакции базы данных .

Вы можете сделать это с помощью такой транзакции. (не отлажено)

BEGIN TRANSACTION;

SELECT SUM(amount) INTO @sum
  FROM mytable
 WHERE status IN (0,1)
   AND recharge_date >= LAST_DAY(CURDATE()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
   AND recharge_date < LAST_DAY(CURDATE()) + INTERVAL 1 DAY 
   AND wallet_id = (((whatever))
   FOR update;

UPDATE mytable SET status=2
 WHERE status IN (0,1)
   AND recharge_date >= LAST_DAY(CURDATE()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
   AND recharge_date < LAST_DAY(CURDATE()) + INTERVAL 1 DAY 
   AND wallet_id = (((whatever))
   AND @sum > 1000;

/* then do your application logic, and come back and do */

UPDATE mytable SET status=(((whatever)))
 WHERE status IN (0,1)
   AND recharge_date >= LAST_DAY(CURDATE()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
   AND recharge_date < LAST_DAY(CURDATE()) + INTERVAL 1 DAY 
   AND wallet_id = (((whatever))
   AND @sum > 1000;

/* finally, commit your transaction. */
COMMIT;

Обратите внимание, что все операторы SELECT...FOR UPDATE и два UPDATE имеют одинаковый оператор WHERE. Это важно, потому что вы должны избегать обновления строк, которые вы не выбрали для обновления. Когда вы закончите, COMMIT транзакции. Если вы решите, что не хотите продолжать в любое время после BEGIN TRANSACTION, вы можете ROLLBACK выполнить транзакцию и все в том состоянии, в котором они находились до того, как вы начали.

Этот шаблон

   AND recharge_date >= LAST_DAY(CURDATE()) + INTERVAL 1 DAY - INTERVAL 1 MONTH
   AND recharge_date < LAST_DAY(CURDATE()) + INTERVAL 1 DAY 

- лучший способ выбрать даты в текущем календарном месяце. Это позволяет использовать индекс по столбцу для поиска соответствующих записей, где MONTH(recharge_date) = MONTH(CURDATE()) нет.

Я немного запутался в одном. Вы сказали, что вам нужно сделать СУММУ, которая указывает, что ваше изменение на status затронет несколько записей в вашей таблице. Это действительно то, что вы хотите?

...