解决高并发下的缓存穿透问题

  目前我们针对高并发场景,这种问题很常见,一般有如下两种解决方式:

  一:在方法上加上同步锁 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);
    }

  二:使用双重检测锁(常用)

  

 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);
    }

  三:进行高并发测试

  

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);
    }


}

  住:线程池中不要特别大的线程

  

猜你喜欢

转载自www.cnblogs.com/wangzhengyu/p/10621464.html