最近面试,关于商品秒杀的问题被问了多次,在这里记录一下个人的一些解决思路。
首先我们需要先了解下秒杀场景会出现什么问题
- 高并发,同一时间大量用户请求秒杀接口
- 超发,也就是有可能出现1个商品发给了2个用户,这个时候数据库记录的库存可能为负
围绕上面2个问题,我们可以通过下列的一些措施来避免:
-
前端处理,前端限制用户的点击次数。例如x秒时间内只能允许用户点一次,又或者点击秒杀按钮后页面显示抢购中,防止用户继续点击。
-
秒杀接口层,限制用户访问次数。接收到用户的请求后,可以把用户的uid,uuid,ip等作为key,访问次数作为value,保存在Redis或Memcached缓存中,每访问一次,增加一次访问次数。另外,给key设置一个过期时间,在key的生命周期内,如果访问次数达到了设定的限制值,则直接返回抢购中或其他提示信息。
-
逻辑处理层,把成功进入逻辑处理层的请求放入队列中(可用Redis做队列),然后每次从队列中取出一定数量的请求来处理,成功后继续取下一批来处理。如果库存为0了,则返回秒杀完毕。这里如果只有1个秒杀商品的话,也可以使用文件锁来实现,只有一个请求能获得文件锁资源,其他的请求直接就返回秒杀完毕。PHP中文件锁代码大致如下:
$fp = fopen('a.txt', 'r'); //a.txt是你创建的文件,里面什么都不用写,创建出来就行了
//获取文件锁成功,非阻塞,如果资源已被占用,请求直接到else操作
if(flock($fp, LOCK_EX | LOCK_NB)){
//TODO 你的逻辑处理
flock($fp, LOCK_UN); //释放文件锁
}else{
//TODO 获取失败
}
fclose($fp); //关闭文件资源
-
MySQL层,把库存字段设置为非负数,即unsigned类型
-
库存信息也可以保存一份到Redis等缓存中。每次请求进来后,先检查缓存中的库存数是否充足,然后再做后续的处理。
-
因为前端页面是需要显示商品信息的,所以,后端也可以把商品信息,如商品名称,图片,介绍这些缓存起来,不用每次都去查库,增加数据库的负担。
以上就是关于秒杀场景的一些解决措施,希望对你有所帮助。
网友评论