Files
java-template/docs/alert_rules_cache_sync_solutions.md
T
BrunoGaoandClaude e402548e45 fix: 修复customer_id字段缺失问题,完成数据库schema同步
数据库修复:
- 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]>
2025-09-10 13:49:59 +08:00

400 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 告警规则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<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订阅者
```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*
*技术负责人: 系统架构团队*