## 主要更改 ### 1. 数据库表结构增强 - **t_device_message_v2**: 新增7个关键字段 - priority_level: 优先级数值(1-5) - channels: 分发渠道JSON - require_ack: 是否需要确认 - metadata: 扩展元数据JSON - target_count: 目标用户总数 - version: 乐观锁版本号 - **t_device_message_detail_v2**: 新增8个关键字段 - distribution_id: 分发唯一ID - device_sn: 设备序列号 - user_id: 用户ID(核心查询字段) - response_duration: 响应耗时 - delivery_details: 分发详情JSON - client_info: 客户端信息JSON - location_info: 位置信息JSON - last_retry_time: 最后重试时间 ### 2. 索引优化重构 (基于user_id) - 删除原有device_sn相关索引 - 新建5个user_id高性能复合索引: - idx_customer_user_time: 客户+用户+时间 - idx_customer_user_status: 客户+用户+状态 - idx_customer_user_type: 客户+用户+类型 - idx_user_org_priority: 用户+组织+优先级 - idx_user_status_time: 用户+状态+时间 - detail表新建4个user_id相关索引: - uk_message_user_target: 唯一约束 - idx_customer_user_status: 客户+用户+状态 - idx_user_channel_time: 用户+渠道+时间 - idx_user_delivery_status: 用户+分发状态 ### 3. 数据迁移和初始化 - 自动生成distribution_id - 根据urgency设置priority_level - 初始化默认channels配置 - 保持现有数据完整性 ### 4. 性能提升预期 - 用户消息查询: 预计10-20倍性能提升 - 确认状态查询: 支持快速人员清单检索 - 存储空间: JSON字段替代冗余VARCHAR - 查询覆盖: 所有user_id查询都能命中索引 ### 5. 文档和脚本 - 完整的V2优化SQL脚本 - 消息机制设计文档 - 数据库备份(22MB) - 性能验证查询 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
21 KiB
21 KiB
告警系统监控增强方案
📋 当前状况分析
观察到的日志信息
📊 性能统计: 处理时间=0.009s, 规则数量=25, Redis缓存=命中
Skipping rule 1963764274519986177: missing physical_sign
Skipping rule 1963764274524180481: missing physical_sign
Skipping rule 1963764274528374785: missing physical_sign
Skipping rule 1963764274515791873: missing physical_sign
问题识别
- 告警规则执行效率高: 25个规则仅用时0.009秒
- Redis缓存命中: 性能良好
- 数据完整性问题: 多个规则因缺少
physical_sign字段被跳过 - 静默失败: 跳过的规则没有统计和告警
🎯 监控增强目标
核心改进点
- 告警规则执行监控: 跟踪规则执行成功率和失败原因
- 数据完整性监控: 监控健康数据字段缺失情况
- 告警系统性能监控: 优化告警处理流程监控
- 规则配置质量监控: 识别无效或配置错误的规则
1. 告警规则执行监控
1.1 规则执行统计收集器
from dataclasses import dataclass
from typing import Dict, List, Optional, Set
from collections import defaultdict
import time
import json
@dataclass
class AlertRuleExecutionStats:
"""告警规则执行统计"""
rule_id: str
rule_name: Optional[str]
execution_time: float
status: str # 'success', 'skipped', 'error'
skip_reason: Optional[str]
device_sn: str
timestamp: float
def to_dict(self) -> Dict:
return {
'rule_id': self.rule_id,
'rule_name': self.rule_name,
'execution_time': self.execution_time,
'status': self.status,
'skip_reason': self.skip_reason,
'device_sn': self.device_sn,
'timestamp': self.timestamp
}
class AlertRuleMonitor:
"""告警规则监控器"""
def __init__(self):
self.execution_stats = []
self.stats_lock = threading.Lock()
self.summary_stats = {
'total_executions': 0,
'successful_executions': 0,
'skipped_executions': 0,
'error_executions': 0,
'skip_reasons': defaultdict(int),
'rule_performance': defaultdict(list)
}
# 启动统计输出线程
self.start_stats_reporter()
logger.info("🔍 AlertRuleMonitor已启动")
def record_rule_execution(self, rule_id: str, rule_name: Optional[str],
execution_time: float, status: str,
skip_reason: Optional[str] = None,
device_sn: str = 'unknown'):
"""记录规则执行结果"""
stat = AlertRuleExecutionStats(
rule_id=rule_id,
rule_name=rule_name,
execution_time=execution_time,
status=status,
skip_reason=skip_reason,
device_sn=device_sn,
timestamp=time.time()
)
with self.stats_lock:
self.execution_stats.append(stat)
# 更新汇总统计
self.summary_stats['total_executions'] += 1
if status == 'success':
self.summary_stats['successful_executions'] += 1
elif status == 'skipped':
self.summary_stats['skipped_executions'] += 1
if skip_reason:
self.summary_stats['skip_reasons'][skip_reason] += 1
elif status == 'error':
self.summary_stats['error_executions'] += 1
# 记录规则性能
self.summary_stats['rule_performance'][rule_id].append(execution_time)
# 限制内存使用,保留最近1000条记录
if len(self.execution_stats) > 1000:
self.execution_stats = self.execution_stats[-1000:]
def get_rule_execution_summary(self, time_window_minutes: int = 60) -> Dict:
"""获取规则执行汇总统计"""
cutoff_time = time.time() - (time_window_minutes * 60)
with self.stats_lock:
recent_stats = [
stat for stat in self.execution_stats
if stat.timestamp > cutoff_time
]
if not recent_stats:
return {'message': 'No recent rule executions'}
# 计算统计指标
total_count = len(recent_stats)
success_count = len([s for s in recent_stats if s.status == 'success'])
skipped_count = len([s for s in recent_stats if s.status == 'skipped'])
error_count = len([s for s in recent_stats if s.status == 'error'])
# 跳过原因统计
skip_reasons = defaultdict(int)
for stat in recent_stats:
if stat.skip_reason:
skip_reasons[stat.skip_reason] += 1
# 规则性能统计
rule_performance = defaultdict(list)
for stat in recent_stats:
rule_performance[stat.rule_id].append(stat.execution_time)
# 计算平均执行时间
avg_performance = {}
for rule_id, times in rule_performance.items():
avg_performance[rule_id] = {
'avg_time': sum(times) / len(times),
'max_time': max(times),
'min_time': min(times),
'execution_count': len(times)
}
return {
'time_window_minutes': time_window_minutes,
'summary': {
'total_executions': total_count,
'success_rate': success_count / total_count if total_count > 0 else 0,
'skip_rate': skipped_count / total_count if total_count > 0 else 0,
'error_rate': error_count / total_count if total_count > 0 else 0
},
'skip_reasons': dict(skip_reasons),
'rule_performance': avg_performance,
'timestamp': time.time()
}
def start_stats_reporter(self):
"""启动统计报告线程"""
def reporter_worker():
while True:
try:
time.sleep(300) # 每5分钟输出一次统计
summary = self.get_rule_execution_summary(60) # 最近1小时统计
if 'summary' in summary:
logger.info(f"📊 告警规则执行统计: {json.dumps(summary, ensure_ascii=False, indent=2)}")
# 检查是否有问题需要告警
self._check_rule_execution_alerts(summary)
except Exception as e:
logger.error(f"统计报告线程异常: {e}")
thread = threading.Thread(target=reporter_worker, daemon=True)
thread.start()
def _check_rule_execution_alerts(self, summary: Dict):
"""检查规则执行相关告警"""
summary_data = summary.get('summary', {})
# 高跳过率告警
skip_rate = summary_data.get('skip_rate', 0)
if skip_rate > 0.3: # 30%以上跳过率
logger.warning(f"🚨 告警规则跳过率过高: {skip_rate:.2%}")
# 高错误率告警
error_rate = summary_data.get('error_rate', 0)
if error_rate > 0.05: # 5%以上错误率
logger.critical(f"🚨 告警规则错误率过高: {error_rate:.2%}")
# 特定跳过原因告警
skip_reasons = summary.get('skip_reasons', {})
for reason, count in skip_reasons.items():
if count > 50: # 单个原因跳过超过50次
logger.warning(f"🚨 频繁跳过告警规则: {reason} ({count}次)")
# 全局监控器实例
alert_rule_monitor = AlertRuleMonitor()
1.2 集成到现有告警系统
# 在现有的告警处理逻辑中添加监控
def enhanced_alert_processing(health_data, device_sn):
"""增强的告警处理,包含监控功能"""
start_time = time.time()
total_rules = 0
processed_rules = 0
skipped_rules = 0
try:
# 获取告警规则
rules = get_alert_rules(device_sn, health_data.get('customer_id'))
total_rules = len(rules)
logger.info(f"📊 开始处理告警: device_sn={device_sn}, 规则数量={total_rules}")
for rule in rules:
rule_start_time = time.time()
rule_id = str(rule.get('id', 'unknown'))
rule_name = rule.get('name', 'unnamed')
try:
# 检查规则执行条件
if not _check_rule_conditions(rule, health_data):
skip_reason = _determine_skip_reason(rule, health_data)
# 记录跳过统计
alert_rule_monitor.record_rule_execution(
rule_id=rule_id,
rule_name=rule_name,
execution_time=time.time() - rule_start_time,
status='skipped',
skip_reason=skip_reason,
device_sn=device_sn
)
skipped_rules += 1
logger.debug(f"Skipping rule {rule_id}: {skip_reason}")
continue
# 执行告警规则
alert_result = execute_alert_rule(rule, health_data, device_sn)
# 记录成功统计
alert_rule_monitor.record_rule_execution(
rule_id=rule_id,
rule_name=rule_name,
execution_time=time.time() - rule_start_time,
status='success',
device_sn=device_sn
)
processed_rules += 1
except Exception as rule_error:
# 记录错误统计
alert_rule_monitor.record_rule_execution(
rule_id=rule_id,
rule_name=rule_name,
execution_time=time.time() - rule_start_time,
status='error',
skip_reason=str(rule_error),
device_sn=device_sn
)
logger.error(f"规则执行失败 {rule_id}: {rule_error}")
# 输出处理统计
total_time = time.time() - start_time
cache_status = "命中" if _check_redis_cache_hit() else "未命中"
logger.info(f"📊 性能统计: 处理时间={total_time:.3f}s, 规则数量={total_rules}, "
f"成功={processed_rules}, 跳过={skipped_rules}, Redis缓存={cache_status}")
except Exception as e:
logger.error(f"告警处理异常: {e}")
def _determine_skip_reason(rule: Dict, health_data: Dict) -> str:
"""确定规则跳过原因"""
required_fields = rule.get('required_fields', [])
for field in required_fields:
if field not in health_data or health_data[field] is None:
return f"missing {field}"
# 检查其他跳过条件
if rule.get('enabled', True) == False:
return "rule disabled"
if not _check_time_conditions(rule):
return "time condition not met"
if not _check_device_conditions(rule, health_data):
return "device condition not met"
return "unknown reason"
2. 数据完整性监控
2.1 健康数据字段完整性检查
class HealthDataCompletenessMonitor:
"""健康数据完整性监控器"""
def __init__(self):
self.field_stats = defaultdict(lambda: {'total': 0, 'missing': 0, 'null': 0})
self.stats_lock = threading.Lock()
# 定义预期的健康数据字段
self.expected_fields = {
'basic_vitals': ['heart_rate', 'blood_oxygen', 'temperature'],
'blood_pressure': ['pressure_high', 'pressure_low'],
'activity': ['step', 'distance', 'calorie'],
'location': ['latitude', 'longitude', 'altitude'],
'physical_signs': ['physical_sign'], # 导致告警规则跳过的字段
'sleep': ['sleep', 'sleepData'],
'exercise': ['exerciseDailyData', 'exerciseWeekData'],
'device': ['deviceSn', 'upload_method', 'timestamp']
}
logger.info("🔍 HealthDataCompletenessMonitor已启动")
def check_data_completeness(self, health_data: Dict, device_sn: str = 'unknown') -> Dict:
"""检查健康数据完整性"""
completeness_report = {
'device_sn': device_sn,
'timestamp': time.time(),
'field_categories': {},
'overall_completeness': 0.0,
'missing_fields': [],
'null_fields': []
}
total_fields = 0
complete_fields = 0
for category, fields in self.expected_fields.items():
category_stats = {
'total_fields': len(fields),
'complete_fields': 0,
'missing_fields': [],
'null_fields': [],
'completeness_rate': 0.0
}
for field in fields:
total_fields += 1
with self.stats_lock:
self.field_stats[field]['total'] += 1
if field not in health_data:
# 字段完全缺失
category_stats['missing_fields'].append(field)
completeness_report['missing_fields'].append(field)
with self.stats_lock:
self.field_stats[field]['missing'] += 1
elif health_data[field] is None or health_data[field] == 'null' or health_data[field] == '':
# 字段存在但值为空
category_stats['null_fields'].append(field)
completeness_report['null_fields'].append(field)
with self.stats_lock:
self.field_stats[field]['null'] += 1
else:
# 字段完整
category_stats['complete_fields'] += 1
complete_fields += 1
category_stats['completeness_rate'] = (
category_stats['complete_fields'] / category_stats['total_fields']
if category_stats['total_fields'] > 0 else 0
)
completeness_report['field_categories'][category] = category_stats
# 计算整体完整性
completeness_report['overall_completeness'] = (
complete_fields / total_fields if total_fields > 0 else 0
)
return completeness_report
def get_completeness_summary(self, time_window_minutes: int = 60) -> Dict:
"""获取数据完整性汇总统计"""
with self.stats_lock:
field_summary = {}
for field, stats in self.field_stats.items():
if stats['total'] > 0:
field_summary[field] = {
'total_checks': stats['total'],
'missing_rate': stats['missing'] / stats['total'],
'null_rate': stats['null'] / stats['total'],
'completeness_rate': 1 - (stats['missing'] + stats['null']) / stats['total']
}
return {
'time_window_minutes': time_window_minutes,
'field_completeness': field_summary,
'timestamp': time.time()
}
# 全局完整性监控器
health_completeness_monitor = HealthDataCompletenessMonitor()
2.2 集成到数据处理流程
def enhanced_health_data_processing(health_data, device_sn):
"""增强的健康数据处理,包含完整性检查"""
# 1. 数据完整性检查
completeness_report = health_completeness_monitor.check_data_completeness(
health_data, device_sn
)
# 2. 记录完整性问题
if completeness_report['overall_completeness'] < 0.8: # 80%完整性阈值
logger.warning(f"📊 数据完整性较低: device_sn={device_sn}, "
f"完整性={completeness_report['overall_completeness']:.2%}")
# 记录缺失的关键字段
missing_critical = set(completeness_report['missing_fields']).intersection({
'physical_sign', 'heart_rate', 'blood_oxygen', 'temperature'
})
if missing_critical:
logger.warning(f"🚨 关键字段缺失: {list(missing_critical)}")
# 3. 继续正常处理流程
return process_health_data(health_data, device_sn)
3. 监控API端点
3.1 告警系统监控API
@app.route('/api/alert_system/stats', methods=['GET'])
def alert_system_stats():
"""获取告警系统统计信息"""
try:
time_window = request.args.get('time_window', 60, type=int)
# 获取规则执行统计
rule_stats = alert_rule_monitor.get_rule_execution_summary(time_window)
# 获取数据完整性统计
completeness_stats = health_completeness_monitor.get_completeness_summary(time_window)
return jsonify({
'timestamp': time.time(),
'time_window_minutes': time_window,
'rule_execution': rule_stats,
'data_completeness': completeness_stats
})
except Exception as e:
logger.error(f"获取告警系统统计失败: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/alert_system/health', methods=['GET'])
def alert_system_health():
"""告警系统健康检查"""
try:
rule_stats = alert_rule_monitor.get_rule_execution_summary(60)
completeness_stats = health_completeness_monitor.get_completeness_summary(60)
# 计算健康状态
health_issues = []
overall_status = 'healthy'
# 检查规则执行健康状态
if 'summary' in rule_stats:
skip_rate = rule_stats['summary'].get('skip_rate', 0)
error_rate = rule_stats['summary'].get('error_rate', 0)
if error_rate > 0.1:
health_issues.append(f"高规则错误率: {error_rate:.2%}")
overall_status = 'unhealthy'
elif skip_rate > 0.5:
health_issues.append(f"高规则跳过率: {skip_rate:.2%}")
overall_status = 'degraded' if overall_status == 'healthy' else overall_status
# 检查数据完整性健康状态
field_completeness = completeness_stats.get('field_completeness', {})
critical_fields = ['heart_rate', 'blood_oxygen', 'physical_sign']
for field in critical_fields:
if field in field_completeness:
completeness_rate = field_completeness[field]['completeness_rate']
if completeness_rate < 0.5:
health_issues.append(f"关键字段{field}完整性低: {completeness_rate:.2%}")
overall_status = 'unhealthy'
return jsonify({
'status': overall_status,
'timestamp': time.time(),
'health_issues': health_issues,
'checks': {
'rule_execution_health': 'healthy' if not any('规则' in issue for issue in health_issues) else 'unhealthy',
'data_completeness_health': 'healthy' if not any('字段' in issue for issue in health_issues) else 'unhealthy'
},
'details': {
'rule_stats': rule_stats,
'completeness_stats': completeness_stats
}
})
except Exception as e:
logger.error(f"告警系统健康检查失败: {e}")
return jsonify({
'status': 'error',
'timestamp': time.time(),
'error': str(e)
}), 500
4. 实施建议
4.1 立即实施 (1-2天)
-
集成规则执行监控
- 在现有告警处理逻辑中添加监控代码
- 开始收集规则跳过统计
- 添加性能统计日志
-
添加监控API
- 实现
/api/alert_system/health健康检查端点 - 实现
/api/alert_system/stats统计查询端点
- 实现
4.2 后续优化 (3-5天)
-
完善数据完整性监控
- 实现HealthDataCompletenessMonitor
- 集成到数据处理流程
-
告警规则质量分析
- 分析经常被跳过的规则
- 优化规则配置
- 清理无效规则
4.3 预期改进效果
-
可观测性提升
- 告警规则执行情况完全透明
- 数据质量问题及时发现
- 系统健康状态实时监控
-
运维效率提升
- 快速定位告警规则问题
- 数据质量问题预警
- 自动化健康检查
-
系统稳定性提升
- 减少因数据问题导致的告警失效
- 提高告警系统可靠性
- 优化规则配置质量
总结
基于观察到的日志信息,这个监控增强方案针对性地解决了:
- 规则跳过问题: 通过监控统计跳过原因,识别数据质量问题
- 性能监控: 增强现有的性能统计,提供更详细的执行分析
- 数据完整性: 建立完整的字段缺失监控体系
- 系统健康: 提供告警系统的整体健康状态评估
这些改进可以帮助更好地理解和优化告警系统的运行状况,提高整体的可靠性和效率。