# secKill **Repository Path**: YY_MUYI/secKill ## Basic Information - **Project Name**: secKill - **Description**: 高并发秒杀系统 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-08-07 - **Last Updated**: 2022-08-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 秒杀系统 #### 介绍 搭建了简单的高并发秒杀系统示例,主要用于自己学习高并发场景下的优化思路。 #### 技术选型 前端使用Thymeleaf 后端框架采用SpringBoot搭建 集成MyBatis,Redis,RabbitMQ #### 适用高并发场景的性能优化 1.页面静态化 将thymeleaf页面替换为静态页面,通过ajax调用接口获取数据 优点:静态页面缓存在浏览器,仅需要传输变化的数据对象 2.预减库存 2.1 系统初始化时将库存加载到redis,通过redis减库存和判断,不走数据库。 2.2 内存标记:添加一个map<商品id为键,是否为库存已空>,秒杀时先判断map中对应商品id是否库存已空。库存不空则走Redis减库存,为空直接返回。 优点:先减少了对数据库的访问,之后又优化减少了商品售空后对Redis的访问。 3.异步下单 使用RabbitMQ,通过消息队列完成异步下单操作,秒杀信息发到RabbitMQ交换机后就给前端返回结果"排队中",之后交换机根据消息路由键转发到匹配的队列,监听对应队列的消费者实现库存校验和订单生成。后续客户端轮询验证下单状态再返回是否下单成功。 优点:通过消息队列异步操作可以更快速地提供反馈,同时也实现了流量削峰。 RabbitMQ交换机使用Topic模式,可以支持实际业务场景中更多样化的消息路由键匹配。 4.Redis分布式锁 setIfAbsent加锁,加锁成功进行业务操作,锁的值取随机UUID,业务操作结束执行lua脚本,锁的键和值都匹配才删锁。 Lua脚本可以在Redis服务端原子地执行多个Redis命令,可以有效解决网络延迟给Redis带来的性能问题,且避免了高并发时误删其他线程的锁的问题。 优化后服务流程: 系统初始化,把商品库存数预加载到redis。 收到秒杀请求时,redis预减库存,库存不足设置内存标记,之后直接返回。 库存充足,秒杀请求发送到RabbitMQ交换机,返回"排队中"。 交换机根据消息路由键转发消息到对应消息队列,队列内消息被消费者监听,消费时生成订单减少库存。 客户端轮询验证是否下单成功,返回成功信息。 #### 性能测试 使用JMeter进行压测 性能优化前后QPS对比: 测试配置:服务器4核8g 线程30000*10次 未优化:QPS 1457.3 预减库存优化后; QPS 2573.6 异步处理下单优化后: QPS 3453.4 #### 安全优化 1.接口地址隐藏 实现逻辑:点击秒杀按钮,后端根据用户生成一个随机UUID路径字符串path,用md5加密后存入redis再返回前端,前端拼接path后再传入秒杀接口,秒杀接口将路径中path与redis中path校验通过后才能继续秒杀操作。 作用:防止恶意脚本抢秒杀,提升接口安全性 2.验证码校验 实现逻辑:秒杀前需校验验证码。验证码生成后存入Redis,收到秒杀请求时先校验验证码,校验通过再进入秒杀流程。 作用:分散请求,降低峰值;防止恶意脚本自动获取接口路径。 3.接口限流 计数器算法:Redis增加一个计数器key记录用户请求次数,设置过期时间,用户在计数器失效前请求次数超过上限则不允许访问。计数器值原子递增,过期重置。 通用接口限流:用拦截器+自定义注解实现通用接口限流。 #### Bug解决 1. 解决超卖(订单多于库存数)bug 1.1 更新数据库再删缓存 1.2 添加索引,用户id+商品id联合索引且为UNIQUE。确保并发时一个商品只能有一个用户获取到。 1.3 从Redis缓存获取订单信息,判断用户是否已有该商品的订单。下单时订单信息会存入Redis。订单信息结构:用户id+商品id,确保同一用户不能多次获取同个商品。