[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)
...
...

可以看到多个并发同时访问方法时,只有一个进入同步锁查询了数据库,其它还是通过缓存获取数据.

posted @ 2019-01-01 16:51  ukyo--夜王  阅读(1021)  评论(0编辑  收藏  举报