点击关注上方“SQL数据库开发”,
设为“置顶或星标”,第一时间送达干货
1. 问题的来源
一定要高性能,不然还能叫秒杀吗?
要强一致性,库存只有100个,不能卖出去101个吧?但是库存10000实际只卖了9999是否允许呢?
既然这里说了是秒杀,那往往还会针对每个用户有购买数量的限制。
下文的所有解决方案是在 Mysql InnoDB 下做的。因为用到了很多数据库特性。其他的数据库或其他的数据库引擎会有不同的表现,请注意。
2.完全不考虑一致性的方案
2.1 表结构
2.2 方案
user
和deal
的关联表。谁买了多少就插入数据呗。buy_count
是否超过单人购买限制。select sum(buy_count) from UserDeal where deal_id = ?
select count(*) from UserDeal where user_id = ? and deal_id = ?
insert into UserDeal (user_id, deal_id, buy_count) values (?, ?, ?)
2.3存在的问题
如果库存只剩一个,两个用户同时点购买,两个人检查全部成功,最后,就超卖了。
如果一个用户同时发起两次请求,检测部分同样可能会同时通过,最后,数据就异常了。
3.保证单用户不会重复购买
alter table UserDeal add unique user_id_deal_id(user_id, deal_id)
4. 解决超卖问题
4.1 方案
select
语句没有使用for update
关键字,所以就算加入了事务也不会影响其他人读写。select
语句即可:select sum(buy_count) from UserDeal where deal_id = ? for update
4.2 优化
deal
也会相互影响呢?select
语句中的查询条件是where deal_id = ?
,你以为只会锁所有满足条件的数据对吧?alter table UserDeal add index ix_deal_id(deal_id)
05. 提高性能了
06. 鱼与熊掌不可兼得
6.1 优化的思路
6.2 秒杀可以容忍什么
7. 为了性能牺牲一致性的设计方案
7.1 去掉了事务会发生什么
for update
锁行就无效了,我们可以另辟蹊径,来解决这个问题。7.2 修改表结构
Deal
表,其实它就是存了一下基本信息,包括最大售卖量。sum(buy_count)
操作来得到已经卖掉的数量的,然后进行判断后再进行插入数据。Deal
表,把已经售卖的量也存放在Deal
表中,然后巧妙地把操作转换成一行update
语句。7.3 修改执行过程
update
语句来了:update Deal set buy_count = buy_count + 1 where id = ? and buy_count + 1 <= buy_max
buy_max
是1000,如果有2000个用户同时操作会发生什么?update
语句天然会有行锁,前1000个用户都会执行成功,返回生效行数1。而剩下的1000人不会报错,但是生效行数为0。update
语句的生效行数就知道是否抢购成功了。7.4 还没有结束
update
的生效行数是1,就代表购买成功。所以,如果一个用户购买成功了,那么就再去UserDeal
表中插入一下数据。Deal
表把刚才购买的还回去:update Deal set buy_count = buy_count - 1 where id = ? and buy_count - 1 >= 0
buy_count - 1 < 0
的情况,除非你实现的不对。执行成功
无库存
回滚成功
损失库存
8. 不要过度优化
本文作者:璀璨小二
我是岳哥,最后给大家分享我写的SQL两件套:《SQL基础知识第二版》和《SQL高级知识第二版》的PDF电子版。里面有各个语法的解释、大量的实例讲解和批注等等,非常通俗易懂,方便大家跟着一起来实操。
有需要的读者可以下载学习,在下面的公众号「数据前线」(非本号)后台回复关键字:SQL,就行
数据前线 ——End——
后台回复关键字:1024,获取一份精心整理的技术干货
后台回复关键字:进群,带你进入高手如云的交流群。
推荐阅读