SpringBoot 集成Redis單機(jī)模式
項(xiàng)目名稱:016-springboot-redis
完善根據(jù)學(xué)生id查詢學(xué)生的功能,先從redis緩存中查找,如果找不到,再?gòu)臄?shù)據(jù)庫(kù)中查找,然后放到redis緩存中。
首先通過(guò)MyBatis逆向工程生成實(shí)體bean和數(shù)據(jù)持久層。
① 在pom.xml文件中添加redis依賴
<!-- 加載spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
② 在Spring Boot核心配置文件application.properties中配置redis連接信息
完整application.properties配置文件如下:
#配置內(nèi)嵌Tomcat端口號(hào)
server.port=9090
#配置項(xiàng)目上下文根
server.servlet.context-path=/016-springboot-redis
#配置連接MySQL數(shù)據(jù)庫(kù)信息
spring.datasource.url=jdbc:mysql://192.168.92.134:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#配置視圖解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
#配置redis連接信息(單機(jī)模式)
spring.redis.host=192.168.92.134
spring.redis.port=6379
spring.redis.password=123456
③ 啟動(dòng)redis服務(wù)
④ RedisController類(lèi)
RestController
public class RedisController {
@Autowired
private StudentService studentService;
/**
* 請(qǐng)求地址:http://localhost:9090/016-springboot-redis//springboot/allStudentCount
* @param request
* @return
*/
@GetMapping(value = "/springboot/allStudentCount")
public Object allStudentCount(HttpServletRequest request) {
Long allStudentCount = studentService.queryAllStudentCount();
return "學(xué)生總?cè)藬?shù):" + allStudentCount;
}
}
⑤ StudentService接口
public interface StudentService {
/**
* 獲取學(xué)生總?cè)藬?shù)
* @return
*/
Long queryAllStudentCount();
}
⑥ 在StudentServiceImpl中注入RedisTemplate并修改根據(jù)id獲取學(xué)生的方法配置了上面的步驟,Spring Boot將自動(dòng)配置RedisTemplate,在需要操作redis的類(lèi)中注入redisTemplate即可。
注意:Spring Boot幫我們注入RedisTemplate類(lèi),泛型里面只能寫(xiě) 、或者什么都不寫(xiě)。
@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Long queryAllStudentCount() {
//設(shè)置redisTemplate對(duì)象key的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
//從redis緩存中獲取總?cè)藬?shù)
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判斷是否為空
if (null == allStudentCount) {
//去數(shù)據(jù)庫(kù)查詢,并存放到redis緩存中
allStudentCount = studentMapper.selectAllStudentCount();
redisTemplate.opsForValue().set("allStudentCount",allStudentCount,15, TimeUnit.SECONDS);
}
return allStudentCount;
}
}
⑦ 啟動(dòng)類(lèi)Application在SpringBoot啟動(dòng)類(lèi)上添加掃描數(shù)據(jù)持久層的注解并指定掃描包
@SpringBootApplication
@MapperScan(basePackages = "com.bjpowernode.springboot.mapper")//掃描數(shù)據(jù)持久層
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
⑧ 讓Student類(lèi)實(shí)現(xiàn)序列化接口(可選)在類(lèi)名上Alt + 回車(chē),如果沒(méi)有提示生成序列化id,那么需要做如下的配置
⑨ 啟動(dòng)SpringBoot應(yīng)用,訪問(wèn)測(cè)試
⑩ 打開(kāi)Redis Desktop Mananger查看Redis中的情況
項(xiàng)目名稱:017-springboot-redis-synchronized
首先通過(guò)MyBatis逆向工程生成實(shí)體bean和數(shù)據(jù)持久層。
1.pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bjpowernode.springboot</groupId>
<artifactId>017-springboot-redis-synchronized</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>017-springboot-redis-synchronized</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--SpringBoot web項(xiàng)目起步依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--MyBatis集成SpringBoot框架的起步依賴-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--連接MySQL的驅(qū)動(dòng)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 加載spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<!--mybatis代碼自動(dòng)生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<!--配置文件的位置-->
<configurationFile>GeneratorMapper.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.核心配置文件application.properties
#配置內(nèi)嵌Tomcat端口號(hào)
server.port=9090
#配置項(xiàng)目上下文根
server.servlet.context-path=/017-springboot-redis-synchronized
#配置MySQL數(shù)據(jù)庫(kù)連接
spring.datasource.url=jdbc:mysql://192.168.92.134:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#配置redis緩存
spring.redis.host=192.168.92.134
spring.redis.port=6379
spring.redis.password=123456
#配置SpringMVC視圖解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
3.Dao數(shù)據(jù)持久層
4.Service業(yè)務(wù)層
@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
@Override
public Long queryAllStudentCount() {
//設(shè)置redis的key序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
//從redis中獲取學(xué)生總?cè)藬?shù)
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判斷是否為空
if (null == allStudentCount) {
System.out.println("查詢數(shù)據(jù)庫(kù)。。。。。");
//從數(shù)據(jù)庫(kù)查詢
allStudentCount = studentMapper.selectAllStudentCount();
//將值再存放到redis緩存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 15, TimeUnit.HOURS);
} else {
System.out.println("查找Redis。。。。。");
}
return allStudentCount;
}
}
5.Controller層
@RestController
public class RedisController {
@Autowired
private StudentService studentService;
@GetMapping(value = "/springBoot/student")
public @ResponseBody Object student() {
//線程池個(gè)數(shù),一般建議是CPU內(nèi)核數(shù) 或者 CPU內(nèi)核數(shù)據(jù)*2
ExecutorService executorService = Executors.newFixedThreadPool(8);
for (int i = 0; i < 1000; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
studentService.queryAllStudentCount();
}
});
}
return "學(xué)生總?cè)藬?shù):" + studentService.queryAllStudentCount();
}
}
6.啟動(dòng)類(lèi)
@SpringBootApplication
@MapperScan(basePackages = "com.bjpowernode.springboot.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
7.啟動(dòng)應(yīng)用程序,瀏覽器訪問(wèn)測(cè)試
8.造成的問(wèn)題
Tip:多個(gè)線程都去查詢數(shù)據(jù)庫(kù),這種現(xiàn)象就叫做緩存穿透,如果并發(fā)比較大,對(duì)數(shù)據(jù)庫(kù)的壓力過(guò)大,有可能造成數(shù)據(jù)庫(kù)宕機(jī)。
項(xiàng)目名稱:018-springboot-redis-synchronized
項(xiàng)目描述:018-springboot-redis-synchronized項(xiàng)目是在017-springboot-redis-synchronized項(xiàng)目基礎(chǔ)上進(jìn)行解決緩存穿透現(xiàn)象
1. 修改StudentServiceImpl中的代碼
@Service("studentServiceImpl")
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
@Override
public Long queryAllStudentCount() {
//設(shè)置redisTemplate對(duì)象的key的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
//從redis緩存中獲取學(xué)生總?cè)藬?shù)
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判斷學(xué)生總?cè)藬?shù)是否為空
if (null == allStudentCount) {
//設(shè)置同步代碼塊
synchronized (this) {
//再次從redis緩存中獲取學(xué)生總?cè)藬?shù)
allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//雙重檢測(cè)判斷緩存中是否有數(shù)據(jù)
if (null == allStudentCount) {
System.out.println("從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)");
//從數(shù)據(jù)庫(kù)獲取學(xué)生總?cè)藬?shù)
allStudentCount = studentMapper.selectAllStudentCount();
//將此值存放到redis緩存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 15, TimeUnit.MINUTES);
} else {
System.out.println("從Redis中獲取數(shù)據(jù)。。。。");
}
}
} else {
System.out.println("從Redis中獲取數(shù)據(jù)。。。。");
}
return allStudentCount;
}
}
2. 啟動(dòng)應(yīng)用程序,瀏覽器訪問(wèn)測(cè)試,查看控制臺(tái)輸出只有第一個(gè)線程查詢數(shù)據(jù)庫(kù),其它線程查詢Redis緩存,這樣的解決的小問(wèn)題就是第一批進(jìn)來(lái)的用戶會(huì)有一個(gè)等待,但是這樣的影響可以忽略。
3. springboot集成Redis阻止緩存穿透,為什么要做雙層驗(yàn)證
防止線程獲取到cpu執(zhí)行權(quán)限的時(shí)候,其他線程已經(jīng)將數(shù)據(jù)放到Redis中了,所以再次判斷;
不能將synchronized范圍擴(kuò)大,因?yàn)槿绻鸕edis緩存中如果有數(shù)據(jù),線程不應(yīng)該同步,否則影響效率。
1. 在pom.xml中配置相關(guān)的jar依賴
這步我們前面已經(jīng)做過(guò)
<!-- 加載spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 在Springboot核心配置文件application.properties中配置redis連接信息
IP地址及master根據(jù)自己的情況進(jìn)行修改
#哨兵模式redis集群配置(哨兵模式)
spring.redis.password=123456
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.235.128:26380,192.168.235.128:26382,192.168.235.128:26384
3. 通過(guò)rz –y命令,用我提供的資源中的配置文件覆蓋Linux服務(wù)器上Redis的配置文件,注意根據(jù)自己機(jī)器的情況修改資源文件ip及引用的redis路徑
我們目前6384是主,6380和6382是從
4. 啟動(dòng)三臺(tái)Redis服務(wù)器
5. 在Xshell中開(kāi)三個(gè)窗口,啟動(dòng)三臺(tái)Redis哨兵
6. 在Redis Desktop Manager上創(chuàng)建三個(gè)連接,連接6380,6382,6384Redis服務(wù)器
7. 清空6384主數(shù)據(jù),查看情況
6380和6382也跟著清空。
8. 啟動(dòng)SpringBoot主程序,瀏覽器訪問(wèn),查看Redis情況
Redis6380、6382、6384中都會(huì)存入數(shù)據(jù)。
9. 將Redis6384主停止再啟動(dòng)
哨兵會(huì)將Redis6382作為主,等Redis6384再次啟動(dòng)后,會(huì)作為Redis6382的從。