緩存
一 緩存基礎(chǔ)
1 緩存的概念和作用
緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache),是存貯數(shù)據(jù)的臨時地方,一般讀寫性能較高
2 緩存的使用
之前沒有使用緩存是的模型
3 項目說明
當我們查詢商家信息的時候,直接從mysql中獲取的。現(xiàn)在我們將原來的項目改造。改造地方在ShopController,我們按照流程圖去做,添加redis緩存,業(yè)務(wù)都是在service中實現(xiàn)的。
# 具體實現(xiàn)流程
1 redis中查詢商戶緩存
2 判斷是否存在
3 存在直接返回
4 不存在根據(jù)id去數(shù)據(jù)庫查詢
5 數(shù)據(jù)庫也不存在,返回錯誤
6 存在則寫入redis中
7 返回
<dependency>
? ?<groupId>com.alibaba</groupId>
? ?<artifactId>fastjson</artifactId>
? ?<version>1.2.47</version>
</dependency>
1 修改controller
/**
? ?* 根據(jù)id查詢商鋪信息
? ? * @param id 商鋪id
? ? * @return 商鋪詳情數(shù)據(jù)
*/
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {
? ?return shopService.queryById(id);
? ?// ? ? ? return Result.ok(shopService.getById(id));
}
2 修改service
@Resource
private RedisTemplate<String,Object> redisTemplate;
?
@Override
public Result queryById(Long id) {
? ?//1 redis中查詢商戶緩存
? ?String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
? ?//2 判斷是否存在
? ?if(StrUtil.isNotBlank(shopJson)){
? ? ? ?//3存在直接返回
? ? ? ?Shop shop = JSONUtil.toBean(shopJson, Shop.class);
? ? ? ?return Result.ok(shop);
? }
? ?//4 不存在根據(jù)id去數(shù)據(jù)庫查詢
? ?Shop shop = this.getById(id);
? ?//5 數(shù)據(jù)庫也不存在,返回錯誤
? ?if(shop==null){
? ? ? ?return Result.fail("店鋪不存在");
? }
? ?//6 存在則寫入redis中
? ?redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop));
? ?//7 返回
? ?return Result.ok(shop);
}
3 改造首頁
@Autowired
private RedisTemplate<String, Object> redisTemplate;
?
@Override
public Result queryTypeList() {
?
? ?List<Object> list = redisTemplate.opsForList().range("cache.list",0,-1);
? ?if(list!=null && list.size()!=0){
? ? ? ?return Result.ok(list);
? }
? ?List<ShopType> sort = this.query().orderByAsc("sort").list();
? ?if(sort==null||sort.size()==0){
? ? ? ?return Result.fail("列表不存在");
? }
? ?sort.forEach(s->redisTemplate.opsForList().rightPush("cache.list",s));
? ?return Result.ok(sort);
}
二 數(shù)據(jù)一致性
1 思路
-
查詢數(shù)據(jù)的時候,如果緩存未命中,則查詢數(shù)據(jù)庫,將數(shù)據(jù)寫入緩存設(shè)置超時時間
-
修改數(shù)據(jù)時,先修改數(shù)據(jù)庫,在刪除緩存。
-
延時雙刪策略
2 代碼實現(xiàn)
-
修改更新方法,添加超時時間
@Override
public Result queryById(Long id) {
//1 redis中查詢商戶緩存
String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
//2 判斷是否存在
if(StrUtil.isNotBlank(shopJson)){
//3存在直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//4 不存在根據(jù)id去數(shù)據(jù)庫查詢
Shop shop = this.getById(id);
//5 數(shù)據(jù)庫也不存在,返回錯誤
if(shop==null){
return Result.fail("店鋪不存在");
}
//6 存在則寫入redis中
redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
//7 返回
return Result.ok(shop);
}
-
修改ShopController
?
@PutMapping
? ?public Result updateShop(@RequestBody Shop shop) {
? ? ? ?// 寫入數(shù)據(jù)庫
?
? ? ? ?//shopService.updateById(shop);
? ? ? ?//return Result.ok();
? ? ? ?return ?shopService.update(shop);
? }
-
修改service代碼 延時雙刪策略
?@Override
? ?public Result update(Shop shop) {
?
? ? ? ?Long id = shop.getId();
? ? ? ?if(id==null){
? ? ? ? ? ?return Result.fail("店鋪id不存在");
? ? ? }
? ? ? ? // 刪除緩存
? ? ? ?redisTemplate.delete("cache.shop:" + id);
? ? ? ?// 更新數(shù)據(jù)庫
? ? ? ?updateById(shop);
? ? ? ?Thread.sleep(800);
? ? ? ?// 刪除緩存
? ? ? ?redisTemplate.delete("cache.shop:" + id);
? ? ? ?return Result.ok();
? }
3 修改完代碼以后,將所有的緩存刪除,執(zhí)行查詢操作,多了超時
4 用postman執(zhí)行修改方法: localhost:8081/shop
{
"area":"大關(guān)",
"sold":3035,
"address":"金華路錦昌文華苑29號",
"name":"102茶餐廳",
"x":120.149192,
"y":30.316078,
"typeId":1,
"id":1
}
執(zhí)行完成以后,數(shù)據(jù)庫的數(shù)據(jù)發(fā)生改變,查看redis的數(shù)據(jù)已經(jīng)刪除了。
三 緩存常見問題
1 緩存穿透:
客戶端請求的數(shù)據(jù),在數(shù)據(jù)庫和redis中都不存在,這樣緩存永遠都不會生效,請求最終都到了數(shù)據(jù)庫上。
解決方案:
-
當在數(shù)據(jù)庫查詢的結(jié)果也不存在的時候,可以返回null值給redis,并且設(shè)置TTL
-
布隆過濾器
布隆過濾器是一種數(shù)據(jù)結(jié)構(gòu),底層是位數(shù)組,通過將集合中的元素多次hash得到的結(jié)果保存到布隆過濾器中。主要作用就是可以快速判斷一個元素是否在集合里面,但是因為算法的原因,也有一定概率的錯誤。
開發(fā)的時候我們一般選擇空值值方式。
-
代碼方式實現(xiàn)
根據(jù)id查詢的時候,如果信息不存在,則要將空值寫入redis,并設(shè)置空值過期時間
@Override
public Result queryById(Long id) {
//1 redis中查詢商戶緩存
String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
//2 判斷是否存在
if(StrUtil.isNotBlank(shopJson)){
//3存在直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
if(shopJson!=null){
return Result.fail("店鋪不存在");
}
//4 不存在根據(jù)id去數(shù)據(jù)庫查詢
Shop shop = this.getById(id);
//5 數(shù)據(jù)庫也不存在,返回錯誤
if(shop==null){
// 空值寫入redis中
redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
return Result.fail("店鋪不存在");
}
//6 存在則寫入redis中
redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
//7 返回
return Result.ok(shop);
}
2 緩存擊穿:
也叫熱點key問題,一個被高并發(fā)訪問且業(yè)務(wù)復(fù)雜的key突然失效了,無數(shù)的請求瞬間給數(shù)據(jù)庫帶來的巨大沖擊
解決方案: 互斥鎖 邏輯過期
互斥鎖思路:
查詢緩存的時候,未命中需要獲取鎖 代碼ShopServiceImpl
@Override
public Result queryById(Long id) {
//1 redis中查詢商戶緩存
String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
//2 判斷是否存在
if(StrUtil.isNotBlank(shopJson)){
//3存在直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
if(shopJson!=null){
return Result.fail("店鋪不存在");
}
Shop shop = null;
String lockKey = "lock.id:" + id;
try {
//代碼到這里說明沒有命中緩存,那么就可以獲取鎖了
boolean isLock = tryLock(lockKey);
// 如果沒有拿到鎖,則等待一會,遞歸執(zhí)行代碼
if(!isLock){
Thread.sleep(100);
queryById(id);
}
//獲取鎖成功
//4 不存在根據(jù)id去數(shù)據(jù)庫查詢
shop = this.getById(id);
//5 數(shù)據(jù)庫也不存在,返回錯誤
if(shop==null){
// 空值寫入redis中
redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
return Result.fail("店鋪不存在");
}
//6 存在則寫入redis中
redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
unlock(lockKey);
}
//7 返回
return Result.ok(shop);
}
// 獲取鎖
private boolean tryLock(String key){
Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
//釋放鎖
private void unlock(String key){
redisTemplate.delete(key);
}
3 緩存雪崩
同一時間段內(nèi),大量的緩存key失效或者redis宕機,到時大量的請求到達數(shù)據(jù)庫,帶來巨大的壓力。
解決方案
-
給key設(shè)置隨機的TTL文章來源:http://www.zghlxwxcb.cn/news/detail-796281.html
-
集群方案防止宕機不可用文章來源地址http://www.zghlxwxcb.cn/news/detail-796281.html
到了這里,關(guān)于Redis數(shù)據(jù)緩存的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!