【性能优化】秒杀系统性能优化初体验
秒杀系统性能优化
优化之前的准备
在吞吐量、延迟和内存这三点中选择我们业务的着重点。
例如本次这个系统,我认为应该更加注重吞吐量。
为什么更加注重吞吐量?
延迟:像我们大家平时去进行商品抢购的时候,应该都会觉得现在人太多了,卡一卡没什么问题(但是也不能太久,你给我直接来个10s,肯定给你差评),只关心的是最后我有没有抢到商品。所以我认为延迟这方面应该是其次,主要的是系统处理请求的数量。
内存占用:一般秒杀活动都会有时间限制,这段时间也不会占用太大的内存。我电脑16G也不用担心,如果真的内存很缺少,可以考虑考虑。
所以在保证TP99不超过1s(抢购活动开始时,大量用户进入抢购页面)的情况下,最大的提高系统的吞吐量。
代码优化
将写入Redis缓存代码与数据库事务分开
防止网络抖动可能导致写缓存响应时间较慢,阻塞数据库事务。
原始代码:
/**
* 下订单、减库存事务(原始代码)
*/
@Transactional
public void seckillTransaction(SeckillDto seckillDto, GoodDto goodDto){
//添加订单操作......
//减库存操作......
//写入缓存,不用再调用数据库判断是否还能继续抢购
if (goodDto.getSurplusCount() <= 0){
redisTemplate.delete(seckillDto.getGoodId().toString());
redisTemplate.opsForValue().set(seckillDto.getGoodId().toString(), JSONObject.toJSONString(goodDto), 320, TimeUnit.SECONDS);
}
}
修改后:
public boolean seckillGood(SeckillDto seckillDto) {
// ......
if (goodDto.getSurplusCount()>0){
//执行下单 、 减库存事务
seckillTransaction(seckillDto, goodDto);
//写入缓存,不用再调用数据库判断是否还能继续抢购
if (goodDto.getSurplusCount() - 1 <= 0){
redisTemplate.delete(seckillDto.getGoodId().toString());
redisTemplate.opsForValue().set(seckillDto.getGoodId().toString(), JSONObject.toJSONString(goodDto), 320, TimeUnit.SECONDS);
}
}
// ......
}
/**
* 下订单、减库存事务(修改后)
*/
@Transactional
public void seckillTransaction(SeckillDto seckillDto, GoodDto goodDto){
log.info("seckillTransaction已调用");
//添加订单操作......
//减库存操作......
}
数据库相关优化
因为没弄很多数据,SQL语句也挺简单的,就不考虑那么多了。重点考虑连接池的参数设置。
连接池优化
我的电脑CPU是8核的。因为数据库连接算IO操作,所以我们将连接数在16左右进行调节。
使用的是dbcp2连接池:
max-wait-millis:最大等待时间
initial-size:连接池启动时创建的初始化连接数量
max-total:最大连接数
min-idle:最小空闲连接
max-idle:最大空闲连接
场景:100个请求同时对数据库进行抢购(下订单、减库存)操作,10个商品。开启了事务。
默认情况下,不配置连接参数。initialSize = 0,minIdle = 0。
连接数为16。initialSize = 16,minIdle = 16。
连接数为10。initialSize = 10,minIdle = 10。
连接数12。initialSize = 12,minIdle = 12。
连接数13。initialSize = 13,minIdle = 13。
TP95 | TP99 | 吞吐量 | |
---|---|---|---|
默认 | 2671 | 2694 | 104.2 |
连接数12 | 2408 | 2431 | 114.5 |
上面的数据可以测试数据可以看出:配置了连接数之后整体操作TP99下降了260ms,吞吐量也增加了10/s。
缓存
缓存这方面其实挺多的,有前端缓存、代理服务器缓存、分布式缓存、服务端缓存等。引入缓存之后还得考虑缓存一致性、缓存穿透等问题。
因为本文只是考虑提高系统的吞吐量、延迟方面,就不仔细的考虑那么多。主要从代理服务器缓存和分布式缓存入手。
代理服务器缓存
主要是将图片等静态资源放到Nginx的静态资源下(边缘缓存),就像CDN。
分布式缓存
场景:1000个同时请求,连续请求一分钟
未加分布式缓存:
Redis:
对比:以上数据都是在每种情况下测了至少3次取的最优的。
最小值 | 最大值 | TP95 | TP99 | 吞吐量 | |
---|---|---|---|---|---|
未加分布式缓存 | 5 | 1231 | 379 | 481 | 2368.1 |
Redis | 5 | 1195 | 281 | 455 | 3030 |
上面数据可以看出,使用Redis做分布式缓存时,TP95下降了100ms,TP99下降了30ms,吞吐量更是有很大的提升,整整提高了600/s多!