Mysql транзакция для перевода кредита между пользователями - PullRequest
1 голос
/ 17 февраля 2012

У меня есть простая система электропитания, поскольку пользователи могут брать друг у друга:

mysql_query("INSERT INTO power (sender, receiver, amount)
    VALUES ('$sender', '$receiver', '$amount')");
mysql_query("UPDATE users SET power=power-$amount WHERE user_id='$sender'");
mysql_query("UPDATE users SET power=power+$amount WHERE user_id='$receiver'");

Я проверяю, достаточно ли у отправителя кредита для перевода по запросу SELECT, прежде чем запускать вышеупомянутый наборзапросы.

Проблемы 1: Я думаю, лучше поместить все эти четыре запроса в Transaction;так как InnoDB ACID гарантирует более безопасную работу.Какая транзакция будет лучшей для этого?

Выпуск 2: power is unsigned int.Если по какой-либо случайности (даже маловероятно) пользователю не хватает кредита, power-$amount не установит 0;вместо этого он будет циклически проходить через наибольшее значение int (), равное 4294967295. Это означает, что пользователю будет предоставлена ​​практически неограниченная мощность (кредит).

1 Ответ

3 голосов
/ 17 февраля 2012

Прежде всего, не беспокойтесь об этом:

Я проверяю, достаточно ли у отправителя кредита для передачи с помощью запроса SELECT, прежде чем выполнять вышеупомянутый набор запросов.

Это оставляет вас открытыми для состояния гонки, с которым вам все равно придется иметь дело.Вместо этого установите ограничения в своей базе данных, чтобы сделать невозможным переход в недопустимое состояние.

Проблема 2 : мощность unsigned int.

Не делай этого тоже, в этом нет необходимости.4-байтовое целое число со знаком должно иметь более чем достаточно места на положительной стороне;если это не так, вы можете переключиться на 8-байтовое целое число со знаком.Причина, по которой вы хотите получить значение со знаком, заключается в том, что он делает проверку целостности довольно простой: если баланс падает ниже нуля, значит что-то не так.Если вы используете значение без знака, вам придется зарезервировать 4294967295-n (для некоторых n), чтобы обнаружить недостаточное и избыточное переполнение вместо простого < 0.

Что касается ограничения ваших данныхобычно вы используете ограничение CHECK следующим образом:

check (power >= 0)

, но MySQL не поддерживает ограничения CHECK.Однако вы можете написать триггер BEFORE INSERT OR UPDATE, который может проверить, что new.power >= 0 и вызывают исключение , если это не так.

Теперь у вас есть таблица users, которая нене допустим недопустимые значения power, поэтому мы закончили с выпуском 2.

Выпуск 1: транзакции.Да, вы действительно хотите использовать транзакцию для перевода.Вам понадобится такая последовательность:

  1. start transaction
  2. INSERT INTO power (sender, receiver, amount) VALUES ('$sender', '$receiver', '$amount')
  3. UPDATE users SET power=power-$amount WHERE user_id='$sender'
  4. UPDATE users SET power=power+$amount WHERE user_id='$receiver'
  5. Были ли какие-либо ошибки из "триггера проверки"?
    • Если есть, то отправьте rollback в базу данных и сообщите пользователю, что пошло не так.
    • Если нет, то отправьте commit в базу данных.

Транзакция гарантирует, что все три операции будут либо успешными, либо неудачными как единое целое, а ограничение CHECK (реализовано как триггер) гарантирует, что никто не может отдать больше энергии, чем ониесть.

...