Безопасность транзакций и заказов с MySQL (InnoDB) - PullRequest
1 голос
/ 02 марта 2012

Сценарий

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

Таблица

CREATE TABLE IF NOT EXISTS `order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `voucher_id` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `voucher_id` (`voucher_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `order` ADD CONSTRAINT `order_fk` FOREIGN KEY (`voucher_id`) REFERENCES `voucher` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

CREATE TABLE IF NOT EXISTS `voucher` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `code` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Пример данных

INSERT INTO `voucher` (`code`) VALUES ('A'), ('B'), ('C');

Пример запроса

SELECT @voucher_id := v.id FROM `voucher` v LEFT JOIN `order` o ON o.voucher_id = v.id WHERE o.id IS NULL;
INSERT INTO `order` (`voucher_id`) VALUES (@voucher_id);

Вопрос

Я полагаю, что UNIQUE KEY на voucher_id в таблице order предотвратит два ордера с одинаковым voucher_id, что приведет к ошибке / выдаче исключения, если один и тот же идентификатор ваучера вставлен дважды. Для этого потребуется цикл while, чтобы повторить попытку после сбоя. Альтернативой является чтение блокировки таблицы ваучеров перед SELECT и снятие блокировки после INSERT, гарантируя, что один и тот же ваучер не будет выбран дважды.

Поэтому мой вопрос:

  • Что быстрее?
    • Цикл while в коде PHP.
    • Чтение блокировки таблицы ваучеров.
  • Есть ли другой способ?

Редактирование

ALTER TABLE заказ CHANGE voucher_id voucher_id BIGINT(20) UNSIGNED NOT NULL приведет к сбою INSERT, если @voucher_id равно нулю (по желанию, поскольку ваучеров не останется).

Ответы [ 2 ]

0 голосов
/ 02 марта 2012

«Правильный» и под этим я подразумеваю, что лучший способ сделать то, что вы хотите сделать, это сгенерировать ваучер во время размещения заказа.Посмотрите документацию по функции sha1 () в php.Вы можете заполнить ее уникальной информацией, чтобы предотвратить дублирование, и использовать ее для своего ваучера вместе с полем auto_increment для уникального идентификатора.

Когда заказ размещен, PHP генерирует новый ваучер, сохраняет его в базе данных,и отправляет его пользователю.Таким образом, вы сохраняете только действительные ваучеры и не допускаете создания дубликатов.

0 голосов
/ 02 марта 2012

Вы можете использовать START TRANSACTION, COMMIT и ROLLBACK, чтобы предотвратить состояние гонки в вашем SQL. http://dev.mysql.com/doc/refman/4.1/en/commit.html

В вашем случае я бы просто поместил ваши транзакции в критическую область, ограниченную этими токенами.

...