今天進一步學習了redis的相關知識,希望下面的博客可以給博友們帶來幫助。
docker安裝redis連接Redis Desktop Manager
啓動docker (系統控制ctl:control)
systemctl start docker
查找Docker Hub上的redis鏡像
docker search redis
這裏我們拉取官方的默認redis鏡像
docker pull redis
查詢已有的鏡像
docker images
使用redis鏡像
運行容器
docker run -p 6379:6379 -d redis
命令說明:
-p 6379:6379 : 將容器的6379端口映射到主機的6379端口
查看容器啓動情況
docker ps
連接、查看容器,使用redis鏡像執行redis-cli命令連接到剛啓動的容器,主機IP爲127.0.0.1
docker exec -it 43f7a65ec7f8 redis-cli
127.0.0.1:6379> info
打開redis可視化工具Redis Desktop Manager
連接本地redis
redis中文網 http://redis.cn/ 可以查看redis命令等
springboot項目使用Cache緩存
一.Cache緩存的作用
大規模的數據庫查詢操作會成爲影響用戶使用體驗的瓶頸,此時Cache緩存往往是解決這一問題非常好的手段之一。
1.JSR107
Java Caching定義了5個核心接口,分別是CachingProvider, CacheManager, Cache, Entry 和 Expiry。
示意圖:
CachingProvider定義了創建、配置、獲取、管理和控制多個CacheManager。一個應用可 以在運行期訪問多個CachingProvider。
CacheManager定義了創建、配置、獲取、管理和控制多個唯一命名的Cache,這些Cache 存在於CacheManager的上下文中。一個CacheManager僅被一個CachingProvider所擁有。
Cache是一個類似Map的數據結構並臨時存儲以Key爲索引的值。一個Cache僅被一個 CacheManager所擁有。
Entry是一個存儲在Cache中的key-value對。
Expiry 每一個存儲在Cache中的條目有一個定義的有效期。一旦超過這個時間,條目爲過期 的狀態。一旦過期,條目將不可訪問、更新和刪除。緩存有效期可以通過ExpiryPolicy設置。
2.Spring緩存抽象
Spring從3.1開始定義了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口來統一不同的緩存技術; 並支持使用JCache(JSR-107)註解簡化我們開發。
Cache接口爲緩存的組件規範定義,包含緩存的各種操作集合。
Cache接口下Spring提供了各種xxxCache的實現;如RedisCache,EhCacheCache , ConcurrentMapCache。
每次調用需要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目標方法是否 已經被調用過;如果有就直接從緩存中獲取方法調用後的結果,如果沒有就調用方法 並緩存結果後返回給用戶。下次調用直接從緩存中獲取。
使用Spring緩存抽象時我們需要關注以下兩點:
1.、確定方法需要被緩存以及他們的緩存策略
Spring從3.1開始定義了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口來統一不同的緩存技術; 並支持使用JCache(JSR-107)註解簡化我們開發。
Cache接口爲緩存的組件規範定義,包含緩存的各種操作集合。
Cache接口下Spring提供了各種xxxCache的實現;如RedisCache,EhCacheCache , ConcurrentMapCache。
每次調用需要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目標方法是否 已經被調用過;如果有就直接從緩存中獲取方法調用後的結果,如果沒有就調用方法 並緩存結果後返回給用戶。下次調用直接從緩存中獲取。
使用Spring緩存抽象時我們需要關注以下兩點:
1.確定方法需要被緩存以及他們的緩存策略
2、從緩存中讀取之前緩存存儲的數據
二.幾個重要概念&緩存註解
三.SpringBoot中Cache緩存的使用
1.引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.在配置文件中設置屬性debug=true,這樣就會打印所有的配置報告。
logging:
level:
com.szh.springboot_redis.mapper.*: debug
debug: true
2.@EnableCaching開啓緩存
@MapperScan(basePackages = {"com.szh.springboot_redis.mapper"})
@SpringBootApplication
@EnableCaching // 開啓緩存註解
public class SpringbootRedisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootRedisApplication.class, args);
}
}
3.@Cacheable緩存註解的使用 (標註在service業務層方法上)
執行流程:先執行@Cacheable註解中的getCache(String name)方法,根據name判斷ConcurrentMap中是否有此緩存,如果沒有緩存那麼創建緩存並保存數據,另外service層的方法也會執行。如果有緩存不再創建緩存,另外service層的方法也不會執行。
總結:先執行@Cacheable----->再執行service層的方法
@Cacheable註解的屬性如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
}
service層代碼
第一次查詢數據庫打印service類方法日誌,並把數據保存到Cahce中
第二次傳入相同參數不再執行service類方法,不會打印日誌,查詢的數據直接從緩存中獲取
@Service
@CacheConfig(cacheNames = "person") //將cacheNames抽取出來
public class PersonService {
@Autowired
PersonDao personDao;
/*1. @Cacheable的幾個屬性詳解:
* cacheNames/value:指定緩存組件的名字
* key:緩存數據使用的key,可以用它來指定。默認使用方法參數的值,一般不需要指定
* keyGenerator:作用和key一樣,二選一
* cacheManager和cacheResolver作用相同:指定緩存管理器,二選一
* condition:指定符合條件才緩存,比如:condition="#id>3"
* 也就是說傳入的參數id>3才緩存數據
* unless:否定緩存,當unless爲true時不緩存,可以獲取方法結果進行判斷
* sync:是否使用異步模式*/
//@Cacheable(cacheNames= "person")
//@Cacheable(cacheNames= "person",key="#id",condition="#id>3")
@Cacheable(key="#id")
public Person queryPersonById(Integer id){
System.out.println("查詢"+id+"號員工信息");
Person person=new Person();
person.setId(id);
return personDao.query(person);
}
/**
* @CachePut:即調用方法,又更新緩存數據
* 修改了數據庫中的數據,同時又更新了緩存
*
*運行時機:
* 1.先調用目標方法
* 2.將目標方法返回的結果緩存起來
*
* 測試步驟:
* 1.查詢1號的個人信息
* 2.以後查詢還是之前的結果
* 3.更新1號的個人信息
* 4.查詢一號員工返回的結果是什麼?
* 應該是更新後的員工
* 但只更新了數據庫,但沒有更新緩存是什麼原因?
* 5.如何解決緩存和數據庫同步更新?
* 這樣寫:@CachePut(cacheNames = "person",key = "#person.id")
* @CachePut(cacheNames = "person",key = "#result.id")
*/
@CachePut(key = "#result.id")
public Person updatePerson(Person person){
System.out.println("修改"+person.getId()+"號員工信息");
personDao.update(person);
return person;
}
/**
* @CacheEvict:清除緩存
* 1.key:指定要清除緩存中的某條數據
* 2.allEntries=true:刪除緩存中的所有數據
* beforeInvocation=false:默認是在方法之後執行清除緩存
* 3.beforeInvocation=true:現在是在方法執行之前執行清除緩存,
* 作用是:只清除緩存、不刪除數據庫數據
*/
//@CacheEvict(cacheNames = "person",key = "#id")
@CacheEvict(cacheNames = "person",allEntries=true)
public void deletePerson(Integer id){
System.out.println("刪除"+id+"號個人信息");
//刪除數據庫數據的同時刪除緩存數據
//personDao.delete(id);
/**
* beforeInvocation=true
* 使用在方法之前執行的好處:
* 1.如果方法出現異常,緩存依舊會被刪除
*/
//int a=1/0;
}
/**
* @Caching是 @Cacheable、@CachePut、@CacheEvict註解的組合
* 以下註解的含義:
* 1.當使用指定名字查詢數據庫後,數據保存到緩存
* 2.現在使用id、age就會直接查詢緩存,而不是查詢數據庫
*/
@Caching(
cacheable = {@Cacheable(key="#name")},
put={ @CachePut(key = "#result.id"),
@CachePut(key = "#result.age")
}
)
public Person queryPersonByName(String name){
System.out.println("查詢的姓名:"+name);
return personDao.queryByName(name);
}
}
springboot項目整合 Redis 實現緩存操作
緩存大致流程
springboot-mybatis-redis 案例
pom.xml 依賴配置:
<?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>
<!-- Spring Boot 啓動父依賴 -->
<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.szh</groupId>
<artifactId>springboot_redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_redis</name>
<!--整合 Mybatis 並使用 Redis 作爲緩存-->
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>-->
<!-- Spring Boot Reids 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.8.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml 應用配置文件,增加 Redis 相關配置
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/first?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
redis:
host: 192.168.72.129 ##Redis服務器地址
port: 6379 ## Redis服務器連接端口
database: 0 ## Redis數據庫索引(默認爲0)
logging:
level:
com.szh.springboot_redis.mapper.*: debug
debug: true
實體類這裏就不細寫了,各位小夥伴就根據自己相應的數據庫來編寫吧。
Service業務邏輯層
package com.szh.springboot_redis.service;
import com.szh.springboot_redis.pojo.Students;
import java.util.List;
public interface StudentService {
/**
* 查詢所有信息
* @return
*/
List<Students> findAllStudnet();
/**
* 刪除指定信息
* @param id
* @return
*/
int delStudent(Integer id);
/**
* 修改指定信息
* @param students
* @return
*/
int updateStudent(Students students);
/**
* 根據id查詢
* @param id
* @return
*/
Students findStudentById(Integer id);
}
業務邏輯實現類 StudnetServiceImpl .java:
package com.szh.springboot_redis.service.impl;
import com.szh.springboot_redis.mapper.StudentMapper;
import com.szh.springboot_redis.pojo.Students;
import com.szh.springboot_redis.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudnetServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private RedisTemplate redisTemplate;
/**
* 查詢所有信息
* @return
*/
@Override
public List<Students> findAllStudnet() {
String key = "student";
ValueOperations<String, List<Students>> operations = redisTemplate.opsForValue();
// 緩存存在
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
List<Students> students = operations.get(key);
return students;
}else {
// 從 DB 中獲取城市信息
List<Students> students = studentMapper.findAllStudnet();
// 插入緩存
operations.set(key, students);
return students;
}
}
/**
* 根據id查詢信息
* @param id
* @return
*/
@Override
public Students findStudentById(Integer id) {
String key = "city_" + id;
ValueOperations<String, Students> operations = redisTemplate.opsForValue();
// 緩存存在
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
Students students = operations.get(key);
return students;
}else {
// 從 DB 中獲取城市信息
Students students = studentMapper.findStudentById(id);
// 插入緩存
operations.set(key, students);
return students;
}
}
/**
* 刪除指定信息
* @param id
* @return
*/
@Override
public int delStudent(Integer id) {
int ret = studentMapper.delStudent(id);
// 緩存存在,刪除緩存
String key = "studnet_" + id;
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
redisTemplate.delete(key);
}
return ret;
}
/**
* 修改指定信息
* @param students
* @return
*/
@Override
public int updateStudent(Students students) {
int ret = studentMapper.updateStudent(students);
// 緩存存在,刪除緩存
String key = "students_" + students.getSid();
boolean hasKey = redisTemplate.hasKey(key);
if (hasKey) {
redisTemplate.delete(key);
}
return ret;
}
}
Controller層
StudentController.java類
package com.szh.springboot_redis.controller;
import com.szh.springboot_redis.pojo.Students;
import com.szh.springboot_redis.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class StudentController {
@Autowired
StudentService studentService;
/**
* 查詢所有信息
* @return
*/
@GetMapping("/select")
public List<Students> findStudents(){
List<Students> students = studentService.findAllStudnet();
return students;
}
/**
* 根據id查詢信息
* @param id
* @return
*/
@GetMapping("/select/{id}")
public Students selStudentId(@PathVariable("id") Integer id){
Students student = studentService.findStudentById(id);
return student;
}
/**
* 修改信息
* @param student
* @return
*/
@PutMapping("/update")
public int update(Students student) {
return studentService.updateStudent(student);
}
/**
* 刪除信息
* @param id
* @return
*/
@DeleteMapping("delete")
public int delete(Integer id) {
return studentService.delStudent(id);
}
}
修改springboot中redis配置中的修改RedisTemplate 默認的序列化規則,將緩存序列化。
配置config文件
RedisConfig.java類
package com.szh.springboot_redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig{
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key採用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也採用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式採用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式採用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
運行
測試
第一次查詢是用sql語句從mysql數據庫中查詢出數據
第二次直接從緩存中拿數據
redis的可視化工具Redis Desktop Manager查看
大功告成!
希望本博客可以給大家帶來幫助。
記得點關注,評論哦!