高并发秒杀系统实践

高并发场景

最近在慕课网上发现了一个高并发秒杀系统课程,学习了一下,对最后一章节里的场景分析和优化学习过程做了一个记录,分享一下,感觉这个老师讲的挺好的。

以一个秒杀场景为例,秒杀功能包括:

秒杀接口暴露(不到秒杀时间不允许用户访问)
执行秒杀
相关查询

高并发具体业务(秒杀)场景流程

红色部分可能会发生并发操作,绿色部分不会有并发操作

在这里插入图片描述

下面具体分析并发场景各个节点的优化

1.详情页

在这里插入图片描述

解决方案

  • 提供一个CDN节点,把静态资源(html ,JavaScript等)存储上去,实际上在并发操作过来的时候,用户访问的不再是服务器系统上的资源,而是CDN上的资源,这就避免了对服务器造成压力
  • 拿系统时间还是需要去服务器秒杀系统上拿

什么是CDN?

  • CDN 内容分发网络,加速用户获取数据的系统
  • 部署在离用户最近的网络节点上(通过运营商接口访问城域网最近的节点)
  • 命中CDN就不需要访问后端服务器了,大互联网公司都有自己的CDN集群,或者租用CDN集群

2.为什么访问内存时间不用优化?

访问一次内存只需要10ns,很快

3.秒杀地址接口优化

难点

  • 无法使用CDN缓存(只在一定时间内有效)
  • 适合存在服务器端中的redis中,可以抗很高的QPS(十万百万级)且一致性维护成本低

解决方案

在这里插入图片描述

这里要注意双写一致性问题

  • 双写一致性问题
    • 先更新数据库,再更新缓存
      • 更新数据库的操作顺序到更新缓存时顺序不一定一致,会造成脏数据
      • 写请求很多,读请求很少,频繁更新缓存却没发挥缓存的效果
    • 先更新数据库,再删除缓存
      • 缓存删除失败时,其他用户读到的仍然是旧的缓存数据
    • 先删除缓存,再更新数据库
      • 删除缓存失败,但更新数据库成功,会造成脏数据
    • 都无法保证任何场景下数据库与缓存数据完全一致

  • 主流方法基本上都采用第二或第三种,或者用以下解决方案
  • 解决方案
    • 将数据库缓存更新和读取操作进行串行化
    • 项目里维护一组线程池和内存队列
    • 更新数据时,根据数据唯一标志将请求路由到一个jvm队列中,然后更新,请求结束
    • 读数据时,先查缓存,数据不存在,根据唯一标志路由,发送到同一jvm队列中,重新读取后更新缓存
    • 由于都在同一队列中,执行顺序具有先后性

4.秒杀操作优化

难点

  • 无法使用CDN缓存
  • 后端缓存困难:库存问题(不同用户访问redis同时命中,会产生不一致)
  • 一行数据竞争:热点商品

解决方案

方案一

在这里插入图片描述

  • 原子计数器:redis实现,记录商品的库存,减库存操作保证原子性
  • 记录行为消息:谁减了库存?生产此条消息放入分布式消息队列中
  • 消费消息并落地:消费消息记录到MySQL数据库中
痛点

运维成本太高,都依赖于分布式的NoSQL和MQ

开发成本太高,数据一致性不好保证,回滚方案设计复杂

扫描二维码关注公众号,回复: 11209555 查看本文章

幂等性难保证,重复秒杀问题

为什么不能直接用MySQL?

因为低效率

一条update压力测试结果: 4W QPS,一秒种商品可以更新4W次,那为什么效率还低?

在这里插入图片描述

  • update insert 等操作会有网络延迟和GC操作(由于Java的特性)
  • 事务阻塞十分严重

优化方向:

减少行级锁持有时间

把客户端逻辑放到MySQL服务端,避免网络延迟和GC影响(比如判断是否更新成功)

在这里插入图片描述

总结

在这里插入图片描述

附上慕课网课程地址:
https://www.imooc.com/learn/632

原创文章 40 获赞 16 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43925277/article/details/105105768