秒殺系統中,併發的瓶頸在數據庫,如何有效的減少對數據庫的訪問呢?最有效的方法就是加緩存,頁面靜態化、前後端分離、
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的訪問。