package com.example.redisdistlock.controller;
import com.example.redisdistlock.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class CacheController {
@Autowired
private StringRedisTemplate stringRedisTemplate = null;
@Autowired
private RedisUtil redisUtil = null;
/**
* ****************************** 緩存穿透 ******************************
* 緩存穿透,是指查詢一個數據庫一定不存在的數據。
* 正常的使用緩存流程大致是,數據查詢先進行緩存查詢,
* 如果key不存在或者key已經過期,再對數據庫進行查詢,
* 並把查詢到的對象,放進緩存。如果數據庫查詢對象為空,則不放進緩存。
* 災難現場:想象一下這個情況,如果傳入的參數為-1,會是怎么樣?這個-1,就是一定不存在的對象。就會每次都去查詢數據庫,
* 而每次查詢都是空,每次又都不會進行緩存。假如有惡意攻擊,就可以利用這個漏洞,對數據庫造成壓力,甚至壓垮數據庫。
* 解決方案:如果從數據庫查詢的對象為空,也放入緩存,只是設定的緩存過期時間較短,比如設置為60秒。
*/
/**
* ****************************** 緩存雪崩 ******************************
* 是指在某一個時間段,緩存集中過期失效。此刻無數的請求直接繞開緩存,直接請求數據庫。
* 災難現場:比如天貓雙11,馬上就要到雙11零點,很快就會迎來一波搶購,這波商品在23點集中的放入了緩存,假設緩存一個小時。
* 那么到了凌晨24點的時候,這批商品的緩存就都過期了。而對這批商品的訪問查詢,都落到了數據庫上,對於數據庫而言,就會產生周期性的壓力波峰。
* 對數據庫造成壓力,甚至壓垮數據庫。
*/
/**
* ****************************** 緩存擊穿 ******************************
* 是指一個key非常熱點,在不停的扛着大並發,大並發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大並發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
* 災難現場:比如某個爆款商品(這種爆款很難對數據庫服務器造成壓垮性的壓力。達到這個級別的公司沒有幾家的。)但我們也要做好防護方案
* 解決方案:對爆款商品都是早早的做好了准備,讓緩存永不過期。即便某些商品自己發酵成了爆款,也是直接設為永不過期。
*/
public Object cacheBreakDown(){
Map<String, Object> map = new HashMap<String, Object>();
try {
Object zhangsan = redisUtil.get("zhangsan");
//System.out.println("zhangsan" + zhangsan);
/* 使用雙重驗證鎖解決高並發環境下的緩存穿透問題 */
if (StringUtils.isEmpty(zhangsan)) { // 第一重驗證
synchronized (this) {
zhangsan = redisUtil.get("zhangsan");
if (StringUtils.isEmpty(zhangsan)) { // 第二重驗證
System.out.println("查詢數據庫............");
// 緩存為空,則查詢數據庫將相關數據存儲到redis中
redisUtil.set("zhangsan", "張三",10); //10秒后過期
} else {
System.out.println("2 查詢緩存............");
}
}
} else {
System.out.println("1 查詢緩存............");
}
map.put("success", true);
////entity實體類
//User user = new User();
//user.setUserId(1000);
//user.setUserName("張三");
//user.setAddress("深圳市南山區");
//user.setMobile("13988886666");
//redisUtil.set("userInfo", user.toString(), 10); //10秒后過期自動刪除
////獲取顯示
//String str = String.valueOf(redisUtil.get("userInfo"));
//JSONObject jsonObj = new JSONObject(str);
//map.put("userInfo", jsonObj.get("userId"));
} catch (Exception e) {
map.put("success", false);
e.printStackTrace();
} finally {
}
return map;
}
}
/**
* Redis分布式並發鎖(針對業務場景:庫存超賣 秒殺 限購等)
*
* @return
*/
@RequestMapping("/reductstore")
@ResponseBody //直接輸出字符串
public String ReductStore() {
System.out.println("訪問接口");
String lockKey = "lock";
// setnx redisson
RLock lock = redissonClient.getLock(lockKey);
try {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); lock.lock();
if (stock > 0) {
//業務邏輯減少庫存
stringRedisTemplate.opsForValue().set("stock", (stock - 1) + "");
System.out.println("扣減庫存成功,庫存stock:" + (stock - 1));
} else {
System.out.println("商品已售罄");
}
} catch (NumberFormatException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return "OK";
}
/**
* 單體式架構
*
* @return
*/
@RequestMapping("/reduct")
@ResponseBody //直接輸出字符串
public String Reduct() {
//System.out.println("訪問接口");
try {
synchronized (this) { //jvm核心技術
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
//業務邏輯減少庫存
stringRedisTemplate.opsForValue().set("stock", (stock - 1) + "");
System.out.println("扣減庫存成功,庫存stock:" + (stock - 1));
} else {
System.out.println("商品已售罄");
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
} finally {
}
return "OK";
}