秒杀系统中,并发的瓶颈在数据库,如何有效的减少对数据库的访问呢?最有效的方法就是加缓存,页面静态化、前后端分离、
1.页面缓存
什么是页面缓存呢,就是我们在访问页面时,不是直接让系统帮我们渲染页面,而是去缓存中去取,如果找到了就直接返回给客户端,如果没有我们就手动渲染模板,返回给客户端的同时,将结果缓存到如redis之类的缓存中,以便下次使用。这里我们使用Redis来缓存。
以前,我们直接将动态数据存入model中,然后返回HTML页面,通过SpringMVC来帮我们渲染
@RequestMapping(value="/to_list")
public String list( Model model,User user) {
model.addAttribute("user", user);
List<GoodsVo> goodsList = goodsService.listGoodsVo();
model.addAttribute("goodsList", goodsList);
return "goods_list";
}
缓存就是,我们直接返回HTML的源代码,将HTML源代码存在redis中,通过手动渲染来返回页面。 (这里的模板语言使用的是Thymeleaf)
@RequestMapping(value="/to_list", produces="text/html")
@ResponseBody
public String list(HttpServletRequest request, HttpServletResponse response, Model model,MiaoshaUser user) {
model.addAttribute("user", user);
List<GoodsVo> goodsList = goodsService.listGoodsVo();
model.addAttribute("goodsList", goodsList);
//1.取缓存,看缓存中是否存在以有的缓存
String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
if(!StringUtils.isEmpty(html)) {
return html;
}
//2.如果是空的,我们就手动渲染
//通过SpringWebContext取出动态数据
SpringWebContext ctx = new SpringWebContext(request,response,
request.getServletContext(),request.getLocale(), model.asMap(), applicationContext );
//手动用thymeleaf的模板引擎来渲染
html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);
//存入redis中
if(!StringUtils.isEmpty(html)) {
redisService.set(GoodsKey.getGoodsList, "", html);
}
return html;
}
但是你需要控制好缓存的有效时间,来保证页面的及时性。我这里设置的是60秒,对于大多数人来说,看到的页面是60秒之前的并无大碍。也可以设置更短的时间来保证更高的及时性。
2.对象缓存
对象缓存是更细粒度的缓存。
我们还是拿使用缓存之前和使用缓存之后的两个代码块来进行比较,下面是一个普通的通过userId从数据库中取用户。
public User getById(long id) {
return userDao.getById(id);
}
为了减少对数据库的读写,我们可以加一个缓存,先查看缓存中是否存在user信息,没有再去数据库中读取。
public User getById(long id) {
//1.取缓存 现在redis中看是否存在该user 没有的话就去数据库中取
MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
if(user != null) {
return user;
}
//2.取数据库 从数据库中取出后,记得要加到缓存中去
user = miaoshaUserDao.getById(id);
if(user != null) {
redisService.set(MiaoshaUserKey.getById, ""+id, user);
}
return user;
}
如果我们需要修改用户信息的话,也要修改缓存中对应的用户信息。
public boolean updatePassword(String token, long id, String formPass) {
//取user
User user = getById(id);
if(user == null) {
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
//更新数据库
User toBeUpdate = new User();
toBeUpdate.setId(id);
toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
userDao.updateUser(toBeUpdate);
//处理缓存
redisService.delete(UserKey.getById, ""+id);
user.setPassword(toBeUpdate.getPassword());
redisService.set(UserKey.token, token, user);
return true;
}
将页面和对象加了缓存之后,QPS从1267翻倍到 2884. 可见网站的性能翻了一倍,就因为减少了对MySQL的访问。