场景
假设我有一份要赠送的优惠券代码列表,我需要确保如果两个人同时下订单,他们不会得到相同的优惠券。
表格
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);
问题
我相信 order
表中 voucher_id
上的 UNIQUE KEY
会阻止两个订单具有相同的 voucher_id
,如果相同的凭证 ID 被插入两次,则给出错误/抛出异常。这将需要一个 while 循环来在失败时重试。
另一种方法是在 SELECT
之前读取并锁定凭证表,并在 INSERT
之后释放该锁定,以确保不会两次选择同一张凭证。
因此我的问题是:
- 哪个更快?
- PHP 代码中的 while 循环。
- 读取锁定凭证表。
- 还有别的办法吗?
编辑
ALTER TABLE
顺序CHANGE
voucher_idvoucher_id
BIGINT(20) UNSIGNED NOT NULL
如果 @voucher_id
为 null(根据需要,因为不会留下任何凭证),将导致 INSERT
失败。
最佳答案
“正确”,我的意思是完成您想要做的事情的最佳方式是在您下订单时生成凭证。查看 php 中 sha1() 函数的文档。您可以为它添加唯一信息以防止重复,并将其用于您的优惠券以及唯一 ID 的 auto_increment 字段。
下订单时,PHP 生成新的凭证,将其保存到数据库中,然后发送给用户。这样您就可以只存储有效的凭证,并且还可以防止创建重复凭证。
关于php - MySQL (InnoDB) 的交易和订单安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9537794/