高并发PHP礼品卡站点如何修改MySQL查询避免同卡重复售卖
问题修复方案
你遇到的同卡重复售卖问题根源是无锁SELECT在高并发场景下会被多个请求同时读到同一条未使用的礼品卡记录,后续操作自然会出现冲突。你需要按以下规则调整:
前置要求
将步骤1、2、3的所有操作包裹在同一个数据库事务中执行,事务隔离级别设置为*读已提交(READ COMMITTED)*即可,避免跨事务脏读。
步骤1查询语句修改
适配MySQL 8.0+ / MariaDB 10.6+及以上版本
直接在原查询末尾追加FOR UPDATE SKIP LOCKED,即可给查到的礼品卡加排他锁,同时跳过其他事务已经锁定的记录,完全避免重复读卡问题,性能损耗极低:
SELECT id, reference_number, card_number FROM cards WHERE category_id = 3 AND card_case = 'unused' ORDER BY id ASC LIMIT 1 FOR UPDATE SKIP LOCKED;
适配MySQL 5.x等不支持SKIP LOCKED的旧版本
如果你的数据库版本不支持SKIP LOCKED语法,可以调整执行逻辑,先做原子预占再查询:
- 先执行预占更新操作(原子操作无并发问题):
UPDATE cards SET card_case = 'used' WHERE category_id = 3 AND card_case = 'unused' ORDER BY id ASC LIMIT 1;
- 再查询当前事务刚预占的卡片信息:
SELECT id, reference_number, card_number FROM cards WHERE category_id = 3 AND card_case = 'used' ORDER BY id DESC LIMIT 1;
兜底优化
为了避免极端场景下的异常,建议在插入订单前增加一次卡状态校验:
SELECT count(*) FROM cards WHERE id = 你拿到的卡ID AND card_case = 'unused';
如果返回结果为0,直接终止流程返回库存不足即可。
另外建议给cards表的(category_id, card_case)字段加联合索引,大幅提升查询、更新的效率,避免高并发下出现锁表问题。
内容的提问来源于stack exchange,提问作者Ahmed Fawky
相关产品推荐
相关产品推荐

