php - MySQL (InnoDB) 的交易和订单安全

标签 php mysql innodb

场景

假设我有一份要赠送的优惠券代码列表,我需要确保如果两个人同时下订单,他们不会得到相同的优惠券。

表格

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顺序CHANGEvoucher_idvoucher_idBIGINT(20) UNSIGNED NOT NULL 如果 @voucher_id 为 null(根据需要,因为不会留下任何凭证),将导致 INSERT 失败。

最佳答案

“正确”,我的意思是完成您想要做的事情的最佳方式是在您下订单时生成凭证。查看 php 中 sha1() 函数的文档。您可以为它添加唯一信息以防止重复,并将其用于您的优惠券以及唯一 ID 的 auto_increment 字段。

下订单时,PHP 生成新的凭证,将其保存到数据库中,然后发送给用户。这样您就可以只存储有效的凭证,并且还可以防止创建重复凭证。

关于php - MySQL (InnoDB) 的交易和订单安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9537794/

相关文章:

mysql - Prestashop 的 MyISAM 或 InnoDB 数据库?

MySQL InnoDB 复合索引选择性和基数对性能的影响

php - 在 php 中获取 DATETIME 并将其发布到 MySQL 以实现事务一致性

php通过邮件登录mysql

php - 用mysql填充的多动态组合框

php - 如何在 Laravel 查询中使用多个 OR、AND 条件

php - PHP/MySQL自动对付垃圾邮件发送者有哪些方法

php - Codeigniter 连接过多 __construct()

php - 保存可见的用户日志

php - 将字符串编码为字符代码