本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时,使用到了 Pipeline 功能,并对此做出了整理。
一、Redis Pipeline 是什么
Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能,性能提升的原因在于可以批量执行命令。当我们在存储数据时,会遇到批量存储的情况,在这种情况下,Pipeline 可以很好的处理,它可以是减少网络往返次数,从而显著提高 Redis 操作的性能。
这种情况例如:聊天系统中要统计每个用户的最后的状态,在这个情况下,用户数是一个很大的基体,每秒中会有很多的用户状态变化,变化的过程依赖最后一次使用状态,这就造成了批量的效果。
Redis Pipeline 是一种将多个命令打包发送到 Redis 服务器的技术,避免了逐条发送命令的网络延迟问题。通过 Pipeline,客户端可以一次性发送多个命令,服务器依次处理这些命令并将结果批量返回。
注意:
- 事务性:Pipeline 并不自动开启事务,它只是将多个命令打包发送,可以结合 Redis 的事务功能。
- 错误处理:在 Pipeline 中,如果某个命令失败,其他命令仍然会继续执行,需要在代码中处理可能的异常。
二、如何在 SpringBoot 使用
Spring Data Redis 提供了对 Pipeline 的支持,可以通过 RedisTemplate
的 executePipelined
方法实现。
如何在 SpringBoot 创建并连接 Redis 可参考该文章:SpringBoot 框架关于如何创建并使用 Redis 的详细介绍
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
默认情况下,Spring Boot 使用 Lettuce 作为 Redis 客户端,创建一个配置类来定义 RedisTemplate,并设置序列化器。
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer()); // 设置键的序列化器
template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 设置值的序列化器
return template;
}
}
创建一个服务类来封装 Pipeline 操作:
@Service
public class RedisPipelineService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void executePipeline() {
// 使用 SessionCallback 来实现 Pipeline
List<Object> results = redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) {
// 在此处添加多个命令到 Pipeline 中
operations.opsForValue().set("key1", "value1");
operations.opsForValue().set("key2", "value2");
operations.opsForValue().set("key3", "value3");
return null;
}
});
// 获取执行结果
System.out.println("Pipeline 执行结果: " + results);
}
}
在控制器中调用 Pipeline 方法。
@RestController
public class RedisController {
@Autowired
private RedisPipelineService redisPipelineService;
@GetMapping("/testPipeline")
public String testPipeline() {
redisPipelineService.executePipeline();
return "Pipeline 执行已完成!";
}
}
通过以上的步骤就可以做到简单的使用Redis 的 Pipeline 功能,面对复杂业务,其实也就是数据的键和值之间的调整,本质还是简单方法的调用。
三、实战示例
业务:将用户的最后一次状态存入缓存,例如最后一次状态信息是上线还是下线。需要将一个 LIst<User>
数组批量存入到缓存中,这个数组就是通过某些组件得到,例如 MQ 队列,数组元素是 User
对象,含有最后一次状态的数据。缓存中 key
是 userId
,我现在将这个数组使用 Pipeline
存入缓存,规则是缓存中如果存在就更新缓存,没有就新加缓存。
1、定义 User 类
@Data
public class User {
private String userId;
private String name;
private int status;
}
2、配置 RedisTemplate 类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置键的序列化器
template.setKeySerializer(new StringRedisSerializer());
// 设置值的序列化器(使用 JSON 序列化器,方便存储复杂对象)
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
2、实现 Pipeline 存储逻辑
@Service
public class RedisUserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 使用 Pipeline 将 List<User> 存入 Redis
* @param users 用户列表
*/
public void saveUsersWithPipeline(List<User> users) {
// 使用 executePipelined 方法执行 Pipeline 操作
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
// 遍历用户列表,将每个用户对象存入 Redis
for (User user : users) {
String key = user.getUserId();
// 这里可以添加其他业务,例如根据用户的上一次状态确定是否修改存储
operations.opsForValue().set(key, user); // 如果键存在则更新,不存在则新增
}
return null;
}
});
}
}
3、使用方法
@RestController
public class RedisController {
@Autowired
private RedisUserService redisUserService;
@GetMapping("/saveUsers")
public String saveUsers() {
// 创建一个用户列表
List<User> users = new ArrayList<>();
User user1 = new User();
user1.setUserId("1");
user1.setName("Alice");
user1.setStatus(0);
User user2 = new User();
user2.setUserId("2");
user2.setName("Bob");
user2.setStatus(1);
users.add(user1);
users.add(user2);
// 使用 Pipeline 存储用户列表
redisUserService.saveUsersWithPipeline(users);
return "用户列表已成功存入 Redis!";
}
}
通过上述代码,实现了使用 Spring Data Redis
的 executePipelined
方法将一个 List<User>
数组通过 Pipeline
存入 Redis
。每个 User
对象的 userId
作为键,User
对象作为值。如果键存在则更新,不存在则新增。这种方式不仅提高了性能,还简化了批量操作的代码逻辑。