[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 [org.apache.ibatis.session.defaults.DefaultSqlSession@b49b283] 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 [HikariProxyConnection@1935473697 wrapping com.mysql.cj.jdbc.ConnectionImpl@4bc0a38] 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 [org.apache.ibatis.session.defaults.DefaultSqlSession@b49b283]
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)
...
...

可以看到多個並發同時訪問方法時,只有一個進入同步鎖查詢了數據庫,其它還是通過緩存獲取數據.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM