关于秒杀一般是如何保证库存操作的原子性的

2019-11-27 22:45:42 +08:00
 yejianmail
需要 java 做一个用户不太多的秒杀,springboot 加 mariadb,涉及库存操作的地方已加锁同步,事务隔离级别为 read-commit,偶尔出现剩余库存为-1,初步认为是数据库出现了幻读,这样的抢单一般是用什么技术实现的,如果并发不高,是不是也必须上 redis,有大佬指导下么?
5168 次点击
所在节点    程序员
37 条回复
jeffh
2019-11-27 23:57:09 +08:00
我记得在网上看过,淘宝的秒杀是异步的,先在内存中设置一个总量 v,秒杀到的显示排队中 mq 削峰异步处理,同时 v-1,如果 v 小于 0 了,直接返回秒杀结束。
hhx
2019-11-28 00:04:43 +08:00
@yejianmail 我想知道你是怎么用的
h123123h
2019-11-28 00:07:34 +08:00
对速度不敏感的话事务+乐观锁就可以了吧
ljpCN
2019-11-28 00:51:42 +08:00
消费者队列
wangyzj
2019-11-28 00:58:29 +08:00
select for update
redis nx
queue 同时保存状态+轮询获取新状态
imcj
2019-11-28 00:59:48 +08:00
如果没有范围读取,read commit 足够,要么设置为 repeatable-read,要么修改代码,避免范围读取。

当然,事务的开启和提交是否都正确?嵌套是否是否存在?
imcj
2019-11-28 01:00:07 +08:00
如果没有范围读取,read commit 足够,要么设置为 repeatable-read,要么修改代码,避免范围读取。

当然,事务的开启和提交是否都正确?嵌套事务是否存在?
yc8332
2019-11-28 08:09:16 +08:00
用户不多直接数据库加锁,或者更新的时候校验数据库的库存,和你判断时的值不一样就失败
willm
2019-11-28 09:15:02 +08:00
@renmu 你一定是小米员工
markgor
2019-11-28 12:14:48 +08:00
一个用户不太多的秒杀 還能出現-1 的情況....


我通常偷懶的做法

start transaction;
select 1 from item where id = 123 and less > 1;沒記錄就返回失敗。
update item set less = less -1 where less > 1 and id = 123;影響條數=0 返回失敗
commit;

還未出現過超售。對了 item 的 less 是不允許負數的。

另外也試過 redis 預熱,
把獎品加進去 redis,
然後成功 pop 出來再去 mysql 扣減。
markgor
2019-11-28 12:15:42 +08:00
@markgor #30 select 1 from item where id = 123 and less > 0;沒記錄就返回失敗
update item set less = less -1 where less > 0 and id = 123;影響條數=0 返回失敗

臨時寫的,上面寫錯了,這更改回來。
shenyuzhi
2019-11-28 14:45:17 +08:00
可以先收集请求,然后抽奖。
比如你预计 3 秒钟秒完,就收集前 5 秒的请求,只记录不处理,然后抽奖。秒杀本来就看运气,用户又不知道你怎么实现的。
yejianmail
2019-11-28 18:22:35 +08:00
@markgor 我觉得你这个方法可行,毕竟项目用户不多,不用整得太复杂
markgor
2019-11-28 18:36:22 +08:00
@yejianmail 你可以做個頁面,一有請求就減庫存,按照我上面那個方法,然後 ab 測試一下,看看會不會出現超售。
反正超售是肯定可以保證,但是由於執行流程是一個個執行,所以後面的並發會卡在那,此時前端 ajax 設置個超時重試參數。
另外流量真的大,你直接後台按百分比來放行,比方隨機 1~100,只要是 50 以上的放行進行搶購,50 以下的直接返回失敗。
markgor
2019-11-28 18:40:41 +08:00
你可以參考下:
前端靜態,丟 CDN。
兩個前端頁面,一個是發無效請求去百度,一個是發有效請求到 java
然後有效請求的那個頁面,發送 ajax 去 java 第一輪按百分比返回結果,
50%以上執行上面搶購邏輯。50%的就直接返回失敗。

當然,這是很低莊的方法,但一定程度內有效分流,也能保證到超售情況。
yejianmail
2019-11-28 20:23:52 +08:00
@markgor 谢谢大佬,我测试下,ab 没用过,我用 jmeter 试试
crclz
2019-12-01 19:13:50 +08:00
库存为 -1,应该是没有在库存那条记录上面加锁,而不是幻读。
另外 ReadCommited 足够。

如果数据高争用,那么就应该用 redis。但是 redis 是内存数据库,Durability 没法保证,所以就 2 台以上 redis 吧。redis 写个 lua 脚本,最小化往返次数。
(没实际用过 redis,只是提供一个思路)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/623770

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX