從.Net到Java學習系列目錄
場景描述:我們在項目中使用緩存通常都是先檢查緩存中是否存在,如果存在直接傳回緩存内容,如果不存在就直接查詢資料庫然後再緩存查詢結果傳回。這個時候如果我們查詢的某一個資料在緩存中一直不存在,就會造成每一次請求都查詢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是差不多的。
部落格位址: | http://www.cnblogs.com/jiekzou/ |
部落格版權: | 本文以學習、研究和分享為主,歡迎轉載,但必須在文章頁面明顯位置給出原文連接配接。 如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起讨論,共同進步! 再次感謝您耐心的讀完本篇文章。 |
其它: | .net-QQ群4:612347965 java-QQ群:805741535 H5-QQ群:773766020 我的拙作《ASP.NET MVC企業級實戰》《H5+移動應用實戰開發》 《Vue.js 2.x實踐指南》 《JavaScript實用教程 》 《Node+MongoDB+React 項目實戰開發》 已經出版,希望大家多多支援! |