商城秒杀功能总结
秒杀系统的场景特点高并发、请求数量远远大于库存数量、下订单减库存。秒杀架构设计理念限流、削峰、异步处理、缓存、可拓展。实现方案限流、采用消息队列缓存、利用缓存应对读写请求。使用技术前端小程序后端Spring Cloud、Mybatis中间件RabbitMQ、Redis关键技术1、无状态登录实现分布式session好处不依赖于服务端信息用户信息只存在于user模块、减小服务端存储压力、服务端可以任意迁移伸缩。流程登录时对用户信息进行认证-认证通过后将用户信息加密形成token返回客户端-每次请求都携带该token-服务对token进行解密。技术实现JWTRSA非对称加密JWTJson Web Token是JSON风格轻量级的授权和身份认证规范。JWT包含三部分Headertoken类型最基础是JWT、加密算法Payload有效数据、SignatureRSA256(Base64(Header)Base64(Payload)私钥用于校验信息是否被篡改。RSA256非对称加密。私钥加密时持有私钥或公钥解密公钥加密持有私钥才可解密。为什么要使用RSA256如果使用非可逆加密由于安全性私钥只会存在鉴权中心因此所有请求都要访问鉴权中心导致鉴权中心压力过大。私钥只存在于鉴权模块中公钥存在于其他所有模块所有模块都可以通过公钥解析用户身份信息。2、Redis预减库存减少数据库访问内存标记减少Redis访问读取策略首先尝试从缓存读取读取到数据则直接返回如果读不到就读数据库并将数据库写回到缓存并返回。更新策略先更新数据库然后把缓存对应的数据删除防止频繁更新懒加载的思想。如果先删除缓存再更新数据库如果有A、B线程同时更新这时C线程在AB更新之间读取会把A更新的数据写入缓存导致缓存、数据库不一致如果不删除缓存A、B线程更新数据库B后更新的数据库却先更新了缓存也会导致缓存、数据库不一致。但是如果redis删除失败也会存在缓存不一致问题更好的做法是对相同的商品id做hash将对于商品的操作路由到某一个内存队列中以此来保证多线程环境下对同一条记录读写并发的问题其中可以对多个更新缓存请求合并并且设置超时时长防止频繁更新导致大量读请求积压但一般来说更新频率不会这么高。当读请求并发量很高时可以扩容。但是缺点是串行化降低系统吞吐量。3、使用消息队列完成异步下单秒杀逻辑1把商品库存加载到Redis2后端受到秒杀请求Redis预减库存如果库存不足直接返回失败3库存充足且无重复秒杀则将秒杀请求入队返回排队中4后端消费者监听秒杀消息通道在此判断库存、判断是否重复秒杀然后执行秒杀事务。5秒杀成功则返回商品ID-1秒杀失败0表示排队中。4、安全限制1秒杀接口地址隐藏秒杀请求发起之前先去获取秒杀地址-生成随机MD5字符串存入redis并设置有效期60s)-前端携带该参数进行秒杀后端验证该参数2数学公式验证码ScriptEngineManager eval生成结果存入redis3接口限流防刷使用用户ID和商品ID拼接key用key记录用户访问次数有效期1分钟并设定访问限制次数。项目细节问题解决1、为防止缓存穿透对不存在的数据缓存空数据2、秒杀开始前进行缓存预热并使用keepalived建立主从热备3、对于缓存中库存已清零采用内存标记减少redis服务器的压力4、在秒杀订单表中使用商品Id和goodsId建立唯一索引防止重复下单5、在减库存sql中增加stock_count0判断防止卖超6、设置定时任务轮询超过30分钟未支付的订单将其关闭并回复库存。压力测试使用JMeter测试5000个线程*10次优化前后QPS从1000多提升到2700多。改进方法nginx横向扩展、分布式缓存、分布式数据库。