一、在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、为添加双重检测锁,出现同时访问数据的情况