## 主要更改 ### 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 <[email protected]>
17 KiB
17 KiB
健康数据上传批处理流程优化方案
📋 目录
1. 系统概述
1.1 系统职责
ljwx-bigscreen 健康数据批处理系统负责处理来自可穿戴设备的健康数据上传,包括:
- 实时健康指标收集(心率、血氧、体温等)
- 数据验证和清洗
- 批量数据库存储
- Redis缓存更新
- 实时告警触发
1.2 技术栈
- 后端框架: Flask + SQLAlchemy
- 数据库: MySQL (主表 + 日/周分表)
- 缓存层: Redis
- 异步处理: ThreadPoolExecutor + Queue
- 监控: 专业日志系统
1.3 核心指标
- 并发处理: 支持多设备同时上传
- 吞吐量: 目标QPS >1400
- 响应时间: <3秒端到端处理
- 可靠性: 99.9%成功率
2. 当前架构分析
2.1 核心组件
HealthDataOptimizer (批处理引擎)
# 当前配置 (CPU自适应)
- 批次大小: CPU核心数 × 25 (50-500范围)
- 批处理超时: 2秒
- 线程池: CPU核心数 × 2.5 (4-32范围)
- 队列容量: 5000
数据存储层级
- 主表:
t_user_health_data(实时数据) - 日表:
t_user_health_data_daily(聚合数据) - 周表:
t_user_health_data_weekly(周度统计) - Redis: 实时缓存和发布订阅
2.2 处理流程架构
graph TB
A[设备上传] --> B[Flask API接口]
B --> C[数据验证和映射]
C --> D[批处理队列]
D --> E[批处理引擎]
E --> F[数据库批量插入]
E --> G[Redis更新]
E --> H[告警检测]
F --> F1[主表插入]
F --> F2[日表聚合]
F --> F3[周表聚合]
G --> G1[健康数据缓存]
G --> G2[发布订阅通知]
H --> H1[规则引擎]
H --> H2[微信通知]
3. 数据流程梳理
3.1 API接入层
POST /upload_health_data
├── 请求验证 (设备SN、客户信息)
├── 数据格式标准化
├── 字段映射处理
│ ├── upload_method: 4g → esim
│ ├── body_temperature → temperature
│ └── blood_pressure_* → pressure_*
└── 调用 optimized_upload_health_data()
3.2 批处理队列层
HealthDataOptimizer.add_data()
├── 确保批处理器启动
├── 客户信息解析 (user_id/org_id/customer_id)
├── 重复检测 (数据库查询)
├── 数据分类处理
│ ├── 主表数据: 快速更新字段
│ ├── 日表数据: 睡眠、运动等慢字段
│ └── 周表数据: 周度统计字段
├── Redis数据构建
└── 队列提交 (Queue.put)
3.3 批处理执行层
_batch_processor() 主循环
├── 队列监听 (2秒超时或批次满)
├── 批量数据收集
└── _flush_batch() 执行
├── 数据分离 (主表/日表/周表)
├── 并行数据库操作
│ ├── 主表批量插入 + 重复处理fallback
│ ├── 日表 upsert (检查+插入/更新)
│ └── 周表 upsert (检查+插入/更新)
├── Redis异步更新 (线程池)
└── 告警异步检测 (线程池)
3.4 数据库存储层
-- 主表插入 (实时数据)
INSERT INTO t_user_health_data
(device_sn, user_id, org_id, customer_id, heart_rate, blood_oxygen,
temperature, pressure_high, pressure_low, stress, step, distance,
calorie, latitude, longitude, altitude, sleep, timestamp, upload_method)
-- 日表聚合 (UPSERT模式)
INSERT/UPDATE t_user_health_data_daily
(device_sn, user_id, org_id, customer_id, date, sleep_data,
exercise_daily_data, workout_data)
-- 周表聚合 (UPSERT模式)
INSERT/UPDATE t_user_health_data_weekly
(device_sn, user_id, org_id, customer_id, week_start, exercise_week_data)
4. 性能瓶颈分析
4.1 数据库层瓶颈
4.1.1 重复检测开销
问题: 每条记录插入前都执行重复检测查询
SELECT id FROM t_user_health_data WHERE device_sn = ? AND timestamp = ?
影响: 大批量数据时,查询开销 = 插入开销
4.1.2 日/周表频繁UPSERT
问题: 每批次都检查并更新日表和周表
-- 每个设备每批次都执行
SELECT id FROM t_user_health_data_daily WHERE device_sn = ? AND date = ?
UPDATE/INSERT t_user_health_data_daily SET ... WHERE id = ?
影响: 锁争用和磁盘IO增加
4.1.3 事务粒度不当
问题: 批处理中多表操作未充分利用事务优势
- 主表、日表、周表分别提交
- 异常回滚范围不完整
4.2 并发处理瓶颈
4.2.1 队列单点处理
问题: 单一批处理器线程处理所有数据
while self.running:
item = self.batch_queue.get(timeout=timeout) # 串行处理
batch_data.append(item)
4.2.2 线程池资源竞争
问题: Redis更新和告警检测共享线程池
self.executor.submit(self._async_process, item) # 同一线程池
4.3 内存和资源瓶颈
4.3.1 批次大小静态配置
问题: 固定批次大小无法适应负载变化
self.batch_size = max(50, min(500, self.cpu_cores * 25)) # 静态值
4.3.2 内存泄漏风险
问题: 长期运行的已处理键值集合
self.processed_keys = set() # 无界增长
5. 优化建议方案
5.1 数据库层优化
5.1.1 【高优先级】批量UPSERT优化
建议: 使用MySQL的批量UPSERT语法减少查询次数
-- 优化前:N次查询 + N次插入/更新
SELECT id FROM t_user_health_data WHERE device_sn = ? AND timestamp = ?
INSERT INTO t_user_health_data ... ON DUPLICATE KEY UPDATE ...
-- 优化后:1次批量UPSERT
INSERT INTO t_user_health_data
(device_sn, user_id, ..., timestamp, upload_method)
VALUES
(?, ?, ..., ?, ?),
(?, ?, ..., ?, ?),
...
ON DUPLICATE KEY UPDATE
heart_rate = VALUES(heart_rate),
blood_oxygen = VALUES(blood_oxygen),
...;
预期收益: 减少50-70%的数据库交互次数
5.1.2 【高优先级】索引优化
建议: 优化重复检测和查询索引
-- 当前索引分析
SHOW INDEX FROM t_user_health_data;
-- 建议添加复合索引 (如不存在)
CREATE INDEX idx_device_timestamp ON t_user_health_data(device_sn, timestamp);
CREATE INDEX idx_customer_create_time ON t_user_health_data(customer_id, create_time);
CREATE INDEX idx_org_user_timestamp ON t_user_health_data(org_id, user_id, timestamp);
5.1.3 【中优先级】分区表策略
建议: 按时间分区主表,提高查询和插入性能
-- 主表按月分区
ALTER TABLE t_user_health_data
PARTITION BY RANGE (YEAR(create_time)*100 + MONTH(create_time)) (
PARTITION p202501 VALUES LESS THAN (202502),
PARTITION p202502 VALUES LESS THAN (202503),
...
PARTITION p_future VALUES LESS THAN MAXVALUE
);
5.2 并发处理优化
5.2.1 【高优先级】多队列并行处理
建议: 实现设备级别的分片队列
class ShardedBatchProcessor:
def __init__(self, shard_count=4):
self.shard_count = shard_count
self.queues = [queue.Queue(maxsize=1250) for _ in range(shard_count)]
self.processors = []
def _get_shard(self, device_sn: str) -> int:
return hash(device_sn) % self.shard_count
def add_data(self, data, device_sn):
shard_id = self._get_shard(device_sn)
self.queues[shard_id].put(data)
预期收益: 提高2-4倍并发处理能力
5.2.2 【中优先级】专用线程池分离
建议: 为不同类型的异步任务创建专用线程池
class SpecializedExecutors:
def __init__(self):
self.db_executor = ThreadPoolExecutor(max_workers=8, thread_name_prefix="db")
self.redis_executor = ThreadPoolExecutor(max_workers=4, thread_name_prefix="redis")
self.alert_executor = ThreadPoolExecutor(max_workers=6, thread_name_prefix="alert")
5.2.3 【低优先级】异步IO框架迁移
建议: 考虑使用asyncio替代线程池,减少上下文切换
import asyncio
import aioredis
import aiomysql
class AsyncBatchProcessor:
async def process_batch_async(self, batch_data):
tasks = [
self.insert_to_db_async(batch_data),
self.update_redis_async(batch_data),
self.check_alerts_async(batch_data)
]
await asyncio.gather(*tasks)
5.3 缓存和性能优化
5.3.1 【高优先级】智能批次调整
建议: 基于系统负载动态调整批次大小
class AdaptiveBatchSizer:
def __init__(self):
self.min_batch_size = 50
self.max_batch_size = 1000
self.current_batch_size = 200
self.performance_history = []
def adjust_batch_size(self, processing_time: float, batch_size: int):
throughput = batch_size / processing_time
# 基于吞吐量和系统资源调整
if throughput > self.target_throughput:
self.current_batch_size = min(self.max_batch_size,
int(batch_size * 1.2))
else:
self.current_batch_size = max(self.min_batch_size,
int(batch_size * 0.8))
5.3.2 【中优先级】Redis流水线优化
建议: 使用Redis Pipeline批量执行命令
def update_redis_batch(self, batch_data):
pipe = redis.pipeline()
for item in batch_data:
device_sn = item['device_sn']
redis_data = item['redis_data']
# 批量命令
pipe.hset(f"health_data:{device_sn}", mapping=redis_data)
pipe.publish(f"health_data_channel:{device_sn}", device_sn)
# 每100个命令执行一次
if len(pipe.command_stack) >= 100:
pipe.execute()
pipe = redis.pipeline()
# 执行剩余命令
if pipe.command_stack:
pipe.execute()
5.3.3 【中优先级】内存管理优化
建议: 实现更智能的内存管理策略
class MemoryOptimizedProcessor:
def __init__(self):
self.processed_keys = {} # 改为时间戳字典
self.cleanup_interval = 1800 # 30分钟清理一次
def cleanup_expired_keys(self):
cutoff_time = time.time() - 3600 # 保留1小时
expired_keys = [
key for key, timestamp in self.processed_keys.items()
if timestamp < cutoff_time
]
for key in expired_keys:
del self.processed_keys[key]
5.4 监控和可观测性优化
5.4.1 【高优先级】性能指标收集
建议: 实现全面的性能监控体系
@dataclass
class BatchProcessingMetrics:
batch_id: str
batch_size: int
processing_time: float
db_insert_time: float
redis_update_time: float
alert_check_time: float
success_count: int
duplicate_count: int
error_count: int
class MetricsCollector:
def __init__(self):
self.metrics_queue = queue.Queue()
self.start_metrics_reporter()
def record_batch_metrics(self, metrics: BatchProcessingMetrics):
self.metrics_queue.put(metrics)
def start_metrics_reporter(self):
def reporter():
while True:
try:
metrics = self.metrics_queue.get(timeout=60)
self.send_to_monitoring_system(metrics)
except queue.Empty:
continue
threading.Thread(target=reporter, daemon=True).start()
5.4.2 【中优先级】健康检查端点
建议: 实现专门的批处理系统健康检查
@app.route('/api/batch_processor/health')
def batch_processor_health():
optimizer_stats = optimizer.get_detailed_stats()
health_status = {
'status': 'healthy' if optimizer_stats['errors'] < 10 else 'degraded',
'queue_size': optimizer.batch_queue.qsize(),
'processed_total': optimizer_stats['processed'],
'error_rate': optimizer_stats['errors'] / max(optimizer_stats['processed'], 1),
'avg_batch_size': optimizer_stats.get('avg_batch_size', 0),
'throughput_per_second': optimizer_stats.get('throughput', 0)
}
return jsonify(health_status)
5.4.3 【低优先级】分布式链路追踪
建议: 集成分布式追踪系统 (如Jaeger)
from opentelemetry import trace
from opentelemetry.exporter.jaeger import JaegerExporter
class TracedBatchProcessor:
def __init__(self):
self.tracer = trace.get_tracer(__name__)
def process_with_tracing(self, batch_data):
with self.tracer.start_as_current_span("batch_processing") as span:
span.set_attribute("batch_size", len(batch_data))
with self.tracer.start_as_current_span("database_insert"):
self.insert_to_database(batch_data)
with self.tracer.start_as_current_span("redis_update"):
self.update_redis(batch_data)
6. 实施优先级
6.1 第一阶段 (高优先级) - 立即实施
| 优化项 | 预期收益 | 实施难度 | 风险等级 |
|---|---|---|---|
| 批量UPSERT优化 | 50-70%性能提升 | 中 | 低 |
| 索引优化 | 30-50%查询提升 | 低 | 极低 |
| 多队列并行处理 | 2-4倍并发提升 | 高 | 中 |
| 智能批次调整 | 20-30%吞吐量提升 | 中 | 低 |
| 性能指标收集 | 可观测性提升 | 低 | 极低 |
实施时间: 2-3周
预期整体性能提升: 100-200%
6.2 第二阶段 (中优先级) - 后续实施
| 优化项 | 预期收益 | 实施难度 | 风险等级 |
|---|---|---|---|
| 专用线程池分离 | 资源利用率提升 | 中 | 低 |
| Redis流水线优化 | Redis性能提升30% | 低 | 极低 |
| 内存管理优化 | 内存稳定性提升 | 低 | 极低 |
| 分区表策略 | 大数据量性能提升 | 高 | 中 |
| 健康检查端点 | 运维能力提升 | 低 | 极低 |
实施时间: 3-4周
预期整体性能提升: 50-80%
6.3 第三阶段 (低优先级) - 长期规划
| 优化项 | 预期收益 | 实施难度 | 风险等级 |
|---|---|---|---|
| 异步IO框架迁移 | 资源效率大幅提升 | 极高 | 高 |
| 分布式链路追踪 | 问题诊断能力提升 | 高 | 中 |
实施时间: 4-6周
预期整体性能提升: 100-300% (但实施风险高)
7. 监控和度量
7.1 核心性能指标 (KPI)
7.1.1 吞吐量指标
- 批处理QPS: 每秒处理的批次数量
- 目标: >50 batches/s
- 当前: ~20 batches/s
- 数据处理TPS: 每秒处理的健康数据记录数
- 目标: >2000 records/s
- 当前: ~800 records/s
7.1.2 延迟指标
- 端到端延迟: 从API接收到数据库插入完成
- 目标: P95 < 3秒
- 当前: P95 ~5-8秒
- 队列等待时间: 数据在批处理队列中的等待时间
- 目标: P95 < 1秒
- 当前: P95 ~2-3秒
7.1.3 可靠性指标
- 成功率: 批处理成功率
- 目标: >99.9%
- 当前: ~99.5%
- 重复率: 重复数据检测和处理率
- 目标: <5%
- 当前: ~8-12%
7.2 资源使用监控
7.2.1 内存监控
def get_memory_metrics():
process = psutil.Process()
return {
'memory_usage_mb': process.memory_info().rss / 1024 / 1024,
'memory_percent': process.memory_percent(),
'queue_size': optimizer.batch_queue.qsize(),
'processed_keys_count': len(optimizer.processed_keys)
}
7.2.2 数据库连接监控
-- 监控数据库连接状态
SELECT
VARIABLE_NAME,
VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
'Threads_connected',
'Threads_running',
'Questions',
'Queries'
);
7.3 告警规则
7.3.1 性能告警
- 批处理队列大小 > 1000 (5分钟内)
- 平均处理时间 > 5秒 (5分钟内)
- 错误率 > 1% (10分钟内)
- 内存使用率 > 80% (持续5分钟)
7.3.2 可用性告警
- 批处理器停止运行
- 数据库连接失败
- Redis连接失败
- 线程池异常退出
7.4 监控面板
建议在Grafana中创建专门的批处理监控面板,包含:
-
实时处理指标
- 每秒处理数据量 (时间序列图)
- 批处理队列大小 (实时计数器)
- 平均批处理时间 (时间序列图)
-
错误和异常监控
- 错误率趋势 (百分比图表)
- 重复数据率 (百分比图表)
- 异常类型分布 (饼图)
-
资源使用监控
- CPU使用率 (时间序列图)
- 内存使用率 (时间序列图)
- 数据库连接数 (时间序列图)
总结
通过以上优化方案的实施,预期可以实现:
- 性能提升: 整体处理性能提升150-300%
- 可靠性增强: 成功率提升至99.9%以上
- 可扩展性: 支持更高并发和数据量
- 可观测性: 完善的监控和告警体系
- 可维护性: 更清晰的代码结构和错误处理
建议按照优先级分阶段实施,先实现高优先级的优化项获得快速收益,再逐步完善中低优先级的改进项。