数据库修复: - t_user_health_data_daily: 添加customer_id字段(租户ID) - t_user_health_data_weekly: 添加customer_id字段(租户ID) ljwx-boot同步更新: - TUserHealthDataDailyMapper.xml: 添加customer_id字段映射 - TUserHealthDataWeeklyMapper.xml: 添加customer_id字段映射 其他改进: - 告警规则缓存优化 - Redis缓存性能提升 - 健康数据处理性能优化 - 添加完整的文档和工具脚本 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
12 KiB
12 KiB
告警规则Redis缓存同步方案设计
📊 现状分析
共享Redis架构确认 ✅
经过代码分析确认,ljwx-boot 和 ljwx-bigscreen 共用一个Redis实例:
ljwx-boot配置 (application-local.yml:19):
redis:
url: redis://default:123456@localhost:6379/1 # DB=1
ljwx-bigscreen配置 (redis_config.py:17):
self.db = int(os.getenv('REDIS_DB', 0)) # DB=0(默认)
重要发现: 两个服务使用不同的Redis DB,完全避免了键冲突!
当前实现情况
通过分析 TAlertRulesFacadeImpl 发现,系统已经实现了完善的Redis缓存同步机制:
// 当前实现 - 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时间
改进点:
- 缓存策略优化
- 订阅者管理完善
- 容错机制增强
方案二:数据库触发器
优势:
- 数据库级别保证一致性
- 无需修改业务代码
劣势:
- 数据库性能影响
- 跨平台兼容性问题
- 调试困难
方案三:消息队列 (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策略调整
// 当前 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 版本控制机制
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<TAlertRules> rules = getRulesByCustomerId(customerId);
String cacheKey = "alert_rules_" + customerId;
Map<String, Object> 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订阅者
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 多级缓存策略
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 健康检查机制
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天)
- 调整Redis TTL为24小时
- 添加版本控制机制
- 完善分布式锁逻辑
阶段二:订阅者完善 (2-3天)
- Python端实现Redis订阅者
- 本地缓存与Redis缓存结合
- 添加容错和重连机制
阶段三:监控和测试 (1-2天)
- 添加缓存监控指标
- 压力测试验证
- 故障场景测试
🔧 配置建议
Redis配置优化
# 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
# 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
)
缓存参数
# 告警规则缓存配置
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连接状态
- 数据一致性: 缓存与数据库的一致性检查
- 错误率: 缓存更新失败率
⚠️ 注意事项
- 渐进式部署: 先在测试环境验证,再灰度发布
- 回滚准备: 保留原有查询数据库的逻辑作为兜底
- 监控告警: 设置缓存相关的监控告警
- 数据预热: 系统启动时预加载热点客户的规则数据
- 容量规划: 根据客户数量规划Redis内存容量
🎯 总结
推荐采用现有机制改进方案,因为:
- 零冲突架构: ljwx-boot(DB=1)和ljwx-bigscreen(DB=0)完全隔离,无键冲突风险
- 改动最小: 基于现有pub/sub机制改进,无需修改架构
- 风险可控: 保留数据库兜底逻辑,多级缓存保障
- 性能最佳: TTL可安全延长到24小时,大幅减少数据库查询
- 维护简单: 不引入额外中间件,现有运维流程不变
- 扩展性好: DB分离架构天然支持服务隔离和独立扩展
🔥 关键优势
共享Redis + DB分离是最优方案:
- 硬件成本最低: 共用一个Redis实例
- 管理成本最低: 统一的Redis运维
- 冲突风险为零: DB分离完全避免键冲突
- 通信效率最高: pub/sub跨DB消息零延迟
通过这套方案,Redis告警规则缓存的TTL可以安全地延长到24小时或更久,同时保证数据的实时性和一致性,完美实现您的需求!
文档创建时间: 2025-09-09
技术负责人: 系统架构团队