目录
- 效果展示
- 需求场景
- 配置文件编写
- 属性类封装
- 多数据源 Redis 配置
- 工具类封装
- 业务调用示例
- 总结
在实际开发中,随着业务复杂度提升,我们常常会遇到 Redis 需要按不同业务模块(如认证、聊天等)分库管理的需求。这样做可以实现数据隔离、便于运维和扩展。本文将以 Spring Boot 为例,手把手教你如何优雅地实现 Redis 多数据库(多数据源)切换。
效果展示
这里只是范例,大家拿到我的实现即可集成到项目中
接口Auth和Chat使用了不同的redis库
配置了不同的redis库
Auth
发送请求并拿到redis的键值
并出现在redis数据库1中
正常拿到数据
Chat
发送请求并拿到redis的键值
并出现在redis数据库2中
正常拿到数据
实现了不同的redis数据库
需求场景
假设我们有如下需求:
- auth 业务使用 Redis 的第 1 号数据库(database=1)
- chat 业务使用 Redis 的第 2 号数据库(database=2)
- 还有一个默认 Redis(database=0)供其他业务使用
我们希望通过配置和代码实现,能够灵活地在不同 Redis 数据库间切换和操作。
配置文件编写
首先,在 application-redis.yml
中分别配置不同的 Redis 数据源:
spring: data: redis: host: localhost port: 6379 database: 0 # 默认 Redis,JWT 用 auth-redis: host: localhost port: 6379 database: 1 # 默认 Redis,JWT 用 chat-redis: host: localhost port: 6379 database: 2 # 用于 ChatHistory
属性类封装
为每个自定义 Redis 数据源编写属性类,方便后续自动注入:
AuthRedisProperties.Java
package com.anfioo.common.bean; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "spring.data.auth-redis") @Data public class AuthRedisProperties { private String host; private int port; private int database; }
ChatRedisProperties.java
package com.anfioo.common.bean; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "spring.data.chat-redis") @Data public class ChatRedisProperties { private String host; private int port; private int database; }
多数据源 Redis 配置
核心配置类如下:
RedisConfig.java
package com.anfioo.admin.config; import com.anfioo.common.bean.AuthRedisProperties; import com.anfioo.common.bean.ChatRedisProperties; import com.fasterXML.jackson.annotation.jsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import com.fasterxml.jackson.datatype.jsr310AMQbqjszlT.JavaTimeModule; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; 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.core.script.DefaultRedisScript; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; /** * Redis配置类,启用Spring Cache并定义多个Redis实例和模板 */ @Configuration @EnableCaching public class RedisConfig { // ======================= 默认 Redis (database=0) ======================= /** * 创建默认Redis连接工厂 * @param redisProperties Redis属性配置 * @return LettuceConnectionFactory实例 */ @Primary @Bean(name = "defaultRedisConnectionFactory") public LettuceConnectionFactory defaultRedisConnectionFactory(RedisProperties redisProperties) { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); config.setHostName(redisProperties.getHost()); config.setPort(redisProperties.getPort()); config.setDatabase(redisProperties.getDatabase()); return new LettuceConnectionFactory(config); } /** * 创建默认Redis模板 * @param factory Redis连接工厂 * @return RedisTemplate实例 */ @Primary @Bean(name = "defaultRedisTemplate") public RedisTemplate<Object, Object> defaultRedisTemplate( @Qualifier("defaultRedisConnectionFactory") RedisConnectionFactory factory) { return createRedisTemplate(factory); } /** * Spring Cache使用的CacheManager(绑定 defaultRedis database=0) * @param factory Redis连接工厂 * @return CacheManager实例 */ @Primary @Bean public CacheManager cacheManager(@Qualifier("defaultRedisConnectionFactory") RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( new Jackson2JsonRedisSerializer<>(Object.class))); return RedisCacheManager.builder(factory).cacheDefaults(config).build(); } // ======================= Auth Redis (database=1) ======================= /** * 创建Auth Redis连接工厂 * @param properties Auth Redis属性配置 * @return LettuceConnectionFactory实例 */ @Bean(name = "authRedisConnectionFactory") public LettuceConnectionFactory authRedisConnectionFactory(AuthRedisProperties properties) { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); config.setHostName(properties.getHost()); config.setPort(properties.getPort()); config.setDatabase(properties.getDatabase()); return new LettuceConnectionFactory(config); } /** * 创建Auth Redis模板 * @param factory Redis连接工厂 * @return RedisTemplate实例 */ @Bean(name = "authRedisTemplate") public RedisTemplate<Object, Object> authRedisTemplate( @Qualifier("authRedisConnectionFactory") RedisConnectionFactory factory) { return createRedisTemplate(factory); } // ======================= Chat Redis (database=2) ======================= /** * 创建Chat Redis连接工厂 * @param properties Chat Redis属性配置 * @return LettuceConnectionFactory实例 */ @Bean(name = "chatRedisConnectionFactory") public LettuceConnectionFactory chatRedisConnectionFactory(ChatRedisProperties properties) { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); config.setHostName(properties.getHost()); config.setPort(properties.getPort()); config.setDatabase(properties.getDatabase()); return new LettuceConnectionFactory(config); } /** * 创建Chat Redis模板 * @param factory Redis连接工厂 * @return RedisTemplate实例 */ @Bean(name = "chatRedisTemplate") public RedisTemplate<Object, Object> chatRedisTemplate( @Qualifier("chatRedisConnectionFactory") RedisConnectionFactory factory) { return createRedisTemplate(factory); } // ======================= 公共 RedisTemplate 构造方法 ======================= /** * 创建RedisTemplate实例 * @param connectionFactory Redis连接工厂 * @return RedisTemplate实例 */ private RedisTemplate<Object, Object> createRedisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); objectMapper.registerModule(new JavaTimeModule()); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(jacksonSerializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jacksonSerializer); template.afterPropertiesSet(); return template; } // ======================= 限流 Lua 脚本 ======================= /** * 创建限流Lua脚本 * @return DefaultRedisScript实例 */ @Bean public DefaultRedisScript<Long> limitScript() { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(limitScriptText()); redisScript.setResultType(Long.class); return redisScript; } /** * 限流Lua脚本文本 * @return Lua脚本字符串 */ private String limitScriptText() { return "local key = KEYS[1]\n" + "local count = tonumber(ARGV[1])\n" + "local time = tonumber(ARGV[2])\n" + "local current = redis.call('get', key);\n" + "if current and tonumber(current) > count then\n" + " return tonumber(current);\n" + "end\n" + "current = redis.call('incr', key)\n" + "if tonumber(current) == 1 then\n" + " redis.call('expire', key, time)\n" + "end\n" + "return tonumber(current);"; } }
要点说明:
- 每个 Redis 数据库都对应一个
LettuceConnectionFactory
和一个RedisTemplate
。 - 通过
@Qualifier
注解区分不同的 Bean。 @Primary
标注默认 Redis,Spring Cache 相关功能会自动使用它。
工具类封装
为了方便业务调用,我们可以为每个 Redis 数据源封装一个工具类,继承自通用的 RedisCache
:
RedisCache.java(通用工具类,支持常用操作,参考ruoyi实现)
package com.anfioo.common.utils; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; /** * spring redis 工具类 * * @author ruoyi **/ @SuppressWarnings(value = {"unchecked", "rawtypes"}) @Component public class RedisCache { private final RedisTemplate redisTemplate; public RedisCache(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */ public <Tphp> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTempythonplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 获取有效时间 * * @param key Redis键 * @return 有效时间 */ public long getExpire(final String key) { return redisTemplate.getExpire(key); } /** * 判断 key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 删除单个对象 * * @param key */ public boolean deleteObject(final String key) { return redisTemplate.delete(key); } /** * 删除集合对象 * * @param collection 多个对象 * @return */ public boolean deleteObject(final Collection collection) { return redisTemplate.delete(collection) > 0; } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public <T> long setCacheList(final String key, final List<T> dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public <T> List<T> getCacheList(final String key) { return redisTemplate.opsForList().range(key, 0, -1); } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * * @param key * @return */ public <T> Set<T> getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } /** * 缓存Map * * @param key * @param dataMap */ public <T> void setCacheMap(final String key, final Map<String, T> dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } } /** * 获得缓存的Map * * @param key * @return */ public <T> Map<String, T> getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** android* 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */ public <T> void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public <T> T getCacheMapValue(final String key, final String hKey) { HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 删除Hash中的某条数据 * * @param key Redis键 * @param hKey Hash键 * @return 是否成功 */ public boolean deleteCacheMapValue(final String key, final String hKey) { return redisTemplate.opsForHash().delete(key, hKey) > 0; } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public Collection<String> keys(final String pattern) { return redisTemplate.keys(pattern); } }
AuthRedisCache.java
package com.anfioo.common.utils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component public class AuthRedisCache extends RedisCache { public AuthRedisCache(@Qualifier("authRedisTemplate") RedisTemplate redisTemplate) { super(redisTemplate); } }
ChatRedisCache.java
package com.anfioo.common.utils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @Component public class ChatRedisCache extends RedisCache { public ChatRedisCache(@Qualifier("chatRedisTemplate") RedisTemplate redisTemplate) { super(redisTemplate); } }
这样,业务层只需注入 AuthRedisCache
或 ChatRedisCache
,即可操作对应的 Redis 数据库。
业务调用示例
以 Controller 为例,演示如何分别操作不同 Redis 数据库:
package com.anfioo.sys.controller; import com.anfioo.common.core.ApiResponse; import com.anfioo.common.core.BaseController; import com.anfioo.sys.service.RedisDemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/redis-demo") public class RedisDemoController extends BaseController { @Autowired private RedisDemoService redisDemoService; @PostMapping("/create-auth") public ApiResponse<String> setAuthRedis() { String s = redisDemoService.setAuthRedisValue(); return success(s); } @PostMapping("/create-chat") public ApiResponse<String> setChatRedis() { String s = redisDemoService.setChatRedisValue(); return success(s); } @GetMapping("/auth/{id}") public ApiResponse<String> getAuthRedis(@PathVariable String id) { String value = redisDemoService.getAuthRedisValueById(id); return success(value); } @GetMapping("/chat/{id}") public ApiResponse<String> getChatRedis(@PathVariable String id) { String value = redisDemoService.getChatRedisValueById(id); return success(value); } }
对应Service和其实现类
package com.anfioo.sys.service.impl; import java.util.ArrayList; import com.anfioo.common.utils.AuthRedisCache; import com.anfioo.common.utils.ChatRedisCache; import com.anfioo.sys.service.RedisDemoService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.UUID; @Service public class RedisDemoServiceImpl implements RedisDemoService { @Autowired private AuthRedisCache authRedisCache; @Autowired private ChatRedisCache chatRedisCache; private static final String LOGIN_PREFIX = "login_token:"; private static final String CHAT_PREFIX = "chat_token:"; @Override public String setAuthRedisValue() { //todo 获取生成的jwt等等,验证登录成功与否 ,看你的设计, //todo 键 - 可以使用唯一uuid 值 -你要放入的缓存的值 String AuthId = UUID.randomUUID().toString(); String redisKey = LOGIN_PREFIX + AuthId; YouSaveData youSaveData = new YouSaveData(); youSaveData.setId("1"); ArrayList<String> roles = new ArrayList<>(); roles.add("user"); roles.add("manager"); youSaveData.setRole(roles); youSaveData.setUsername("Anfioo"); youSaveData.setPassword("Anfioo666"); authRedisCache.setCacheObject(redisKey, youSaveData); return AuthId; } @Override public String setChatRedisValue() { String chatId = UUID.randomUUID().toString(); String redisKey = CHAT_PREFIX + chatId; YouSaveChat youSaveChat = new YouSaveChat(); youSaveChat.setChatId(chatId); ArrayList<String> members = new ArrayList<>(); members.add("userA"); members.add("userB"); youSaveChat.setMembers(members); youSaveChat.setChatName("Anfioo聊天室"); youSaveChat.setLastMessage("Hello, world!"); chatRedisCache.setCacheObject(redisKey, youSaveChat); return chatId; } @Override public String getAuthRedisValueById(String id) { String redisKey = LOGIN_PREFIX + id; Object obj = authRedisCache.getCacheObject(redisKey); return obj != null ? obj.toString() : null; } @Override public String getChatRedisValueById(String id) { String redisKey = CHAT_PREFIX + id; Object obj = chatRedisCache.getCacheObject(redisKey); return obj != null ? obj.toString() : null; } @Data public static class YouSaveChat { private String chatId; private List<String> members; private String chatName; private String lastMessage; } @Data public static class YouSaveData { private String id; private List<String> role; private String username; private String password; } }
package com.anfioo.sys.service; public interface RedisDemoService { String setAuthRedisValue(); String setChatRedisValue(); String getAuthRedisValueById(Stringjavascript id); String getChatRedisValueById(String id); }
总结
通过以上配置和代码实现,我们就可以在 Spring Boot 项目中灵活地切换和操作多个 Redis 数据库,实现数据隔离和多业务场景支持。核心思路是:
- 配置文件中分模块配置不同 Redis 数据源
- 编写属性类和配置类,分别注入不同的
RedisTemplate
- 通过工具类封装,业务层按需注入和调用
这种方式不仅适用于 auth、chat 场景,也适用于任何需要 Redis 多数据源的业务需求。
以上就是SpringBoot实现Redis多数据库切换(多数据源配置)的详细内容,更多关于SpringBoot Redis多数据库切换的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论