springboot切换redis DB

需求描述

由于项目分为多个模块,每个模块使用同一个redis的不同DB,有时需要在不同的模块使用相同的缓存,一开始想到的策略是通过模块间通讯的方式,将需要同步的缓存内容发送到对应模块,有相应的模块进行处理;但是这样的的方式不太面向对象,于是考虑使用同一模块切换DB进行操作。

查找解决方案

说明:目前只针对手动切换,没有做多DB自动切换策略。自动切换DB后期实践后在进行补充

网上找了很多资料,这方面的资料也很多,解决方案都基本一致:
获取到RedisConnectionFactory,在factory中设置DB,然后重连实现切换DB,实现逻辑如下:

@Autowired
private RedisTemplate redisTemplate;

@Test
public void testUpdateDB() {
    LettuceConnectionFactory connectionFactory  = (LettuceConnectionFactory ) redisTemplate.getConnectionFactory();
    int database = connectionFactory .getDatabase();
    log.info("curr redis template db: {}", database);
    log.info("set cf before keys: \n{}", redisTemplate.keys("*"));

    connectionFactory.setDatabase(7);
    redisTemplate.setConnectionFactory(connectionFactory);
    connectionFactory.resetConnection();
    log.info("changed redis template db: {}", cf1.getDatabase());
    log.info("set cf after keys: \n{}", redisTemplate.keys("*"));
}

但结果并不理想,切换前后输出的DB确实变了,但是查询到的key值还是切换前的,也就是切换后其实并没重连接。
网上找了很多资料,说是这样做只有某个版本的才可以这样操作,其他的切换不了。尝试各种办法,比如:
redisTemplate.setConnectionFactory(connectionFactory); 方法后调用redisTemplate.afterPropertiesSet(); 依然无效。于是有继续找资料。最终看到一篇博客(喝水不忘挖井人),解决了不重连的问题:

@Autowired
private RedisTemplate redisTemplate;

@Test
public void testUpdateDB() {
    LettuceConnectionFactory connectionFactory  = (LettuceConnectionFactory ) redisTemplate.getConnectionFactory();
    int database = connectionFactory .getDatabase();
    log.info("curr redis template db: {}", database);
    log.info("set cf before keys: \n{}", redisTemplate.keys("*"));

    connectionFactory.setDatabase(7);
    connectionFactory.afterPropertiesSet(); // TODO 注意这行代码的加入
    cconnectionFactory.resetConnection();
    log.info("changed redis template db: {}", cf1.getDatabase());
    log.info("set cf after keys: \n{}", redisTemplate.keys("*"));
}

于是找到了问题原因:需要调用connectionFactory.afterPropertiesSet(); 方法手动触发factory刷新重新初始化后才能真正切换到新的DB。

一个优化小技巧

真实的业务场景,其实是通用redis+数据库实现序列编号的自动计算:每次申请序列编号时,不确保一定被使用,于是使用数据库持久化记录当前被使用的最后一个编号,然后将未使用的序列编号存入redis,下次申请时,先看redis中是否有编号,有则直接返回缓存的编号,当申请者确认使用该编号后移除缓存;没有创建新的编号,并存入reids。

但是这样有个问题,使用编号者不讲武德,没使用掉他也进行了确认使用的操作,导致需要回滚编号。但是回滚编号需要将数据库和redis缓存都进行处理,但是项目不运行以任何方式(客户端或者命令行方式)进行redis操作,于是只能通过在程序中通过接口的方式进行处理。

但是考虑到项目有多个模块,都分别使用了不同的DB,并且其他模块没有对外的GUI界面,于是考虑在当前模块中去实现跨DB进行处理。于是得考虑 操作完后需要将DB恢复到切换前的DB上

上代码

@RestController
@RequestMapping("/monitor/cache")
public class CacheController {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /** 切换redis数据库,并返回切换前的DB */
    private Integer changeDb(Integer dbIndex) {
        if (dbIndex == null) return null;
        LettuceConnectionFactory connectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory();
        if (connectionFactory == null) {
            throw new RuntimeException("redis未连接到服务器");
        }
        Integer orgDB = connectionFactory.getDatabase();
        if (!orgDB.equals(dbIndex)) {
            connectionFactory.setDatabase(dbIndex);
            connectionFactory.afterPropertiesSet();
            connectionFactory.resetConnection();
        }
        return orgDB;
    }

	/** 查询指定DB下的所有key */
    @GetMapping("/all/keys")
    public AjaxResult getKeys(String pattern, Integer dbIndex) {
        Integer orgDB = changeDb(dbIndex);// 切换到指定DB,并记录原DB
        if (ObjectUtils.isEmpty(pattern)) {
            pattern = "*";
        }
        Set<String> keys = redisTemplate.keys(pattern);
        changeDb(orgDB);// 操作结束,切换会原DB
        return AjaxResult.success(new TreeSet<>(keys == null ? new ArrayList<>() : keys));
    }

	/** 删除指定DB中的指定键值对 */
    @PostMapping("/del/key")
    public AjaxResult getDbKeys(@RequestBody CacheRemoveDTO dto) {
        Integer orgDB = changeDb(dto.getDbIndex());
        Long del = redisTemplate.delete(dto.getKey());
        changeDb(orgDB);

        return AjaxResult.success(del);
    }

CacheRemoveDTO 代码如下:

/**
 * redis缓存删除DTO
 *
 * @author pengyl
 * @date 2024/7/1 14:56
 * @description redis缓存删除DTO
 */
@Getter
@Setter
public class CacheRemoveDTO {
    @NotEmpty(message = "缓存key不能为空")
    private List<String> key; // 一次可删除多个Key
    private Integer dbIndex;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值