從.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