目标:
1,实现系统的搭建
2,实现商品页的展示
3,实现防止库存超卖方案一,悲观锁方案
- 秒杀系统的特点分析
瞬间超高的访问量,QPS达到10万+ 商品数量有限,先到先得 10 100000 有明确的开始和结束时间 8:00 8:30 不能出现超卖 10 100
- 方案实现的关键点
1,独立部署,不能影响正常的业务运营(最关键) 2,尽快将请求控制在上游 3,秒杀系统确权,MQ(削峰),订单系统 4,防刷反爬虫 抢购成功,正在生成订单,请稍后.... 10 10000 10 99990 查数据库磁盘 redis内存 JMeter 吞吐量
- 创建秒杀系统,项目的结构

- 引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
- 数据库表新增一个库存字段
ALTER TABLE `t_product` ADD COLUMN `store` bigint NULL AFTER `update_user`;
- 配置MySQL数据库及Redis连接信息
server: port: 8080 spring: datasource: url: jdbc:mysql://192.168.77.128:3306/qf-v7 username: java password: java driver-class-name: com.mysql.jdbc.Driver redis: host: 192.168.77.128 password: java1807 mybatis: mapper-locations: classpath:/mapper/*.xml
- 实现商品详情页的开发
- 引入静态资源
- 引入商品详情页模板页面
- 开发后台Controller接口,跳转到商品详情页
- 创建mapper层及entity层,实现持久化层的开发,商品详情页的数据真实展示
- 访问接口:http://localhost:8888/product/getById?id=3
- 通过JMeter做压力测试,检测当前系统能扛住的并发量,创建CSV文件
- 关键参数说明
Average: 平均响应时间, 单位ms Min: 最小响应时间, 单位ms Max: 最大响应时间, 单位ms Error%: 事务错误率, 一般不高于1% Throughput: 吞吐量, 每秒完成的事务数, 也叫TPS, 也是性能测试很重要的指标, 越大越好, 越大说明系统的处理能力越强 Received KB/sec: 每秒从服务器端接收到的数据量 Sent KB/sec: 每秒向服务器发送的数据量 Avg. Bytes: 平均数据流量,单位是Byte
- 增加缓存,优化商品详情页的效率
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
在启动类,添加注解
@EnableCaching//开启声明式缓存,利用注解来控制缓存 public class MiaoshaApplication {
@Override //value和key构成最终的key //value作为前缀 //key product::1 @Cacheable(value = "product",key = "#id") public TProduct getById(Long id) { return productMapper.selectByPrimaryKey(id); }
清空缓存:FLUSHALL ,重新做压力测试,查看效果,
- 静态化
- 批量生成静态页面,然后将其部署到Nginx上面,观察结果
- 静态页面需要的动态数据
- 比如评论信息,这个需要通过Ajax发起请求获取到数据
- 不过对于秒杀页面,这块数据,我们不需要去获取,此块我们采用降级处理的
- 降级处理:保护核心业务(展示商品的基本信息),关掉非核心业务(评论)
- 带宽是一个要考虑的现实问题,压缩文件
解决超卖问题!!!
版本一:悲观锁解决秒杀库存超卖问题
- 演示数据库版的超卖问题
service @Override public boolean sale(Long id) { int store = productMapper.getStoreById(id); if(store > 0){ productMapper.updateStoreById(id); return true; } return false; } <select id="getStoreById" parameterType="java.lang.Long" resultType="java.lang.Integer"> select store from t_product where id=#{id,jdbcType=BIGINT} </select> <update id="updateStoreById" parameterType="java.lang.Long"> update t_product set store=store-1 where id=#{id,jdbcType=BIGINT} </update>
- 采用悲观锁解决超卖的问题
<select id="getStoreById" parameterType="java.lang.Long" resultType="java.lang.Integer"> select store from t_product where id=#{id,jdbcType=BIGINT} for UPDATE </select> @Override @Transactional public boolean sale(Long id) { int store = productMapper.getStoreById(id); if(store > 0){ productMapper.updateStoreById(id); return true; } return false; }
16,乐观锁
version,高并发的情况下会出现频繁冲突
重试机制
1,基于固定次数,3次
2,基于时间的方式,在一个固定时间段内,100ms