SpringBoot + redisTemplate 实现 redis 数据库迁移、键名修改

目录

在 Spring Boot 项目中使用RedisTemplate实现:
Redis 数据库迁移 ,本质上就是将一个 Redis 数据库中的数据复制到另一个 Redis 数据库中。
Redis 修改键名 ,实际上 Redis 并没有直接提供修改键名的方法,但可以通过先复制原键的值到新键,然后删除原键的方式来间接实现键的修改

以下是详细的实现步骤:

1.项目依赖

首先,确保你的 Spring Boot 项目中已经添加了 Spring Data Redis 依赖。

在pom.xml中添加以下依赖:

xml 复制代码
<!--redis依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.4.6</version>
</dependency>

2.动态创建 RedisTemplate实例

创建一个工具类,用于根据传入的数据库配置信息动态创建 RedisTemplate 实例。

java 复制代码
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操作服务类

java 复制代码
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.创建控制器

创建一个控制器类,用于接收客户端传递的数据库配置信息,并进行数据迁移、修改键名的操作。

java 复制代码
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 发起请求进行数据迁移:

修改键名:

相关推荐
广州灵眸科技有限公司22 分钟前
xfce桌面触摸校准:基于灵眸科技EASY-EAl-Orin-Nano
数据库·windows·科技
腾渊信息科技公司2 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
憧憬成为web高手3 小时前
皮卡丘靶场速通--sql 2
数据库·sql·mybatis
段一凡-华北理工大学3 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
二宝哥4 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
oradh5 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
隔窗听雨眠6 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z123456789866 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD6 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
java_logo6 小时前
Docker Compose 部署 ClickHouse:快速搭建高性能列式数据库
数据库·clickhouse·docker·列式数据库·轩辕镜像·高性能数据库·clickhouse部署教程