当前位置: 首页 > article >正文

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 发起请求进行数据迁移:
在这里插入图片描述
修改键名:
在这里插入图片描述


http://www.kler.cn/a/571223.html

相关文章:

  • TCP/IP四层模型:从入门到精通
  • 卡尔曼滤波算法(Kalman Filter, KF)深入推导
  • 面向对象三大特性:多态
  • Git安装部署
  • 力扣-动态规划-115 不同子序列
  • kubectl 运行脚本 kubernetes 部署springcloud微服务 yaml + Dockerfile+shell 脚本
  • 软件架构师日常工作和核心技能
  • 【Git原理与使用一】Git概念与基本操作
  • 人工智能 全部技术栈以及简单运用场景
  • GBT32960 协议编解码器的设计与实现
  • 概率论基础概念
  • leetcode 0018 四数之和-medium
  • 将 XML 文件转换为字典形式
  • AUTOSAR微控制器抽象层(MCAL)详解及综合实例
  • Docker安装Redpandata-console控制台
  • JavaWeb——MySQL-索引(3/3)-操作语法(索引操作语法概述、创建索引、查看索引、删除索引)
  • C++网络编程之Socket
  • 二分题目leetcode
  • 二百八十五、华为云PostgreSQL——建分区表并设置主键
  • 10.LED点阵实验