[Redis] - 高併發下Redis快取穿透解決
阿新 • • 發佈:2019-01-01
高併發情況下,可能都要訪問資料庫,因為同時訪問的方法,這時需要加入同步鎖,當其中一個快取獲取後,其它的就要通過快取獲取資料.
方法一: 在方法上加上同步鎖 synchronized
//加同步鎖,解決高併發下快取穿透 @Test public synchronized void getMyUser(){ //字串的序列化器 redis RedisSerializer redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer);//查詢快取 MyUser myUser = (MyUser) redisTemplate.opsForValue().get("myUser"); if(null == myUser){ System.out.println("當快取沒有myUser時顯示."); //快取為空,查詢資料庫 myUser = myUserMapper.selectByPrimaryKey(1l); //把資料庫查詢出來的資料放入redis redisTemplate.opsForValue().set("myUser",myUser); } System.out.println(myUser); }
方法二: 使用雙層檢測鎖, 效率高於方法一.
@Test public void getMyUser(){ //字串的序列化器 redis RedisSerializer redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer); //查詢快取 MyUser myUser = (MyUser) redisTemplate.opsForValue().get("myUser");//雙層檢測鎖 if(null == myUser){ synchronized(this){ myUser = (MyUser) redisTemplate.opsForValue().get("myUser"); if(null == myUser){ System.out.println("當快取沒有myUser時顯示."); //快取為空,查詢資料庫 myUser = myUserMapper.selectByPrimaryKey(1l); //把資料庫查詢出來的資料放入redis redisTemplate.opsForValue().set("myUser",myUser); } } } System.out.println(myUser); }
進行高併發測試:
package com.ykmimi.job.controller; import com.ykmimi.job.bean.MyUser; import com.ykmimi.job.mapper.MyUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController public class MyUserController { @Autowired private RedisTemplate<Object,Object> redisTemplate; @Resource private MyUserMapper myUserMapper; public Integer insertNew(){ return 0; } @RequestMapping("/getMyUserTest") public void getMyUserTest(){ //執行緒,該執行緒呼叫底層查詢MyUser的方法 Runnable runnable = new Runnable() { @Override public void run() { getMyUser(); } }; //多執行緒測試一下快取穿透的問題 ExecutorService executorService = Executors.newFixedThreadPool(25); for(int i=0;i<10000;i++){ executorService.submit(runnable); } } public void getMyUser(){ //字串的序列化器 redis RedisSerializer redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer); //查詢快取 MyUser myUser = (MyUser) redisTemplate.opsForValue().get("myUser"); //雙層檢測鎖 if(null == myUser){ System.out.println("當快取沒有myUser時顯示.沒有進入同步鎖"); synchronized(this){ myUser = (MyUser) redisTemplate.opsForValue().get("myUser"); if(null == myUser){ System.out.println("當快取沒有myUser時顯示.已經進入同步鎖"); //快取為空,查詢資料庫 myUser = myUserMapper.selectByPrimaryKey(1l); //把資料庫查詢出來的資料放入redis redisTemplate.opsForValue().set("myUser",myUser); } } } System.out.println(myUser); } }
執行緒池中不要特別大的執行緒,
隨後看列印輸出:
當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.沒有進入同步鎖 當快取沒有myUser時顯示.已經進入同步鎖 Creating a new SqlSession SqlSession [[email protected]] was not registered for synchronization because synchronization is not active 2019-01-01 17:10:51.616 INFO 19540 --- [ool-1-thread-14] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2019-01-01 17:10:51.747 INFO 19540 --- [ool-1-thread-14] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. JDBC Connection [[email protected]1935473697 wrapping [email protected]] will not be managed by Spring ==> Preparing: select id, user_id, open_id, user_name, user_phone, location from my_user where id = ? ==> Parameters: 1(Long) <== Columns: id, user_id, open_id, user_name, user_phone, location <== Row: 1, 111, 111, 123, 111, t <== Total: 1 Closing non transactional SqlSession [[email protected]] MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t) MyUser(id=1, userId=111, openId=111, userName=123, userPhone=111, location=t)
...
...
可以看到多個併發同時訪問方法時,只有一個進入同步鎖查詢了資料庫,其它還是通過快取獲取資料.