SpringBoot + redisTemplate 实现 redis 数据库迁移、键名修改
目录
- 1.项目依赖
- 2.动态创建 RedisTemplate实例
- 3.创建Redis操作服务类
- 4.创建控制器
- 5.测试
在 Spring Boot 项目中使用RedisTemplate实现:
Redis 数据库迁移
,本质上就是将一个 Redis 数据库中的数据复制到另一个 Redis 数据库中。
Redis 修改键名
,实际上 Redis 并没有直接提供修改键名的方法,但可以通过先复制原键的值到新键,然后删除原键的方式来间接实现键的修改
以下是详细的实现步骤:
1.项目依赖
首先,确保你的 Spring Boot 项目中已经添加了 Spring Data Redis 依赖。
在pom.xml中添加以下依赖:
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.6</version>
</dependency>
2.动态创建 RedisTemplate实例
创建一个工具类,用于根据传入的数据库配置信息动态创建 RedisTemplate 实例。
package com.example.redisdemo.utils;
import io.micrometer.common.util.StringUtils;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
/**
* redis工具类:根据传入的数据库配置信息动态创建 RedisTemplate 实例
* @author qzz
* @date 2025/3/4
*/
@Component
public class RedisTemplateFactory {
private String REDIS_HOST = "localhost";
private Integer REDIS_PORT = 6379;
private String REDIS_PASSWORD = "";
/**
* 根据传入的数据库配置信息动态创建 RedisTemplate 实例
* @param database
* @return
*/
public RedisTemplate<String, Object> createRedisTemplate(int database) {
return createRedisTemplate(REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, database);
}
/**
* 根据传入的数据库配置信息动态创建 RedisTemplate 实例
* @param host
* @param port
* @param password
* @param database
* @return
*/
public RedisTemplate<String, Object> createRedisTemplate(String host, int port, String password, int database) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
if(StringUtils.isNotEmpty(password)){
config.setPassword(password);
}
config.setDatabase(database);
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(config);
connectionFactory.afterPropertiesSet();
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 设置键的序列化器
template.setKeySerializer(new StringRedisSerializer());
// 设置值的序列化器
template.setValueSerializer(new StringRedisSerializer());
// 设置哈希键的序列化器
template.setHashKeySerializer(new StringRedisSerializer());
// 设置哈希值的序列化器
template.setHashValueSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
3.创建Redis操作服务类
package com.example.redisdemo.service.impl;
import com.example.redisdemo.model.ResultJson;
import com.example.redisdemo.request.RedisDBRequestJson;
import com.example.redisdemo.request.RedisKeyRequestJson;
import com.example.redisdemo.service.IRedisOperationService;
import com.example.redisdemo.utils.RedisTemplateFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* redis 迁移数据库、修改键名操作
* @author qzz
* @date 2025/3/3
*/
@Slf4j
@Service
public class RedisOperationServiceImpl implements IRedisOperationService {
@Autowired
private RedisTemplateFactory redisTemplateFactory;
/**
* redis数据库迁移 --- 数据库只动态修改 database
* @param redisDBRequestJson
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ResultJson transferRedisDB(RedisDBRequestJson redisDBRequestJson) {
try {
//redis源数据库
RedisTemplate<String, Object> sourceRedisTemplate = redisTemplateFactory.createRedisTemplate(redisDBRequestJson.getSourceDatabase());
//redis目标数据库
RedisTemplate<String, Object> targetRedisTemplate = redisTemplateFactory.createRedisTemplate(redisDBRequestJson.getTargetDatabase());
// 获取源数据库中的所有键
Set<String> keys = sourceRedisTemplate.keys("*");
if (keys != null) {
for (String key : keys) {
//复制键及其值 到 目标RedisTemplate
copyKey(key, sourceRedisTemplate, targetRedisTemplate);
}
}
}catch (Exception e){
log.error("Redis数据库迁移失败", e);
return ResultJson.fail(500,"Redis数据库迁移失败");
}
return ResultJson.success();
}
/**
* 复制键及其值 到 目标RedisTemplate
* @param sourceKey 源键
* @param sourceRedisTemplate 源RedisTemplate
* @param targetRedisTemplate 目标RedisTemplate
*/
private static void copyKey(String sourceKey, RedisTemplate<String, Object> sourceRedisTemplate, RedisTemplate<String, Object> targetRedisTemplate) {
copyKey(sourceKey, sourceKey, sourceRedisTemplate, targetRedisTemplate);
}
/**
* 复制键及其值 到 目标RedisTemplate,并可以指定目标键名
* @param sourceKey 源键
* @param targetKey 目标键
* @param sourceRedisTemplate 源RedisTemplate
* @param targetRedisTemplate 目标RedisTemplate
*/
private static void copyKey(String sourceKey, String targetKey, RedisTemplate<String, Object> sourceRedisTemplate, RedisTemplate<String, Object> targetRedisTemplate) {
// 获取键的类型
String type = sourceRedisTemplate.type(sourceKey).code();
switch (type){
case "string":
//处理字符串类型的值
String value = (String) sourceRedisTemplate.opsForValue().get(sourceKey);
targetRedisTemplate.opsForValue().set(targetKey, value);
break;
case "list":
Long listSize = sourceRedisTemplate.opsForList().size(sourceKey);
for (int i = 0; i < listSize; i++) {
String listValue = (String) sourceRedisTemplate.opsForList().index(sourceKey, i);
targetRedisTemplate.opsForList().rightPush(targetKey, listValue);
}
break;
case "set":
// 处理集合类型的数据
Set<Object> setMembers = sourceRedisTemplate.opsForSet().members(sourceKey);
if (setMembers != null) {
for (Object member : setMembers) {
targetRedisTemplate.opsForSet().add(targetKey, member);
}
}
break;
case "zset":
// 处理有序集合类型的数据
Set<Object> zsetMembers = sourceRedisTemplate.opsForZSet().range(sourceKey, 0, -1);
if (zsetMembers != null) {
for (Object member : zsetMembers) {
Double score = sourceRedisTemplate.opsForZSet().score(sourceKey, member);
targetRedisTemplate.opsForZSet().add(targetKey, member, score);
}
}
break;
case "hash":
// 处理哈希类型的数据
Map<Object, Object> hashEntries = sourceRedisTemplate.opsForHash().entries(sourceKey);
if (!hashEntries.isEmpty()) {
targetRedisTemplate.opsForHash().putAll(targetKey, hashEntries);
}
break;
default:
System.out.println("Unsupported data type: " + type);
}
}
/**
* redis 修改键名: 实现 Redis 键的修改,实际上 Redis 并没有直接提供修改键名的方法,但可以通过先复制原键的值到新键,然后删除原键的方式来间接实现键的修改
* @param redisDBRequestJson
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ResultJson updateRedisKey(RedisDBRequestJson redisDBRequestJson) {
try {
//redis目标数据库
RedisTemplate<String, Object> targetRedisTemplate = redisTemplateFactory.createRedisTemplate(redisDBRequestJson.getTargetDatabase());
//遍历key列表
for (RedisKeyRequestJson redisKey : redisDBRequestJson.getRedisKeyList()) {
if (targetRedisTemplate.hasKey(redisKey.getSourceKey())) {
String sourceKey = redisKey.getSourceKey();
String targetKey = redisKey.getTargetKey();
//1.复制键及其值 到新键
copyKey(sourceKey, targetKey, targetRedisTemplate, targetRedisTemplate);
//2.获取原键的过期时间
Long expire = targetRedisTemplate.getExpire(sourceKey, TimeUnit.SECONDS);
if (expire != null && expire > 0) {
// 设置新键的过期时间
targetRedisTemplate.expire(targetKey, expire, TimeUnit.SECONDS);
}
//3.删除原键
targetRedisTemplate.delete(sourceKey);
}
}
}catch (Exception e){
log.error("Redis 修改键名失败", e);
return ResultJson.fail(500,"Redis 修改键名失败");
}
return ResultJson.success();
}
/**
* redis数据库迁移 --- 数据库动态修改 host、password、database
* @param requestJson
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ResultJson transferRedisDBByDynamics(RedisDBRequestJson requestJson) {
try {
//redis源数据库
RedisTemplate<String, Object> sourceRedisTemplate = redisTemplateFactory.createRedisTemplate(requestJson.getSourceHost(), requestJson.getSourcePort(), requestJson.getSourcePassword(), requestJson.getSourceDatabase());
//redis目标数据库
RedisTemplate<String, Object> targetRedisTemplate = redisTemplateFactory.createRedisTemplate(requestJson.getTargetHost(), requestJson.getTargetPort(), requestJson.getTargetPassword(), requestJson.getTargetDatabase());
// 获取源数据库中的所有键
Set<String> keys = sourceRedisTemplate.keys("*");
if (keys != null) {
for (String key : keys) {
//复制键及其值 到 目标RedisTemplate
copyKey(key, sourceRedisTemplate, targetRedisTemplate);
}
}
}catch (Exception e){
log.error("Redis数据库迁移失败", e);
return ResultJson.fail(500,"Redis数据库迁移失败");
}
return ResultJson.success();
}
}
4.创建控制器
创建一个控制器类,用于接收客户端传递的数据库配置信息,并进行数据迁移、修改键名的操作。
package com.example.redisdemo.controller;
import com.example.redisdemo.model.ResultJson;
import com.example.redisdemo.request.RedisDBRequestJson;
import com.example.redisdemo.service.IRedisOperationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* redis 迁移数据库、修改键名操作
* @author qzz
* @date 2025/3/3
*/
@RestController
public class RedisController {
@Autowired
private IRedisOperationService operationService;
/**
* redis数据库迁移 --- 数据库只动态修改 database
* @return
*/
@PostMapping("/api/{version}/redis-migration/")
public ResultJson transferRedisDB(@RequestBody RedisDBRequestJson redisDBRequestJson) {
return operationService.transferRedisDB(redisDBRequestJson);
}
/**
* redis 修改键名
* @return
*/
@PostMapping("/api/{version}/redis/rename-key/")
public ResultJson updateRedisKey(@RequestBody RedisDBRequestJson redisDBRequestJson) {
return operationService.updateRedisKey(redisDBRequestJson);
}
/**
* redis数据库迁移 --- 数据库动态修改 host、password、database
* @return
*/
@PostMapping("/api/{version}/redis-migration/dynamics/")
public ResultJson transferRedisDBByDynamics(@RequestBody RedisDBRequestJson redisDBRequestJson) {
return operationService.transferRedisDBByDynamics(redisDBRequestJson);
}
}
5.测试
启动 Spring Boot 应用后,可以通过以下 URL 发起请求进行数据迁移:
修改键名: