一、使用場景
我們在日常的開發中,經常會遇到查詢數據列表的問題,有些數據是不經常變化的,如果想做一下優化,在提高查詢的速度的同時減輕數據庫的壓力,那么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 :傳送門