您的位置:首页 > 运维架构 > Docker

docker安装redis连接redis可视化工具Redis Desktop Manager+springboot项目使用Cache缓存+springboot项目整合 Redis 实现缓存

2019-06-11 11:03 1366 查看

今天进一步学习了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查看

大功告成!
希望本博客可以给大家带来帮助。
记得点关注,评论哦!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: