Redis
Redis
- 导入
Redis的坐标(Spring Data Redis)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置
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号数据库)
- 创建配置类
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;
}
}
- 通过配置类创建的
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的缓存实现
- 导入
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- 缓存数据时
value和key共同构成了缓存的key值 实际生成的key为:value::key
动态生成key
key值可以使用表达式动态的生成key
- 形参导航:使用形参的属性作为缓存的
key
- 使用
#参数名语法,获取形参值
@CachePut(value = "dish", key = "#dish.id")//使用形参中的id作为缓存的key 语法为 #参数
@Cacheable(value = "dish", key = "#dish.id")
public Dish update(Dish dish) {
}
- 对象导航:使用返回值的属性作为缓存的
key
注意
@Cacheable无此类语法
- 原因:
@Cacheable首先缓存中是否有数据 在此时并没有返回值 也就获取不到key值
#result取返回值 该示例中#result为Dish对象
@CachePut(value = "dish", key = "#result.id")
public Dish update(Dish dish) {
}
- 索引导航:使用参数的索引值作为缓存的
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- 删除缓存数据时
value和key共同构成了缓存的key值 - 实际生成的
key为:value::key allEntries: 是否删除所有缓存数据
- 形参导航 删除指定缓存数据
@CacheEvict(value = "dish", key = "#id")
public void delete(Long id) {
}
- 对象导航 删除指定缓存数据
@CacheEvict(value = "dish", key = "#result.id")
public Dish update(Dish dish) {
}
- 索引导航 删除指定缓存数据
@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() {
}