SpringBoot学习笔记八:springBoot集成redis以及缓存击穿问题

一、在pom文件中加入redis的连接依赖;

        <!-- Redis依赖 -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-redis</artifactId>
		    <!--<version>2.1.4.RELEASE</version>-->
		</dependency>

二、在application.properties文件中进行redis的连接配置;

# Redis连接配置(本地测试没有设置密码)
spring.redis.host=127.0.0.1
spring.redis.port=6379

 三、编写测试方法,引入依赖后springBoot会自动配置一个RedisTemplate,它的泛型只有<String,String>和<Object,Object>两种;

package com.springboot.business.service.impl;

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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.springboot.business.mapper.AdminUserMapper;
import com.springboot.business.model.AdminUser;
import com.springboot.business.service.AdminUserService;

@Service
public class AdminUserServiceImpl implements AdminUserService{

	@Autowired
	private AdminUserMapper adminUserMapper;
	
	//注入springBoot自动配置好的redisTemplate
	@Autowired
	private RedisTemplate<Object, Object> redisTemplate;
	
	/**
	 * 查询单个数据
	 */
	@Override
	public AdminUser getOneByPrk(Integer prk) {
		
		//序列化redis键值对的key为string类型(方便redis管理工具查看)
		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);
		
		//缓存中获取adminUser对象(当前写法存在缓存击穿问题)
		AdminUser adminUser = (AdminUser) redisTemplate.opsForValue().get("adminUser");
		if(null == adminUser) {
            System.out.println("数据库中取数据");
			adminUser = adminUserMapper.selectByPrimaryKey(prk);
			redisTemplate.opsForValue().set("adminUser", adminUser);//存入redis
		}else{
             System.out.println("缓存中取数据");
            }
		
		return adminUser;
	}
	

}

 四、方法中所使用的实体类必须实现序列化接口(Serializable),否则会抛出序列化异常

package com.springboot.business.model;

import java.io.Serializable;

public class AdminUser implements Serializable{
	
	private static final long serialVersionUID = 1L;

	private Integer adminUserId;

    private String loginUserName;

    private String loginPassword;

    private String nickName;

    private Byte locked;

    public Integer getAdminUserId() {
        return adminUserId;
    }

    public void setAdminUserId(Integer adminUserId) {
        this.adminUserId = adminUserId;
    }

    public String getLoginUserName() {
        return loginUserName;
    }

    public void setLoginUserName(String loginUserName) {
        this.loginUserName = loginUserName == null ? null : loginUserName.trim();
    }

    public String getLoginPassword() {
        return loginPassword;
    }

    public void setLoginPassword(String loginPassword) {
        this.loginPassword = loginPassword == null ? null : loginPassword.trim();
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName == null ? null : nickName.trim();
    }

    public Byte getLocked() {
        return locked;
    }

    public void setLocked(Byte locked) {
        this.locked = locked;
    }
}

 五、解决缓存击穿问题:

        缓存击穿:高并发访问时,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

        解决方法:1、直接在方法前添加synchronized锁,此方法会降低效率

                          2、进行双重检测锁,效率影响较小,如下;


测试方法:创建10000个线程进行访问

package com.springboot.business.controller;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.springboot.business.model.AdminUser;
import com.springboot.business.service.AdminUserService;

//此注解作用是:该controller下 所有方法都返回字符串
@RestController
public class AdminUserController {

	@Autowired
	private AdminUserService adminUserService;
	
	@GetMapping("/demo/getAdminUser/{adminUerId}")
	public Object getAdminUser(@PathVariable("adminUerId") Integer prk) {
		
		//新建个线程池
		ExecutorService executorService = Executors.newFixedThreadPool(25);
		//创建10000个线程访问
		for(int i=0; i<10000; i++) {
			executorService.submit(new Runnable() {
				@Override
				public void run() {
					adminUserService.getOneByPrk(prk);
				}
			});
		}
		return adminUserService.getOneByPrk(prk);
	}
	
	
}

 方法处理:

   /**
	 * 查询单个数据
	 */
	@Override
	public AdminUser getOneByPrk(Integer prk) {
		
		//序列化redis键值对的key为string类型
		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(redisSerializer);
		
		//缓存中获取adminUser对象(当前写法存在缓存击穿问题)
		AdminUser adminUser = (AdminUser) redisTemplate.opsForValue().get("adminUser");
		
		//双重检测锁
		if(null == adminUser) {
			//加锁
			synchronized (this) {
				adminUser = (AdminUser) redisTemplate.opsForValue().get("adminUser");
				if(null == adminUser) {
					System.out.println("数据库中取数据");
					adminUser = adminUserMapper.selectByPrimaryKey(prk);
					redisTemplate.opsForValue().set("adminUser", adminUser);//存入redis
				}else {
					System.out.println("缓存中取数据");
				}
			}
			
		}else {
			System.out.println("缓存中取数据");
		}
		
		return adminUser;
	}

 测试结果:

 1、添加双重检测锁,数据正常

2、为添加双重检测锁,出现同时访问数据的情况

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值