MySQL秒杀优化

今天学习了楼方鑫先生《基于SQL的秒杀解决方案》,讲解了如何定位和优化秒杀业务中问题。
首先介绍了库存业务,库存可以分为前端库存,后端库存,实体库存。秒杀时,存在的主要问题

  • 库存数据不准确,下单、付款后,得知零库存;超卖或少卖
  • 废单较多,只下单不付款,转化率低
  • 热点商品,拖垮整个站

秒杀过程中,需要解决的技术点包括

  • 余额减一
  • 操作明细,方便追溯对账,防止一个帐号多次参与
  • 完整事务,保障记录明细与扣减库存同时完成
  • 数据落地,内存数据不可靠

针对库存技术要求,做了多个库存解决方案,比如Mysql + Read /Write Cache 。Read Cache方案不足是读有延迟影响用户体验;Write Cache方案存在多个APP写数据不一致性。Mysql + Cache + NoSQL方案则太复杂未实现。

于是又重新回到优化Mysql上。Mysql优势在于事务机制成熟,程序稳定。存在技术难点:单行并发,热点商品,瞬间压力,前一分钟,千万用户,容易堵塞,拖垮网站。于是从以下几个方面进行优化

  • 事务优化,单行更新
  • 并发优化,最大并发数
  • 排队优化,抢同一商品

分析秒杀时的处理逻辑,扫描系统代码,发现大部分程序都在等待确认Update记录数,才提交事务。

  • 开启事务
  • Insert库存明细
  • Update库存余额
  • 提交事务

在良好设计下,Mysql的Insert操作,不使用自增列是不会阻塞请求。但是Mysql的Update同一条记录是串行的,需要等远程客户端发送提交命令后才能释放锁,让其他会话继续。简单的更新操作,不考虑IO和锁冲突,一条语句执行的时间大约是0.1ms,一般条件下的网络时延为0.4-0.8ms,即等待事务提交通知的时间比真正SQL执行的时间长数倍。
于是扩展了SQL语法(OneSQL),指定在Update执行完后自动提交,不需要等待客户端发送提交命令,从而节约这一个网络来回的事务等待时间,提升网络性能。

秒杀时如果遇到大量请求需要进行排队,以免太多的请求拖垮Mysql

  • 在应用层排队的缺点,应用需要改造,使用统一框架(需要考虑跨语言),应用集群扩容时,控制不准确(连接数分配)
  • 在Mysql排队的优点,应用改造极少,只需修改少量SQL语句,无需统一框架,排队精确,发挥InnoDB性能。

于是开发了兼容Mysql的分布式数据访问层(OneProxy),为并发请求进行排队。

另外,还对热点商品进行独立数据库拆分和优化。目前,双十一前商品便已挂出,用户可以收藏或预购,对于商家而言可以准备更多商品;对于平台而言可以预先发现热点商品做优化。

总结,对于业务优化,需要循序渐进,深入了解业务逻辑和技术点,比较不同的解决方案,就算是平常的update操作也有优化空间;同时需要从其他方面进行特定优化,如高并发排队,热点数据分离等。

除了后端数据库优化,对于秒杀抽奖业务,问题的解决核心就是控制单位时间内的流量,使其不超过后端的处理能力。前端的做法包括

  • 分批次(少量多次)进行秒杀
  • 先玩游戏再抢购,如抽奖
  • 随机过滤掉部分请求,仅部分进入系统,如1/10
  • 阈值控制,一旦达到阈值,不再接收新请求
  • 预约排号,未排号用户返回失败(用户分类)
  • 验证码验证

另外,OneProxy 提供的连接池功能对于PHP非常有用。PHP运行在CGI下面,每一个请求到来便需要重新创建一个数据库连接与Mysql进行交互,并发量大量的情况下便会出现:too many connetion,乃至拖垮数据库:mysql server has gone away,影响其他业务。因此Mysql连接池,对于PHP显得非常重要。

更新:小米网在开发抢购系统的时候,最早使用PHP + Mysql碰到了一些问题,例如并发性能,数据一致性,在OneSQL上面都已经做了改进优化,只是小米自己使用Go语言重构,开发大秒系统(BigTap)。

参考链接:
限量秒杀等高并发活动的正确性如何保证?
MySQL 5.6.17/Percona5.6.16/MariaDB 10.0.11/OneSQL 5.6.16 TpmC测试
由12306.cn谈谈网站性能技术
“米粉节”背后的故事——小米网抢购系统开发实践
Web系统大规模并发——电商秒杀与抢购
OneProxy : 如何给PHP页面以及其他Ruby/Python/Go程序添加连接池功能?
基于Swoole实现的Mysql连接池

发表评论

电子邮件地址不会被公开。 必填项已用*标注