一、使用場景
我們在日常的開發中,經常會遇到查詢數據列表的問題,有些數據是不經常變化的,如果想做一下優化,在提高查詢的速度的同時減輕數據庫的壓力,那么redis緩存絕對是一個好的解決方案。
二、需求
假設有10000個請求,想達到第一次請求從數據庫中獲取,其他9999個請求從redis中獲取這種效果。
三、代碼實現
3.1、常規寫法
public List<UsersDO> getAllUserWithNoPage2(){
try{
//序列化器,將key的值設置為字符串
RedisSerializer redisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
//查緩存
List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
if(null==list){
UsersQuery query=new UsersQuery();
list=usersDOMapper.selectByExample(query);
redisTemplate.opsForValue().set("allUsers", list);
System.out.println("從數據庫中取數據");
}
else{
System.out.println("從緩存中取數據");
}
return list;
}
catch (Exception e) {
logger.error("UserService.getAllUserWithNoPage error",e);
}
return null;
}
常規的這種寫法單線程沒有問題,但是考慮到並發的存在,就會出現緩存滲透的問題,也就是不能保證其他9999個請求都是從redis中取。
3.2、常規寫法壓測
@GetMapping(value = "/test2")
public String test2(){
ExecutorService executorService= Executors.newFixedThreadPool(20);
for(int i=1 ; i<=10000;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
userService.getAllUserWithNoPage2();
}
});
}
return "test over";
}
3.3、常規寫法壓測結果

3.4、常規寫法的改進,使用雙重檢測鎖
public List<UsersDO> getAllUserWithNoPage(){
try{
//序列化器,將key的值設置為字符串
RedisSerializer redisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
//查緩存
List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
if(null==list){
//雙重檢測 鎖
synchronized (this) {
List<UsersDO> list1 = (List<UsersDO>) redisTemplate.opsForValue().get("allUsers");
if (null == list1) {
UsersQuery query=new UsersQuery();
list=usersDOMapper.selectByExample(query);
redisTemplate.opsForValue().set("allUsers", list);
System.out.println("從數據庫中取數據");
}
else{
System.out.println("從緩存中取數據");
}
}
}
else{
System.out.println("從緩存中取數據");
}
return list;
}
catch (Exception e) {
logger.error("UserService.getAllUserWithNoPage error",e);
}
return null;
}
3.5、雙重檢測鎖壓測
@GetMapping(value = "/test")
public String test(){
ExecutorService executorService= Executors.newFixedThreadPool(20);
for(int i=1 ; i<=10000;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
userService.getAllUserWithNoPage();
}
});
}
return "test over";
}
3.6、雙重檢測鎖壓測結果

壓測結果符合要求。
完整代碼已上傳Github :傳送門
