談談redis緩存三大問題(三)- 緩存擊穿

首先還是先看下緩存擊穿的一個基本概念:
在這裏插入圖片描述
如上圖,這個圖應該在其他博文中出現過很多次了,同樣,緩存擊穿就是在某個時刻。當某個熱點key失效的瞬間,大批量請求進來,造成數據庫壓力太大導致數據庫服務宕機。

當然關於緩存擊穿也是有對應的解決方法:
1,設置熱點key用不過期
2,使用分佈式鎖
備註:關於redis實現分佈式鎖,可以參考我的另一篇博客:https://blog.csdn.net/baomw/article/details/84931234

當然當有了分佈式鎖之後,我們該如何實現呢?可以看下如下案例:

 public User queryById(String id) {
        Jedis jedis = jedisPool.getResource();
        String userStr = jedis.get(id);
        //先查緩存,查到直接返回
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        //模擬併發效果這邊在查緩存和數據庫之間延時50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException ignored) {
        }
        //查不到差數據庫
        System.out.println("==========開始查數據庫============");
        User user = dao.queryById(id);
        jedis.setex(user.getId(), 10, JSONObject.toJSONString(user));
        return user;
    }
	
	public static void main(String[] args) {
	    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	    final BaomwTestService cityService = ac.getBean(BaomwTestService.class);
	    //模擬併發效果這邊啓動10個線程同時做數據庫查詢操作
        for (int i=0;i<10;i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(cityService.queryById("2"));
                }
            }).start();
        }
    }

在這裏插入圖片描述
可以看到,十次都查詢了數據庫,如果是高併發場景下,成千上萬個線程同時進來,那麼數據庫壓力顯然是受不住的。

當然解決方案就是加分佈式鎖,這邊可以看下案例

public User queryById(String id) {
        Jedis jedis = jedisPool.getResource();
        //先查緩存,查到直接返回
        String userStr = jedis.get(id);
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException ignored) {
        }
        //對查詢健加鎖,使鎖的粒度最小
        lock.lock("locak"+id);
        //這裏需要再次查詢緩存,因爲可能會出現第一波併發線程同時到達:lock.lock("locak"+id);的
        //位置,雖然是每次只有一個線程會拿到鎖並執行,但是已經到這個位置的線程還是都會去查詢一遍數據庫
        //所以這邊必須加一個查詢緩存的操作
        userStr = jedis.get(id);
        if (StringUtils.isNotEmpty(userStr)) {
            return JSONObject.parseObject(userStr, User.class);
        }
        try {
            //查不到差數據庫
            System.out.println("==========開始查數據庫============");
            User user = dao.queryById(id);
            jedis.setex(user.getId(), 100, JSONObject.toJSONString(user));
            return user;
        }finally {
            //爲了預防死鎖,這邊再finally加解鎖操作
            lock.unlock("locak"+id);
        }
    }

在這裏插入圖片描述
可以看到現在是隻查詢一次數據庫了。

發佈了47 篇原創文章 · 獲贊 97 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章