1、秒杀场景
应对策略:
1)秒杀系统独立部署
2)秒杀商品页面静态化
3)租借秒杀活动带宽(将秒杀页面缓存到CDN)
4)动态生成下单URL,防止用户直接访问下单页面URL
5)按钮置灰,5秒内不可重复点击
6)集群中的每台服务器都只接受固定数量请求,其他直接响应秒杀活动结束
7)友好设计:所有异常都应捕获,即使服务器瘫痪,应返回“秒杀活动结束”
Mysql层面优化,丁奇:
测试显示,在6线程时,TPS达到顶峰1.9W,随着线程数增加TPS急剧下降
问题:当线程并发时,Innodb内部存在太多线程
1)关掉死锁检测,正常的业务死锁会变成超时,治标不治本,效果微弱
2)修改源码:将排队队列提到进入引擎层之前(放到server层),随着线程数增加TPS稳定在1.5W
3)优化排队,组提交策略,TPS提升稳定至8.5W
如何防止超卖
以下3种办法:
1、将提交操作变成两段式,先申请后确认。
1.1利用Redis的原子自增操作(相比较MySQL的自增来说没有空洞),同时利用Redis的事务特性来发号,
保证拿到小于等于库存阀值的号的人都可以成功提交订单。
1.2数据异步更新到DB中。
优点:解决超卖问题,库存读写都在内存中,故同时解决性能问题。
缺点:由于异步写入DB,可能存在数据不一致。另可能存在少买,也就是如果拿到号的人不真正下订单,可能库存减为0,但是订单数并没有达到库存阀值。
2、数据库层面
2.1设计2张表:
第一张:判重表(buy_record),该用户有没秒杀过该商品
字段: id, uid, goods_id, addtime
第二张表:商品表 goods
字段: goods_id goods_num
2.2对(uid,goods_id)加唯一索引!
start transaction;
insert into buy_record。。。
if(唯一索引报错?)
抛异常,已经秒过了,回滚。。。
update goods set goods_num=goods_num-1 where goods_id=$goods_id and goods_num>0 ;
if(受影响行数<=0)
抛异常,商品秒完了,回滚。。。
该方法完美的解决了超卖与select排它锁导致的并发低的问题。极大提升性能
3、Mysql优化,改源码
丁奇在DTCC2013上分享的《秒杀场景下MySQL的低效》
1)关掉死锁检测,正常的业务死锁会变成超时,治标不治本,效果微弱
2)修改源码:将排队队列提到进入引擎层之前(放到server层),随着线程数增加TPS稳定在1.5W
3)优化排队,组提交策略,TPS提升稳定至8.5W
网友评论