Redis

Redis

  1. 导入Redis的坐标(Spring Data Redis)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置Redis连接信息
## 生产环境库
sky:
  host:localhost
  port: 6379
  database: 0

##application.yml 开发环境库
# redis配置
spring:
  # redis配置
  redis:
    host: ${sky.redis.host}
    port: ${sky.redis.port}
    database: ${sky.redis.database} #指定使用哪个数据库(默认使用0号数据库)
  1. 创建配置类RedisTemplate对象,并注入到Spring容器中,用于操作Redis中的数据
package com.sky.config;

import lombok.extern.slf4j.Slf4j;
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.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Slf4j
@Configuration
public class RedisConfiguration {
    @Bean
    public <T, V> RedisTemplate<T, V> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("开始创建RedisTemplate对象...");
        RedisTemplate<T, V> redisTemplate = new RedisTemplate<>();
        //设置序列化器,默认为JdkSerializationRedisSerializer
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        //为值设置序列化器 可以处理任意类型的数据
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }
}

  1. 通过配置类创建的RedisTemplate对象,就可以操作Redis中的数据
package com.sky.text;

import com.sky.SkyApplication;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;

@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void testRedis() {
        System.out.println(redisTemplate);
        //获取字符串类型的操作对象
        ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue();
        //设置值
        valueOperations.set("name", "张三");
        //获取值
        System.out.println(valueOperations.get("name"));

        //获取列表类型的操作对象
        ListOperations<Object, Object> objectObjectListOperations = redisTemplate.opsForList();
        //添加元素
        objectObjectListOperations.leftPush("mylist", "张三");
        objectObjectListOperations.leftPush("mylist", "李四");
        objectObjectListOperations.leftPush("mylist", "王五");
        //获取集合的长度
        System.out.println(objectObjectListOperations.size("mylist"));
        //获取集合中的元素
        System.out.println(objectObjectListOperations.range("mylist", 0, -1));
        //移除集合中的元素
        objectObjectListOperations.remove("mylist", 1, "张三");

        //获取哈希类型的操作对象
        HashOperations<Object, Object, Object> objectObjectObjectHashOperations = redisTemplate.opsForHash();
        //添加元素
        objectObjectObjectHashOperations.put("myhash", "id", "1"); // 修复:将Integer改为String
        objectObjectObjectHashOperations.put("myhash", "name", "张三");
        objectObjectObjectHashOperations.put("myhash", "age", "18"); // 修复:将Integer改为String
        //获取元素
        System.out.println(objectObjectObjectHashOperations.entries("myhash"));
        //移除元素
        objectObjectObjectHashOperations.delete("myhash", "id");

        //获取集合类型的操作对象
        SetOperations<Object, Object> objectObjectSetOperations = redisTemplate.opsForSet();
        //添加元素
        objectObjectSetOperations.add("myset", "张三");
        objectObjectSetOperations.add("myset", "李四");
        objectObjectSetOperations.add("myset", "王五");
        //获取集合的长度
        System.out.println(objectObjectSetOperations.size("myset"));
        //获取集合中的元素
        System.out.println(objectObjectSetOperations.members("myset"));

        //获取有序集合类型的操作对象
        ZSetOperations<Object, Object> objectObjectZSetOperations = redisTemplate.opsForZSet();
        //添加元素
        objectObjectZSetOperations.add("myzset", "张三", 1);
        objectObjectZSetOperations.add("myzset", "李四", 2);
        objectObjectZSetOperations.add("myzset", "王五", 3);
        //获取集合的长度
        System.out.println(objectObjectZSetOperations.size("myzset"));
        //获取集合中的元素
        System.out.println(objectObjectZSetOperations.range("myzset", 0, -1));
    }
}

字符串操作

package com.sky.text;

import com.sky.SkyApplication;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;

import java.util.concurrent.TimeUnit;

@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextString() {
        //字符串操作
        ValueOperations<Object, Object> ops = redisTemplate.opsForValue();
        //设置数据
        ops.set("name1", "张三");
        //获取数据
        ops.get("name");
        //设置数据有效期 TimeUnit.SECONDS 单位(秒)
        ops.set("name2", "李四", 10, TimeUnit.SECONDS);
        //当元素不存在时设置值 并添加有效期
        ops.setIfAbsent("name3", "王五", 10, TimeUnit.SECONDS);
        //当元素存在时设置值 并添加有效期
        ops.setIfPresent("name4", "赵四", 10, TimeUnit.SECONDS);
        //删除元素 返回删除结果
        System.out.println(redisTemplate.delete("name1"));
    }
}

哈希操作

package com.sky.text;

import com.sky.SkyApplication;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;

import java.util.concurrent.TimeUnit;

@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextHash() {
        //哈希操作
        HashOperations<Object, Object, Object> ops = redisTemplate.opsForHash();
        //添加元素
        ops.put("hash1", "name", "张三");
        //批量添加元素
        ops.putAll("hash1", Map.of("name", "张三", "age", "18"));
        //获取元素
        System.out.println(ops.get("hash1", "name"));
        //获取所有元素
        System.out.println(ops.entries("hash1"));
        //获取所有的 key
        System.out.println(ops.keys("hash1"));
        //获取所有的 value
        System.out.println(ops.values("hash1"));
        //删除元素
        ops.delete("hash1", "name");
        //判断元素是否存在
        System.out.println(ops.hasKey("hash1", "name"));
        //获取元素数量
        System.out.println(ops.size("hash1"));

    }
}

列表操作

package com.sky.text;

import com.sky.SkyApplication;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;

import java.util.concurrent.TimeUnit;

@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextList() {
        //列表操作
        ListOperations<Object, Object> ops = redisTemplate.opsForList();
        //添加元素
        ops.leftPush("list1", "张三");
        ops.leftPush("list1", "李四");
        ops.leftPush("list1", "王五");
        //批量添加元素
        ops.leftPushAll("list1", "赵六", "孙七");
        //获取元素数量
        System.out.println(ops.size("list1"));
        //获取元素
        System.out.println(ops.index("list1", 0));
        System.out.println(ops.range("list1", 0, -1));
        //移除元素
        ops.remove("list1", 1, "张三");
        //从右侧 移除元素
        ops.rightPop("list1");
        //获取列表长度
        System.out.println(ops.size("list1"));
    }
}

集合操作


@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextSet() {
        //集合操作
        SetOperations<Object, Object> ops = redisTemplate.opsForSet();
        //添加元素
        ops.add("set1", "张三", "李四", "王五");
        ops.add("set2", "张三", "赵六", "孙七");
        //获取元素
        Set<Object> set1 = ops.members("set1");
        System.out.println(set1);
        //获取元素数量
        Long setSize = ops.size("set1");
        System.out.println(setSize);
        //计算集合交集
        Set<Object> intersection = ops.intersect("set1", "set2");
        System.out.println(intersection);
        //计算集合并集
        Set<Object> union = ops.union("set1", "set2");
        System.out.println(union);
        //计算集合差集
        Set<Object> difference = ops.difference("set1", "set2");
        System.out.println(difference);
        //移除元素
        ops.remove("set1", "张三");

    }
}

有序集合操作


@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextZSet() {
        //有序集合操作
        ZSetOperations<Object, Object> ops = redisTemplate.opsForZSet();
        //添加元素
        ops.add("zset1", "张三", 1);
        ops.add("zset1", "李四", 2);
        ops.add("zset1", "王五", 3);
        ops.add("zset1", "赵六", 4);
        ops.add("zset1", "孙七", 5);
        //获取元素数量
        System.out.println(ops.size("zset1"));
        //获取元素
        System.out.println(ops.range("zset1", 0, -1));
        //获取元素分数
        System.out.println(ops.score("zset1", "张三"));
        //移除元素
        ops.remove("zset1", "张三");
    }
}

通用命令操作


@SpringBootTest(classes = SkyApplication.class)
@RequiredArgsConstructor
public class SpringBootDataRedisText {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Test
    public void TextCommon() {
        //通用命令操作
        //查询元素
        Set<Object> keys = redisTemplate.keys("*");
        System.out.println(keys);
        System.out.println(redisTemplate.hasKey("name"));
        //获取数据类型
        System.out.println(redisTemplate.type("name"));
        //删除元素
        System.out.println(redisTemplate.delete("name"));
    }
}

使用redis缓存热数据

苍穹外卖缓存菜品数据,减少数据库查询操作

  • 每个分类下的菜品保存一份缓存数据
  • 数据库中菜品数据有变更时及时清除缓存数据

@Service
@RequiredArgsConstructor
@Slf4j
public class DishServiceImpi extends ServiceImpl<DishMapper, Dish> implements DishService {
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public List<DishVO> listDishVOByCategoryId(Long categoryId) {
        //判断是否有缓存
        String key = "dish_" + categoryId;
        List<DishVO> dishVOList = (List<DishVO>) redisTemplate.opsForValue().get(key);
        log.info("缓存命中");
        if (dishVOList != null) return dishVOList;
        //获取菜品数据
        List<Dish> list = this.listByCategoryId(categoryId);
        dishVOList = list.stream().map(item -> {
            DishVO dishVO = new DishVO(item);
            dishVO.setFlavors(dishFlavorService.lambdaQuery().eq(DishFlavor::getDishId, item.getId()).list());
            return dishVO;
        }).toList();
        log.info("缓存未命中");
        redisTemplate.opsForValue().set(key, dishVOList);
        return dishVOList;
    }
}

通过spring Cache缓存数据

  • spring Cache是一个框架。实现了基于注解的缓存功能。只需要简单的加一个注解,就能实现缓存的功能

切换不同的缓存

spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如

  • ehcache: 基于ehcachejava内存的缓存
  • redis: 基于redis内存的缓存
  • caffeine: 基于caffeine内存的缓存 当前实现默认使用的是基于redis的缓存实现
  • 当导入redis依赖包时,会默认使用基于redis的缓存实现
  1. 导入spring Cache依赖的坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.3</version>
</dependency>

spring Cache常用注解

注解说明位置
@EnableCaching开启缓存注解功能,通常加在启动类上启动类
@Cacheable在方法执行前先查询缓存是否有数据,如果有数据,则返缓存数据,如果没有缓存数据,调用方法并将方法返回值存放到缓存中方法中
@CachePut将方法的返回值放入到缓存中(仅仅放入缓存)方法中
@CacheEvict将一条或多条数据从缓存中删除方法中

@EnableCaching启动类加入


@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
@EnableCaching//开启缓存注解功能
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

@CachePut@Cacheable 缓存数据

注意

@CachePut仅仅放入缓存,不会返回数据 @Cacheable 在方法执行前先查询缓存是否有数据,如果有数据,则返回缓存数据,如果没有缓存数据,调用方法并将方法返回值存放到缓存中

格式:@CachePut(value = "cacheName", key = "cacheKey")

  • value: 缓存名称
  • key: 缓存的key
  • 缓存数据时valuekey共同构成了缓存的key值 实际生成的key为:value::key

动态生成key

  • key值可以使用表达式动态的生成key
  1. 形参导航:使用形参的属性作为缓存的key
  • 使用#参数名语法,获取形参值

@CachePut(value = "dish", key = "#dish.id")//使用形参中的id作为缓存的key 语法为 #参数
@Cacheable(value = "dish", key = "#dish.id")

public Dish update(Dish dish) {
}
  1. 对象导航:使用返回值的属性作为缓存的key

注意

@Cacheable无此类语法

  • 原因:@Cacheable首先缓存中是否有数据 在此时并没有返回值 也就获取不到key

#result取返回值 该示例中#resultDish对象


@CachePut(value = "dish", key = "#result.id")
public Dish update(Dish dish) {
}
  1. 索引导航:使用参数的索引值作为缓存的key
  • 使用#p+index语法,获取第index个形参值
  • 使用#p0表示第一个形参

@CachePut(value = "dish", key = "#p0")
@Cacheable(value = "dish", key = "#p0")
public Dish update(Dish dish) {
}

// 使用`#root.args[index]`语法,获取第`index`个形参值
@CachePut(value = "dish", key = "#root.args[0]")
@Cacheable(value = "dish", key = "#root.args[0]")
public Dish update(Dish dish) {
}

@CacheEvict删除缓存数据

格式:@CacheEvict(value = "cacheName", key = "cacheKey", allEntries = boolean)

  • value: 缓存名称
  • key: 缓存的key
  • 删除缓存数据时valuekey共同构成了缓存的key
  • 实际生成的key为:value::key
  • allEntries: 是否删除所有缓存数据
  1. 形参导航 删除指定缓存数据

@CacheEvict(value = "dish", key = "#id")
public void delete(Long id) {
}
  1. 对象导航 删除指定缓存数据

@CacheEvict(value = "dish", key = "#result.id")
public Dish update(Dish dish) {
}
  1. 索引导航 删除指定缓存数据

@CacheEvict(value = "dish", key = "#p0")
public Dish update(Dish dish) {
}

@CacheEvict(value = "dish", key = "#root.args[0]")
public Dish update(Dish dish) {
}

//删除所有以"dish"开头的缓存数据
@CacheEvict(value = "dish", allEntries = true)
public void deleteAll() {
}
上次更新 2025/11/30 18:15:43