Redis热点Key独立集群实现方案(核心思路)

张开发
2026/4/17 9:19:29 15 分钟阅读

分享文章

Redis热点Key独立集群实现方案(核心思路)
Redis热点Key独立集群实现方案1. 设计背景在高并发场景下热点Key会导致Redis实例负载过高影响整个系统的稳定性。通过将热点Key分离到独立的Redis集群可以实现资源隔离提高系统的抗风险能力。2. 实现方案2.1 核心设计思路多实例配置支持配置多个Redis实例包括普通实例和热点实例Key路由策略根据Key的特征或规则将请求路由到不同的Redis实例统一访问接口对外提供统一的Redis访问接口屏蔽底层实例差异灵活的路由规则支持多种路由规则如前缀匹配、正则匹配、哈希路由等2.2 配置扩展2.2.1 修改配置属性类1234567891011121314151617181920212223242526272829303132333435363738394041424344454647DataConfigurationProperties(prefix redis.sdk, ignoreInvalidFields true)publicclassRedisClientConfigProperties {// 默认实例配置privateDefaultConfig defaultConfig;// 多个实例配置privateMapString, InstanceConfig instances newHashMap();// 路由规则配置privateMapString, RouteRule routeRules newHashMap();DatapublicstaticclassDefaultConfig {privateString host;privateintport;privateString password;privateintpoolSize 64;privateintminIdleSize 10;privateintidleTimeout 10000;privateintconnectTimeout 10000;privateintretryAttempts 3;privateintretryInterval 1000;privateintpingInterval 0;privatebooleankeepAlive true;}DatapublicstaticclassInstanceConfig {privateString host;privateintport;privateString password;privateintpoolSize 64;privateintminIdleSize 10;privateintidleTimeout 10000;privateintconnectTimeout 10000;privateintretryAttempts 3;privateintretryInterval 1000;privateintpingInterval 0;privatebooleankeepAlive true;}DatapublicstaticclassRouteRule {// 路由类型prefix前缀匹配、regex正则匹配、hash哈希路由privateString type;// 匹配规则privateString pattern;// 目标实例名称privateString targetInstance;}}2.2.2 配置文件示例1234567891011121314151617181920212223242526272829303132redis:sdk:# 默认实例配置default-config:host: 127.0.0.1port: 6379# 多个Redis实例配置instances:# 普通实例normal:host: 127.0.0.1port: 6379# 热点Key实例hot:host: 127.0.0.1port: 6380# 活动相关实例activity:host: 127.0.0.1port: 6381# 路由规则配置route-rules:# 热点Key路由规则以hot_开头的Key路由到hot实例hot-rule:type: prefixpattern: hot_target-instance: hot# 活动Key路由规则以activity_开头的Key路由到activity实例activity-rule:type: prefixpattern: activity_target-instance: activity2.3 Redis客户端配置扩展123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153ConfigurationEnableConfigurationProperties(RedisClientConfigProperties.class)publicclassRedisClientConfig {// Redis实例映射key为实例名称value为RedissonClient实例privatefinalMapString, RedissonClient redisInstances newConcurrentHashMap();// 路由规则列表privatefinalListRouteRuleWrapper routeRules newArrayList();PostConstructpublicvoidinit(ConfigurableApplicationContext applicationContext, RedisClientConfigProperties properties) {// 1. 初始化默认实例initDefaultInstance(applicationContext, properties);// 2. 初始化其他实例initOtherInstances(applicationContext, properties);// 3. 初始化路由规则initRouteRules(properties);}privatevoidinitDefaultInstance(ConfigurableApplicationContext applicationContext, RedisClientConfigProperties properties) {RedisClientConfigProperties.DefaultConfig defaultConfig properties.getDefaultConfig();RedissonClient redissonClient createRedissonClient(defaultConfig);redisInstances.put(default, redissonClient);}privatevoidinitOtherInstances(ConfigurableApplicationContext applicationContext, RedisClientConfigProperties properties) {MapString, RedisClientConfigProperties.InstanceConfig instances properties.getInstances();for(Map.EntryString, RedisClientConfigProperties.InstanceConfig entry : instances.entrySet()) {String instanceName entry.getKey();RedisClientConfigProperties.InstanceConfig instanceConfig entry.getValue();RedissonClient redissonClient createRedissonClient(instanceConfig);redisInstances.put(instanceName, redissonClient);}}privatevoidinitRouteRules(RedisClientConfigProperties properties) {MapString, RedisClientConfigProperties.RouteRule routeRulesConfig properties.getRouteRules();for(Map.EntryString, RedisClientConfigProperties.RouteRule entry : routeRulesConfig.entrySet()) {RedisClientConfigProperties.RouteRule config entry.getValue();RouteRuleWrapper wrapper newRouteRuleWrapper();wrapper.setType(config.getType());wrapper.setPattern(config.getPattern());wrapper.setTargetInstance(config.getTargetInstance());routeRules.add(wrapper);}}privateRedissonClient createRedissonClient(Object configObj) {Config config newConfig();config.setCodec(JsonJacksonCodec.INSTANCE);String host;intport;String password;intpoolSize;intminIdleSize;intidleTimeout;intconnectTimeout;intretryAttempts;intretryInterval;intpingInterval;booleankeepAlive;// 根据配置对象类型获取配置属性if(configObjinstanceofRedisClientConfigProperties.DefaultConfig) {RedisClientConfigProperties.DefaultConfig defaultConfig (RedisClientConfigProperties.DefaultConfig) configObj;host defaultConfig.getHost();port defaultConfig.getPort();password defaultConfig.getPassword();poolSize defaultConfig.getPoolSize();minIdleSize defaultConfig.getMinIdleSize();idleTimeout defaultConfig.getIdleTimeout();connectTimeout defaultConfig.getConnectTimeout();retryAttempts defaultConfig.getRetryAttempts();retryInterval defaultConfig.getRetryInterval();pingInterval defaultConfig.getPingInterval();keepAlive defaultConfig.isKeepAlive();}else{RedisClientConfigProperties.InstanceConfig instanceConfig (RedisClientConfigProperties.InstanceConfig) configObj;host instanceConfig.getHost();port instanceConfig.getPort();password instanceConfig.getPassword();poolSize instanceConfig.getPoolSize();minIdleSize instanceConfig.getMinIdleSize();idleTimeout instanceConfig.getIdleTimeout();connectTimeout instanceConfig.getConnectTimeout();retryAttempts instanceConfig.getRetryAttempts();retryInterval instanceConfig.getRetryInterval();pingInterval instanceConfig.getPingInterval();keepAlive instanceConfig.isKeepAlive();}// 配置单节点RedisSingleServerConfig singleServerConfig config.useSingleServer().setAddress(redis:// host : port).setConnectionPoolSize(poolSize).setConnectionMinimumIdleSize(minIdleSize).setIdleConnectionTimeout(idleTimeout).setConnectTimeout(connectTimeout).setRetryAttempts(retryAttempts).setRetryInterval(retryInterval).setPingConnectionInterval(pingInterval).setKeepAlive(keepAlive);// 设置密码如果有if(StringUtils.isNotBlank(password)) {singleServerConfig.setPassword(password);}returnRedisson.create(config);}/*** 根据Key获取对应的RedissonClient实例*/publicRedissonClient getRedissonClient(String key) {// 遍历路由规则找到匹配的规则for(RouteRuleWrapper rule : routeRules) {if(matchRule(key, rule)) {String targetInstance rule.getTargetInstance();returnredisInstances.get(targetInstance);}}// 没有匹配到规则使用默认实例returnredisInstances.get(default);}/*** 检查Key是否匹配路由规则*/privatebooleanmatchRule(String key, RouteRuleWrapper rule) {String type rule.getType();String pattern rule.getPattern();switch(type) {caseprefix:// 前缀匹配returnkey.startsWith(pattern);caseregex:// 正则匹配returnkey.matches(pattern);casehash:// 哈希路由根据Key的哈希值路由到不同实例// 这里简化实现实际可以根据哈希值和实例数量计算路由inthash key.hashCode();returnMath.abs(hash) %20;// 示例偶数哈希值匹配default:returnfalse;}}/*** 路由规则包装类*/DataprivatestaticclassRouteRuleWrapper {privateString type;privateString pattern;privateString targetInstance;}/*** 注入Redis服务*/Bean(redisService)publicRedisService redisService() {returnnewRedisServiceImpl(this);}}2.4 统一Redis服务封装1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677/*** Redis服务接口*/publicinterfaceRedisService {/*** 设置Key-Value*/Tvoidset(String key, T value);/*** 设置Key-Value带过期时间*/Tvoidset(String key, T value,longexpireTime, TimeUnit timeUnit);/*** 获取Value*/T T get(String key, ClassT clazz);/*** 删除Key*/booleandelete(String key);/*** 设置Hash字段*/Tvoidhset(String key, String field, T value);/*** 获取Hash字段*/T T hget(String key, String field, ClassT clazz);// 其他Redis操作方法...}/*** Redis服务实现类*/ServicepublicclassRedisServiceImplimplementsRedisService {privatefinalRedisClientConfig redisClientConfig;publicRedisServiceImpl(RedisClientConfig redisClientConfig) {this.redisClientConfig redisClientConfig;}OverridepublicTvoidset(String key, T value) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RMapString, T map redissonClient.getMap(cache);map.put(key, value);}OverridepublicTvoidset(String key, T value,longexpireTime, TimeUnit timeUnit) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RBucketT bucket redissonClient.getBucket(key);bucket.set(value, expireTime, timeUnit);}OverridepublicT T get(String key, ClassT clazz) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RBucketT bucket redissonClient.getBucket(key);returnbucket.get();}Overridepublicbooleandelete(String key) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RBucketObject bucket redissonClient.getBucket(key);returnbucket.delete();}OverridepublicTvoidhset(String key, String field, T value) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RMapString, T map redissonClient.getMap(key);map.put(field, value);}OverridepublicT T hget(String key, String field, ClassT clazz) {RedissonClient redissonClient redisClientConfig.getRedissonClient(key);RMapString, T map redissonClient.getMap(key);returnmap.get(field);}// 其他Redis操作方法实现...}2.5 使用示例12345678910111213141516171819202122232425262728ServicepublicclassActivityService {AutowiredprivateRedisService redisService;publicvoidcacheActivityInfo(String activityId, Activity activity) {// 活动相关Key会路由到activity实例String key activity_ activityId;redisService.set(key, activity,1, TimeUnit.HOURS);}publicActivity getActivityInfo(String activityId) {String key activity_ activityId;returnredisService.get(key, Activity.class);}publicvoidcacheHotProduct(String productId, Product product) {// 热点Key会路由到hot实例String key hot_product_ productId;redisService.set(key, product,30, TimeUnit.MINUTES);}publicProduct getHotProduct(String productId) {String key hot_product_ productId;returnredisService.get(key, Product.class);}publicvoidcacheUserInfo(String userId, User user) {// 普通Key会路由到default实例String key user_ userId;redisService.set(key, user,24, TimeUnit.HOURS);}}3. 方案优势3.1 资源隔离热点Key单独存储在独立的Redis实例中避免影响其他业务不同业务线的Key可以分离到不同实例实现业务隔离3.2 灵活扩展支持动态添加Redis实例应对业务增长支持多种路由规则适应不同业务场景3.3 高可用性单个Redis实例故障不会影响整个系统可以为热点实例配置更高的资源规格3.4 统一访问接口对外提供统一的Redis访问接口简化开发底层实例变更对业务代码透明3.5 易于维护集中管理Redis实例配置统一监控和管理所有Redis实例4. 部署架构1234567891011121314------------------- ------------------- -------------------| | | | | || 应用服务 | | Redis普通实例 | | Redis热点实例 || (RedisService) |---| (Port:6379) | | (Port:6380) || | | | | |------------------- ------------------- -------------------|v-------------------| || Redis活动实例 || (Port:6381) || |-------------------5. 注意事项路由规则设计路由规则应根据业务特点精心设计避免规则冲突数据迁移已有数据需要考虑迁移策略确保平滑过渡监控告警需要为每个Redis实例配置独立的监控和告警一致性问题不同实例间的数据一致性需要业务层面保证连接管理需要合理配置连接池大小避免连接泄漏性能测试上线前需要进行充分的性能测试验证方案效果6. 扩展建议自动热点识别结合监控数据实现热点Key的自动识别和迁移动态路由调整支持根据实例负载动态调整路由规则Redis集群支持扩展支持Redis集群配置提高可用性多种客户端支持除了Redisson支持其他Redis客户端如Lettuce缓存预热实现热点数据的自动预热提高系统响应速度通过以上方案可以实现热点Key的独立集群部署提高系统的抗风险能力和性能表现同时保持良好的扩展性和维护性。

更多文章