本章主要内容
数据缓存
为何要使用缓存
哪类数据适合缓存
缓存的利与弊
如何保证缓存和数据库的一致性
不更新缓存,而是删除缓存
先操作缓存,还是先操作数据库
非要保证数据库和缓存数据强一致该怎么办
缓存和数据库一致性实战
实战:先删除缓存,再更新数据库
实战:先更新数据库,再删缓存
实战:缓存延时双删
实战:删除缓存重试机制
实战:读取binlog异步删除缓存
数据缓存
在我们实际的业务场景中,一定有很多需要做数据缓存的场景,比如说售卖商品的页面,包括了许多并发访问量很大的数据,它们可以称作是“热点”数据,这些数据有一个特点,就是更新频率低,读取频率高,这些数据应该尽量被缓存,从而减少请求打到数据库上的机会
为何要使用缓存
缓存是为了追求“快”而存在的。我们用代码举一个例子。
在Demo代码仓库中增减了两个查询库存的接口getStockByDB和getStockCache,分别表示从数据库和缓存查询某商品的库存量。
接口代码:
/**
* 查询库存:通过数据库查询库存
* @param sid
* @return
*/
@RequestMapping("/getStockByDB/{sid}")
@ResponseBody
public String getStockByDB(@PathVariable int sid) {
int count;
try {
count = stockService.getStockCountByDB(sid);
} catch (Exception e) {
LOGGER.error("查询库存失败:[{}]", e.getMessage());
return "查询库存失败";
}
LOGGER.info("商品Id: [{}] 剩余库存为: [{}]", sid, count);
return String.format("商品Id: %d 剩余库存为:%d", sid, count);
}
/**
* 查询库存:通过缓存查询库存
* 缓存命中:返回库存
* 缓存未命中:查询数据库写入缓存并返回
* @param sid
* @return
*/
@RequestMapping("/getStockByCache/{sid}")
@ResponseBody
public String getStockByCache(@PathVariable int sid) {
Integer count;
try {
count = stockService.getStockCountByCache(sid);
if (count == null) {
count = stockService.getStockCountByDB(sid);
LOGGER.info("缓存未命中,查询数据库,并写入缓存");
stockService.setStockCountToCache(sid, count);
}
} catch (Exception e) {
LOGGER.error("查询库存失败:[{}]", e.getMessage());
return "查询库存失败";
}
LOGGER.info("商品Id: [{}] 剩余库存为: [{}]", sid, count);
return String.format("商品Id: %d 剩余库存为:%d", sid, count);
}