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"; }