1. 程式人生 > >[Redis] - 高併發下Redis快取穿透解決

[Redis] - 高併發下Redis快取穿透解決

高併發情況下,可能都要訪問資料庫,因為同時訪問的方法,這時需要加入同步鎖,當其中一個快取獲取後,其它的就要通過快取獲取資料.

方法一: 在方法上加上同步鎖 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)
...
...

可以看到多個併發同時訪問方法時,只有一個進入同步鎖查詢了資料庫,其它還是通過快取獲取資料.