页面级优化
商品列表页缓存实现
热点对象缓存
商品详情页静态化
解决卖超问题
商品列表页缓存实现
实现思路
先查看redis中是否有缓存,如果有,直接返回html页面对象,没有手动渲染模板,创建缓存,并返回html对象
@RequestMapping(value = "/to_list",produces = "text/html")
@ResponseBody
public String toList(HttpServletRequest request, HttpServletResponse response, Model model, MiaoShaUser user){
model.addAttribute("user",user);
//在有缓存的情况下,取出缓存
String html = redisService.get(GoodsKey.goodsKeyPrefix, "", String.class);
if(! StringUtils.isEmpty(html)) return html;
//在没有缓存的时候,手动渲染,添加缓存
List<GoodsVo> goodsVos = goodsService.listGoodsVo();
model.addAttribute("goodsList",goodsVos);
IWebContext ctx = new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());
html = thymeleafViewResolver.getTemplateEngine().process("goods_list",ctx);//这里需要注入IContext
if(!StringUtils.isEmpty(html)){
redisService.set(GoodsKey.goodsKeyPrefix,"",html);
}
return html;
//return "goods_list";
}
1.produces = “text/html” 标注了返回值的类型,这里因为不再返回一个页面而是一个对象,所以要使用@Responsebody注解
2.渲染页面我们使用了ThymeleafViewResolver,这是框架准备好的,这里需要注入icontext
IWebContext ctx = new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());
3.这里缓存过期时间设置的是60秒,即60秒内这个页面的信息不会发生变化
流程(转载自https://blog.csdn.net/qq_46225886/article/details/107340734)
对象缓存
之前我们做的登陆页面的token,取出的token直接对应一个用户对象,这就是一个对象级缓存
public MiaoShaUser getById(long id){
//先从缓存中取
MiaoShaUser user = redisService.get(MiaoShaUserKey.idPrefix, "" + id, MiaoShaUser.class);
if(user != null) return user;
//缓存中没有,从数据库中取,并且把它添加到缓存中
user = miaoShaUserDao.getById(id);
if(user != null) redisService.set(MiaoShaUserKey.idPrefix,"" + id,user);
return user;
}
更新缓存
修改密码场景,先修改数据库,再修改缓存(先更新token-user,再删除id-user)
public boolean updatePassword(long id,String formPass,String token){
//取出user
MiaoShaUser user = getById(id);
//没有这个用户
if(user == null) throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
//修改密码,更新数据库
user.setPassword(MD5Util.formPassToDBPass(formPass,user.getSalt()));
miaoShaUserDao.update(user);
//更新缓存,token-user缓存(登陆用的)这个不能删除,id-user缓存删除
redisService.set(MiaoShaUserKey.getTokenPrefix,token,user);
redisService.delete(MiaoShaUserKey.idPrefix,id);
return true;
}
页面静态化
通常不使用第一种缓存方法,而是通过静态化处理,比较常用的技术有Vue。通过静态化处理,我们将页面缓存在客户端浏览器中,不需要与服务器交互就能访问到页面。
@RequestMapping(value = "/detail/{goodsId}")
@ResponseBody
public Result<GoodsDetailVo> toDetail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoShaUser user, @PathVariable("goodsId") long goodsId){
GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
//秒杀开始、结束时间,当前时间
long startDate = goodsVo.getStartDate().getTime();
long endDate = goodsVo.getEndDate().getTime();
long now = System.currentTimeMillis();
//秒杀状态,0为没开始,1为正在进行,2为秒杀已经结束
int miaoshaStatus = 0;
//距离秒杀剩余的时间
int remainSeconds = 0;
if(now < startDate){
//秒杀没开始,进行倒计时
remainSeconds = (int) (startDate - now) / 1000;
}else if(now > endDate){
//秒杀已经结束
miaoshaStatus = 2;
remainSeconds = -1;
}else {
//秒杀进行时
remainSeconds = 0;
miaoshaStatus = 1;
}
GoodsDetailVo goodsDetailVo = new GoodsDetailVo();
goodsDetailVo.setGoods(goodsVo);
goodsDetailVo.setUser(user);
goodsDetailVo.setMiaoshaStatus(miaoshaStatus);
goodsDetailVo.setRemainSeconds(remainSeconds);
return Result.success(goodsDetailVo);
}
配置
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
超卖问题
1.在更新库存的sql语句中加一个库存的判断
@Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0")
public int reduceStock(MiaoshaGoods g);
2.避免一个用户秒杀多次,在数据库添加唯一索引,绑定user_id和goods_id,这样同一个用户对同一个商品的秒杀订单是唯一的