docker安裝redis連接redis可視化工具Redis Desktop Manager+springboot項目使用Cache緩存+springboot項目整合 Redis 實現緩存

今天進一步學習了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查看
在這裏插入圖片描述
大功告成!
希望本博客可以給大家帶來幫助。
記得點關注,評論哦!

發佈了41 篇原創文章 · 獲贊 136 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章