在高并发下实现抢购秒杀功能中,我有一个疑问,就是数据入库的问题,什么时候入库。
设想思路:
1.判断他抢购成功了,立马把生成的订单数据写入mysql订单表,同时库存表字段减少1;
2.判断抢购成功后,把用户的user_id存入到redis的list列表里(比如:order,以user_id为值的列表)。然后再用crontab定时去一个一个插入到mysql订单表里,同时库存表字段减少1。
设想结果:
第一种思路,很好理解。简单的代码实现如下:
$num=10; //假设库存量
for($i=0;$i<$num;$i++)
\Redis::lpush('goods_store',1);//往goods_store列表中,
未抢购之前这里应该是默认push 10个1进去,当然里面的1没有实际意义
在抢购之前,上面的代码可以先执行,把商品入队。
抢购时间到了:(大量用户请求下面代码执行操作)
/* 模拟抢购操作,抢购前判断redis队列库存量 */
$count=\Redis::lpop('goods_store');//lpop是原子性的,可以保证不会出现超卖现象。
if(!$count)
return '已经抢光了';
/* 下面处理抢购成功后与mysql数据库的交互 */
1. //根据规则生成订单号(order_num),然后把相关的字段数据插入到订单列表里
$data['order_num'] = *****************;
$data['user_id'] = ***;
$data['goods_id'] = **;
.......
$res = DB::table('order')->insert($data);
2. //减少num库存字段
if($res)
DB::table('goods')->decrement('num', 1);,
上面的代码中,当用户抢购成功后,立马把相关的订单数据插入mysql订单表中,同时库存减少。现在我的疑问来了,要是用这种思路的话,大并发下,要是多个用户都同时进入到插入数据到订单列表和减少商品库存量这个过程中,是不是也会造成并发操作导致服务器压力瞬间过大,导致数据入库不正确呢,比如说存库少减了一个(还是说根据mysql增删改查的原子性,并不会造成这样的错误)?。
针对上面抢购成功后,立马把相关的订单数据插入mysql订单表中,同时库存减少,造成的数据库服务器压力过大的问题;
于是有了第二种思路,把用户的user_id存入到redis列表里(比如:order,以user_id为值的列表),在通过定时器crontab定时去从列表里一个一个取出user_id,生成相关的数据插入到mysql订单表里,同时库存表字段减少1。
代码实现跟上面差不多,
/* 模拟抢购操作,抢购前判断redis队列库存量 */
$count=\Redis::lpop('goods_store');//lpop是原子性的,可以保证不会出现超卖现象。
if(!$count)
return '已经抢光了';
/* 下面处理抢购成功后把user_id存入列表 */
\Redis::lpush('order',user_id);
通过定时器crontab定时去下面的代码
$user_id = \Redis::rpop('order',user_id);
1. //根据规则生成订单号(order_num),然后把相关的字段数据插入到订单列表里
$data['order_num'] = *****************;
$data['user_id'] = $user_id;
$data['goods_id'] = **;
.......
$res = DB::table('order')->insert($data);
2. //减少num库存字段
if($res)
DB::table('goods')->decrement('num', 1);,
第二种思路,我的疑问是,要是抢购成功后,先把user_id存入队列,再用定时器每隔一段时间去队列里取数据,然后生成相关的数据插入的mysql订单表里,同时减少库存。这样是可以减轻数据库服务器的压力了。但是我的抢购流程是这么设计的,用户抢购成功后,弹出//去支付按钮//进入订单列表页面(订单列表页数据是从mysql读取出来的),由于使用定时器去执行生成订单数据然后再插入到mysql数据库,这个过程肯定会有延迟,要是用户此时通过//去支付按钮//进入订单列表页面,发现订单列表还没有生成订单数据,那不是很悲催吗?
【这样设计抢购流程是否合理,是不是不用进入到订单列表,直接点击去支付,支付成功后,才生成订单数据插入到数据库呢】
以上就是我对两种思路存在的疑惑,希望可以得到专业人士的讲解,或者一起探讨。
你把你在 crontab 生成订单的逻辑写成一个 services,定期通过 crontab 触发,如果用户点了付款之后,首先先去 reids 查询有没有这个 key,有的话,通过 services 生成订单,并且移除掉这个 key,如果没有,有可能就是通过 crontab 已经入库.不然就是非法订单.