redis学习(五)redis 缓存穿透简单示例

 

正常:一个请求先访问缓存,缓存里面有,取缓存里面的数据,没有去数据库查询返回,放入缓存,返回数据(图一)。

但是:当并发量大的时候,缓存中都没有,多个请求直接落在了数据库上,导致数据库异常。

 

 

代码示例:

引入redis依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.1</version>
        </dependency>

model类:

public class NutUser implements Serializable {


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    private String name;

    @JsonIgnore
    private String password;
    private Integer age;

    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss a", locale = "zh",timezone = "GMT+8")
    private Date birthday;

    @JsonInclude(JsonInclude.Include.NON_NULL)//当这个字段为空的时候是不显示给前端的
    private String desc;
}

 

controller代码:

@RestController
@RequestMapping("/user")
public class NutUserController {

    @Autowired
    private NutService nutService;

    @RequestMapping("/getUserJson")
    public NVJSONResult getUserJson(){


        //请求查询
        Runnable runnable=new Runnable(){
            @Override
            public void run() {
                nutService.findNutUser();
            }
        };

        //多线程测试:
        ExecutorService executorService =Executors.newFixedThreadPool(20);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(runnable);
        }


        return NVJSONResult.ok("ok");
    }
}

service层:

@Service
public class NutService {


    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    public NutUser findNutUser(){

        //字符串的序列化
        RedisSerializer redisSerializer=new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //查询缓存
        NutUser user=(NutUser)redisTemplate.opsForValue().get("user");

        //插入
        if (null==user){
            System.out.println("数据库生成————————>");
            user=create();//1.缓存为空:生成一个用户到数据库(这里生成user的操作代替查询数据库操作)
            redisTemplate.opsForValue().set("user",user);//2.放入redis
        }else {
            System.out.println("查询缓存:");
        }

        //返回
        return user;
    }

    public NutUser create(){
        NutUser a=new NutUser();
        a.setName("张小明");
        a.setBirthday(new Date());
        a.setAge(18);

        String passwords = EncryptUtils.encryptPassword("nutvideo");
        //System.out.println("加密:"+EncryptUtils.encryptPassword("nutvideo"));
        a.setPassword(passwords);
        a.setDesc("我是小明");
        return a;
    }
}

运行调取一下接口:

并发请求的时候,这个时候就穿透了,直接访问数据库。

解决方式:

调整service代码:

    public NutUser findNutUser(){

        //字符串的序列化
        RedisSerializer redisSerializer=new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);

        //1.查询缓存
        NutUser user=(NutUser)redisTemplate.opsForValue().get("user");

        //2.插入
        if (null==user){
             //3.同步锁
            synchronized (this){
                //4.去redis取一下
                user=(NutUser)redisTemplate.opsForValue().get("user");
                if (null ==user){
                    System.out.println("数据库生成————————>");
                    user=create();//5.缓存为空:生成一个用户到数据库(这里生成user的操作代替查询数据库操作)
                    redisTemplate.opsForValue().set("user",user);//6.放入redis
                }else{
                    System.out.println("查询缓存:");
                }
            }
        }else {
            System.out.println("查询缓存:");
        }

        //7.返回user
        return user;
    }

在并发编程中存在线程安全问题,主要原因有:

1.存在共享数据

2.多线程共同操作共享数据。

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我先来一碗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值