# 告警规则Redis缓存同步方案设计 ## 📊 现状分析 ### 共享Redis架构确认 ✅ 经过代码分析确认,**ljwx-boot 和 ljwx-bigscreen 共用一个Redis实例**: **ljwx-boot配置** (`application-local.yml:19`): ```yaml redis: url: redis://default:123456@localhost:6379/1 # DB=1 ``` **ljwx-bigscreen配置** (`redis_config.py:17`): ```python self.db = int(os.getenv('REDIS_DB', 0)) # DB=0(默认) ``` **重要发现**: 两个服务使用**不同的Redis DB**,完全避免了键冲突! ### 当前实现情况 通过分析 `TAlertRulesFacadeImpl` 发现,系统已经实现了完善的Redis缓存同步机制: ```java // 当前实现 - ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/health/facade/impl/TAlertRulesFacadeImpl.java:87 RedisUtil.publish("alert_rules_channel", "update:" + customerId); ``` **现有机制**: - ✅ **事件触发**: 增删改操作后自动触发Redis更新 - ✅ **分客户缓存**: 按 `customer_id` 分别缓存告警规则 - ✅ **发布订阅**: 使用Redis pub/sub通知缓存更新 - ✅ **JSON存储**: 规则数据以JSON格式存储到Redis - ✅ **无键冲突**: DB分离架构避免键冲突 **API接口**: - `POST /t_alert_rules/` - 新增规则 (line:76) - `PUT /t_alert_rules/` - 更新规则 (line:84) - `DELETE /t_alert_rules/` - 删除规则 (line:91) ## 🚀 优化方案对比 ### 方案一:现有机制改进 (推荐) **优势**: - 基于现有架构,改动最小 - 已实现分客户隔离和事件触发 - 可直接延长TTL时间 **改进点**: 1. **缓存策略优化** 2. **订阅者管理完善** 3. **容错机制增强** ### 方案二:数据库触发器 **优势**: - 数据库级别保证一致性 - 无需修改业务代码 **劣势**: - 数据库性能影响 - 跨平台兼容性问题 - 调试困难 ### 方案三:消息队列 (RabbitMQ/Kafka) **优势**: - 解耦性好 - 消息可靠性高 **劣势**: - 架构复杂度增加 - 运维成本提升 - 延迟相对较高 ### 方案四:CDC (Change Data Capture) **优势**: - 实时性极高 - 数据一致性强 **劣势**: - 技术复杂度高 - 维护成本大 ## 🎯 推荐方案详细设计 ### 核心架构(共享Redis优化版) ``` ljwx-admin → ljwx-boot (Redis DB=1) → MySQL ↓ Redis Pub/Sub(跨DB消息) ↓ ljwx-bigscreen (Redis DB=0) → generate_alerts (Python) ``` **架构优势**: - **DB分离**: ljwx-boot使用DB=1,ljwx-bigscreen使用DB=0,完全避免键冲突 - **跨DB通信**: Redis pub/sub消息在所有DB间共享,实现完美的事件通知 - **零冲突**: 现有缓存键完全隔离,无需担心键名冲突 ### 1. 缓存策略改进 #### 1.1 TTL策略调整 ```java // 当前 TTL 可以设置为更长时间,如24小时 RedisUtil.setex("alert_rules_" + customerId, 86400, jsonString); ``` #### 1.2 缓存Key设计 ``` alert_rules_{customer_id} # 主缓存 alert_rules_version_{customer_id} # 版本控制 alert_rules_lock_{customer_id} # 分布式锁 ``` #### 1.3 版本控制机制 ```java public void updateAlertRulesCache(Long customerId) { String lockKey = "alert_rules_lock_" + customerId; String versionKey = "alert_rules_version_" + customerId; // 获取分布式锁 if (RedisUtil.tryLock(lockKey, 5000)) { try { // 增加版本号 Long version = RedisUtil.incr(versionKey); // 更新缓存 List rules = getRulesByCustomerId(customerId); String cacheKey = "alert_rules_" + customerId; Map cacheData = new HashMap<>(); cacheData.put("version", version); cacheData.put("rules", rules); cacheData.put("updateTime", System.currentTimeMillis()); RedisUtil.setex(cacheKey, 86400, JSON.toJSONString(cacheData)); // 发布更新通知 RedisUtil.publish("alert_rules_channel", "update:" + customerId + ":" + version); } finally { RedisUtil.unlock(lockKey); } } } ``` ### 2. 订阅者管理完善 #### 2.1 Python端跨DB订阅者 ```python import redis import json import logging from threading import Thread import time class AlertRulesSubscriber: def __init__(self, redis_client, boot_redis_client): self.redis = redis_client # ljwx-bigscreen Redis (DB=0) self.boot_redis = boot_redis_client # ljwx-boot Redis (DB=1) self.local_cache = {} self.running = True def start_subscriber(self): """启动订阅者""" pubsub = self.redis.pubsub() pubsub.subscribe('alert_rules_channel') def subscriber_thread(): for message in pubsub.listen(): if message['type'] == 'message': self.handle_cache_update(message['data']) Thread(target=subscriber_thread, daemon=True).start() def handle_cache_update(self, message): """处理缓存更新消息""" try: # 解析消息: "update:customer_id:version" parts = message.decode('utf-8').split(':') if len(parts) >= 2 and parts[0] == 'update': customer_id = parts[1] version = int(parts[2]) if len(parts) > 2 else 0 # 检查是否需要更新本地缓存 if self.should_update_cache(customer_id, version): self.refresh_local_cache(customer_id) except Exception as e: logging.error(f"处理缓存更新失败: {e}") def should_update_cache(self, customer_id, version): """检查是否需要更新缓存""" local_version = self.local_cache.get(f"version_{customer_id}", 0) return version > local_version def refresh_local_cache(self, customer_id): """刷新本地缓存 - 从ljwx-boot的Redis DB获取""" try: cache_key = f"alert_rules_{customer_id}" # 从ljwx-boot的Redis DB=1获取数据 cached_data = self.boot_redis.get(cache_key) if cached_data: data = json.loads(cached_data) self.local_cache[customer_id] = data['rules'] self.local_cache[f"version_{customer_id}"] = data['version'] logging.info(f"更新告警规则缓存: customer_id={customer_id}") except Exception as e: logging.error(f"刷新本地缓存失败: {e}") def get_alert_rules(self, customer_id): """获取告警规则""" # 先从本地缓存获取 if customer_id in self.local_cache: return self.local_cache[customer_id] # 本地缓存miss,从ljwx-boot的Redis获取 try: cache_key = f"alert_rules_{customer_id}" cached_data = self.boot_redis.get(cache_key) if cached_data: data = json.loads(cached_data) self.local_cache[customer_id] = data['rules'] self.local_cache[f"version_{customer_id}"] = data['version'] return data['rules'] except Exception as e: logging.error(f"从ljwx-boot Redis获取规则失败: {e}") # 兜底:从数据库获取 return self.get_rules_from_database(customer_id) ``` ### 3. 容错机制设计 #### 3.1 多级缓存策略 ```python class AlertRulesCacheManager: def __init__(self): self.l1_cache = {} # 进程内缓存 self.l2_cache = None # Redis缓存 self.l3_cache = None # 数据库 def get_alert_rules(self, customer_id): """三级缓存获取""" # L1: 进程缓存 if self.is_l1_cache_valid(customer_id): return self.l1_cache[customer_id]['rules'] # L2: Redis缓存 rules = self.get_from_redis(customer_id) if rules: self.update_l1_cache(customer_id, rules) return rules # L3: 数据库兜底 rules = self.get_from_database(customer_id) if rules: self.update_all_cache(customer_id, rules) return rules return [] ``` #### 3.2 健康检查机制 ```python class CacheHealthChecker: def check_cache_health(self): """缓存健康检查""" checks = { 'redis_connection': self.check_redis_connection(), 'cache_consistency': self.check_cache_consistency(), 'pub_sub_status': self.check_pub_sub_status(), } return checks def auto_recovery(self): """自动恢复机制""" if not self.check_redis_connection(): self.reconnect_redis() if not self.check_pub_sub_status(): self.restart_subscriber() ``` ## 📈 性能对比分析 | 方案 | 实时性 | 一致性 | 复杂度 | 性能影响 | 推荐指数 | |------|-------|--------|--------|----------|----------| | 现有机制改进 | 毫秒级 | 强一致 | 低 | 最小 | ⭐⭐⭐⭐⭐ | | 数据库触发器 | 毫秒级 | 强一致 | 中 | 中等 | ⭐⭐⭐ | | 消息队列 | 秒级 | 最终一致 | 高 | 较大 | ⭐⭐ | | CDC方案 | 毫秒级 | 强一致 | 很高 | 较大 | ⭐⭐ | ## 🛠️ 实施步骤 ### 阶段一:缓存策略优化 (1-2天) 1. 调整Redis TTL为24小时 2. 添加版本控制机制 3. 完善分布式锁逻辑 ### 阶段二:订阅者完善 (2-3天) 1. Python端实现Redis订阅者 2. 本地缓存与Redis缓存结合 3. 添加容错和重连机制 ### 阶段三:监控和测试 (1-2天) 1. 添加缓存监控指标 2. 压力测试验证 3. 故障场景测试 ## 🔧 配置建议 ### Redis配置优化 ```yaml # ljwx-boot Redis配置 (DB=1) redis: url: redis://default:123456@localhost:6379/1 timeout: 5000 lettuce: pool: max-active: 20 max-idle: 10 min-idle: 5 # 启用键空间通知 notify-keyspace-events: Ex ``` ```python # ljwx-bigscreen Redis配置 (DB=0) class RedisConfig: def __init__(self): self.host = '127.0.0.1' self.port = 6379 self.db = 0 # ljwx-bigscreen使用DB=0 self.password = '123456' def get_boot_redis_client(self): """获取ljwx-boot的Redis客户端(DB=1)""" return Redis( host=self.host, port=self.port, db=1, password=self.password # DB=1 ) ``` ### 缓存参数 ```properties # 告警规则缓存配置 alert.rules.cache.ttl=86400 alert.rules.cache.local.size=1000 alert.rules.cache.local.ttl=300 alert.rules.pub.channel=alert_rules_channel ``` ## 📊 监控指标 建议监控以下指标: - **缓存命中率**: L1/L2/L3缓存命中率 - **更新延迟**: 从修改到缓存更新的延迟 - **订阅者状态**: pub/sub连接状态 - **数据一致性**: 缓存与数据库的一致性检查 - **错误率**: 缓存更新失败率 ## ⚠️ 注意事项 1. **渐进式部署**: 先在测试环境验证,再灰度发布 2. **回滚准备**: 保留原有查询数据库的逻辑作为兜底 3. **监控告警**: 设置缓存相关的监控告警 4. **数据预热**: 系统启动时预加载热点客户的规则数据 5. **容量规划**: 根据客户数量规划Redis内存容量 ## 🎯 总结 **推荐采用现有机制改进方案**,因为: 1. **零冲突架构**: ljwx-boot(DB=1)和ljwx-bigscreen(DB=0)完全隔离,无键冲突风险 2. **改动最小**: 基于现有pub/sub机制改进,无需修改架构 3. **风险可控**: 保留数据库兜底逻辑,多级缓存保障 4. **性能最佳**: TTL可安全延长到24小时,大幅减少数据库查询 5. **维护简单**: 不引入额外中间件,现有运维流程不变 6. **扩展性好**: DB分离架构天然支持服务隔离和独立扩展 ## 🔥 关键优势 **共享Redis + DB分离**是最优方案: - **硬件成本最低**: 共用一个Redis实例 - **管理成本最低**: 统一的Redis运维 - **冲突风险为零**: DB分离完全避免键冲突 - **通信效率最高**: pub/sub跨DB消息零延迟 通过这套方案,Redis告警规则缓存的TTL可以**安全地延长到24小时或更久**,同时保证数据的实时性和一致性,完美实现您的需求! --- *文档创建时间: 2025-09-09* *技术负责人: 系统架构团队*