1. 程式人生 > >從.Net到Java學習第七篇——SpringBoot Redis 快取穿透

從.Net到Java學習第七篇——SpringBoot Redis 快取穿透

場景描述:我們在專案中使用快取通常都是先檢查快取中是否存在,如果存在直接返回快取內容,如果不存在就直接查詢資料庫然後再快取查詢結果返回。這個時候如果我們查詢的某一個數據在快取中一直不存在,就會造成每一次請求都查詢DB,這樣快取就失去了意義,在流量大時,可能DB就掛掉了。

穿透:頻繁查詢一個不存在的資料,由於快取不命中,每次都要查詢持久層。從而失去快取的意義。

常用解決辦法:
①用一個bitmap和n個hash函式做布隆過濾器過濾沒有快取的鍵。
②持久層查詢不到就快取空結果,有效時間為數分鐘。

我這裡使用的是雙重檢測同步鎖方式。

修改AreaService介面,新增如下兩個介面方法,selectAllArea2方法是可能會導致快取穿透的方法。

    List<Area> selectAllArea();
    List<Area> selectAllArea2();

修改介面的實現類AreaServiceImpl

  @Autowired
    private RedisService redisService;
    private JSONObject json = new JSONObject();

    /**
     * 從快取中獲取區域列表
     *
     * @return
     */
    private List<Area> getAreaList() {
        String result 
= redisService.get("redis_obj_area"); if (result == null || result.equals("")) { return null; } else { return json.parseArray(result, Area.class); } } @Override public List<Area> selectAllArea() { List<Area> list = getAreaList();
if (list == null) { synchronized (this) { list = getAreaList(); //雙重檢測鎖 if (list == null) { list = areaMapper.selectAllArea(); redisService.set("redis_obj_area", json.toJSONString(list)); System.out.println("請求的資料庫。。。。。。"); } else { System.out.println("請求的快取。。。。。。"); } } } else { System.out.println("請求的快取。。。。。。"); } return list; } @Override public List<Area> selectAllArea2() { List<Area> list = getAreaList(); if (list == null) { list = areaMapper.selectAllArea(); redisService.set("redis_obj_area", json.toJSONString(list)); System.out.println("請求的資料庫。。。。。。"); } else { System.out.println("請求的快取。。。。。。"); } return list; }

執行程式,在瀏覽器中輸入地址http://localhost:8083/boot/getAll,第一次訪問

2018-06-22 10:21:24.730  INFO 10436 --- [nio-8083-exec-1] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
請求的資料庫。。。。。。

重新整理瀏覽器地址,第二次訪問

請求的快取。。。。。。

再開啟我們的redis視覺化管理工具

在之前配置mysql資料庫連線的時候,由於沒有指定是否採用SSL,所以控制檯會有一個警告資訊,如下所示:

這個是因為使用的mysql版本比較高,要求開啟SSL,所以控制檯會有一個警告,當然,你也可以忽略,如果要去除這個警告,可以在之前的mysql連線配置後面新增:&useSSL=false

  datasource:
    url: jdbc:mysql://localhost:3306/demo?&useSSL=false

刪除redis中的這個key值,我們通過使用一個併發測試工具來模擬快取穿透的現象,這裡使用到了jmeter這個併發測試工具。jmeter官網:  https://jmeter.apache.org/。jmeter更多使用教程:https://www.yiibai.com/jmeter/

將jmter下載到本地,然後解壓,雙擊jmeter.bat執行

(1)右鍵單擊“測試計劃”,新建測試組

(2)新建HTTP請求

(3)儲存並執行測試,這是時候其實已經在開始運行了,我們可以通過“選項"——“Log Viewer",來檢視執行日誌。

此時再檢視IDEA中的控制檯執行情況如下:

我們看到有四次進行了資料庫查詢,而我們想要的其實是隻進行一次資料庫查詢,其它的都是直接從快取中進行查詢。

重新刪除redis中的key值redis_obj_area,我們再來測試一下采用了雙重檢測同步鎖的方法selectAllArea2

修改jmeter中的請求路徑

然後執行,我們再看下IDEA中控制檯中的記錄:

現在只有第一次是從資料庫中讀取了。

當然,如果我們不採用測試工具的話,我們也可以自己寫一個單元測試,來進行併發測試。

 單元測試類AreaServiceImplTest的程式碼:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AreaServiceImplTest {
    @Autowired
    public AreaService areaService;
    @Before
    public void setUp() throws Exception {

    }

    @Test
    public void selectAllArea() throws InterruptedException {
        final CountDownLatch latch= new CountDownLatch(4);//使用java併發庫concurrent
        //啟用10個執行緒
        for(int i=1;i<=10;i++){
            new Thread(new Runnable(){
                public void run(){
                    try {
                        //Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    areaService.selectAllArea();
                    System.out.println(String.format("子執行緒%s執行!",Thread.currentThread().getName()));
                    latch.countDown();//讓latch中的數值減一
                }
            }).start();
        }
        //主執行緒
        latch.await();//阻塞當前執行緒直到latch中數值為零才執行
        System.out.println("主執行緒執行!");
    }
    @Test
    public void selectAllArea2() throws InterruptedException {
        final CountDownLatch latch= new CountDownLatch(4);//使用java併發庫concurrent
        //啟用10個執行緒
        for(int i=1;i<=10;i++){
            new Thread(new Runnable(){
                public void run(){
                    try {
                        //Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    areaService.selectAllArea2();
                    System.out.println(String.format("子執行緒%s執行!",Thread.currentThread().getName()));
                    latch.countDown();//讓latch中的數值減一
                }
            }).start();
        }
        //主執行緒
        latch.await();//阻塞當前執行緒直到latch中數值為零才執行
        System.out.println("主執行緒執行!");
    }
    @Test
    public void selectAllArea3(){
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                areaService.selectAllArea2();
            }
        };
        ExecutorService executorService=Executors.newFixedThreadPool(4);
        for (int i=0;i<10;i++){
            executorService.submit(runnable);
        }
    }
}

執行結果,和使用jmeter是差不多的。

相關推薦

.Net到Java學習——SpringBoot Redis 快取穿透

場景描述:我們在專案中使用快取通常都是先檢查快取中是否存在,如果存在直接返回快取內容,如果不存在就直接查詢資料庫然後再快取查詢結果返回。這個時候如果我們查詢的某一個數據在快取中一直不存在,就會造成每一次請求都查詢DB,這樣快取就失去了意義,在流量大時,可能DB就掛掉了。 穿透:頻繁查詢一個不存在的資料,

.Net到Java學習——SpringBoot實現session共享和國際化

區分 cal request 展示 hang 輸入 nds www target SpringBoot Session共享 修改pom.xml添加依賴 <!--spring session--> <dependen

.Net到Java學習——SpringBoot+mongodb&Thymeleaf&模型驗證

SpringBoot整合mongodb   MongoDB 是一個介於關係資料庫和非關係資料庫之間的產品,是非關係資料庫當中功能最豐富,最像關係資料庫的。如果你沒用過MongoDB,可以先去看下我的文章:https://www.cnblogs.com/jiekzou/category/851166.ht

.Net到Java學習——SpringBoot下Thymeleaf

Thymeleaf概述   Thymeleaf 是一個流行的模板引擎,該模板引擎採用java語言開發。模板引擎是一個技術名稱,是跨領域平臺的概念,在java語言體系下有模板引擎,在C#、PHP語言體系下也有模板引擎,甚至在JavaScript中也會用到模板引擎技術。Java生態下的模板引擎有Thymel

R語言學習 :列表

方法 靈活的數據類型 引號 bounds 參考 最大的 post 長度 索引操作 列表(List)是R中最復雜的數據類型,一般來說,列表是數據對象的有序集合,但是,列表的各個元素(item)的數據類型可以不同,每個元素的長度可以不同,是R中最靈活的數據類型。列表項可以是列表

機器學習

決策樹 相比於其他方法,決策樹是一種更為簡單的機器學習方法,它是對被觀測資料進行分類的一種相當直觀的方法,決策樹在經過訓練之後,看起來更像是以樹狀形式排列的一系列if-then語句。只要沿著樹的路徑一直向下,正確回答每一個問題,最終就會得到答案,沿著最終的葉節點向上回溯,就會得到一個有關最終分類

android Telephony學習 --- android7.0 來電(MT)流程

我們先看下7.0來電大體流程: ##Framework modem接收到來電通知訊息後,以AT指令的方式上報RIL層,RIL層通過sokcet將訊息傳送給RILJ, 上報事件ID: RIL_UNSOL

Python 語言學習 :函式1(定義、呼叫和變數的作用域)

函式是把一些語句集合在一起的程式結構,用於把複雜的流程細分成不同的元件,能夠減少程式碼的冗餘、程式碼的複用和修改程式碼的代價。 函式可以0個、1個或多個引數,向函式傳遞引數,可以控制函式的流程。函式還可以返回程式碼執行的結果,從技術上講,任何函式都要返回結果,一個沒有返回值的函式會自動返回none物件。如果

ORACLE學習(常用處理函式)

一、常用聚合函式 求最大值:select max(sal) from emp ; 求最小值:select min(sal) from emp ; 求平均值:select avg(sal) from emp ; Sum 求和:select sum(sal) from emp ; Count

Lucene的學習——Lucene開發的lukeall的使用

入門程式碼的再學習後,前面的內容應該明白,理解80%以上,這裡介紹lukeall的使用。Lukeall就是lucene開發必備的,lukeall我從兩個方面介紹: 1:lukeall是什麼? 2:l

精通SpringBoot——:整合Redis實現快取

專案中用到快取是很常見的事情, 快取能夠提升系統訪問的速度,減輕對資料庫的壓力等好處。今天我們來講講怎麼在spring boot 中整合redis 實現對資料庫查詢結果的快取。 首先第一步要做的就是在pom.xml檔案新增spring-boot-starter-data-redis。 要整合快取,必

.Net到Java學習——spring boot+redis

“學習java已經十天,有時也懷念當初.net的經典,讓這語言將你我相連,懷念你......”接上一篇,本篇使用到的框架redis、FastJSON。 環境準備 安裝redis,下圖是我本機的redis綠色版,你可以網上自行下載安裝,如果不知道如何怎麼操作,可以移步到我的另一篇文章:ASP.NET R

MySQL數據庫學習】單表查詢

not null for 比較運算符 創建 字符串 直接 過濾 field gpo 先創建表 #創建表 create table employee( id int not null unique auto_increment, name varchar(20) not

Python 學習日記 -- 函數相關

函數裝飾器一、裝飾器前戲-閉包簡單來說,python中函數的閉包就是在內部函數裏對外部作用域(但不是全局作用域)的變量進行引用,這麽說,不太好理解,下面的示例幫助理解 def outer(): a = 1 def inner(): # 內部函數inner print(a)

.Net到Java學習——spring boot+mybatis+mysql

jar fig targe list pro ble TE png tween 環境:mysql5.7 新建mysql數據庫demo,然後執行如下sql腳本進行數據表創建和數據初始化: -- ---------------------------- -- Tabl

零開始學產品:常用的功能模組有哪些

一個系統中都有哪些模組組成,對於初學者來說,可能還不能夠區分的很清楚。 但是仔細回想一下,是不是幾乎所有的功能都有登入和註冊的功能?   啟動頁,Banner,輪播,個人中心,關於我們,意見反饋,設定,忘記密碼,支付,地圖,等等等等。 這些都是屬於一個系統裡很常見的功能

】Qt學習與使用---TreeView目錄以及其點選事件

1、目的     想要寫一個目錄,可以列出某一路徑下所有的 檔案,並且可以點選開啟。(初始是想做一個閱讀器程式 ) 2、思路 (1)首先需要將檔名稱以目錄的 形式列出。 (2)可以開啟不同型別的檔案,如  資料夾,PDF,doc,csv,&nb

Docker $ :Docker部署SpringBoot+Mysql

一.Dockerfile常用指令 FROM 目的 指定基礎映象 特點 需要寫在其他指令之前,之後的指令都依賴於該指令指定的映象。 語法 FROM <image> FROM

一起來學 SpringBoot 2.x | :整合 Mybatis

點選上方“芋道原始碼”,選擇“置頂公眾號”技術文章第一時間送達!原始碼精品專欄 摘要: 原創出處

.Net到Java學習——Spring Boot檔案上傳和下載

圖片上傳 Spring Boot中的檔案上傳就是Spring MVC中的檔案上傳,將其整合進來了。 在模板目錄建立一個新的頁面 profile/uploadPage.html <!DOCTYPE html> <html xmlns:th="http://www.thymel