1. 项目背景与需求分析在物联网平台ThingsBoard的实际应用中电能量数据统计是一个关键功能。传统方案使用触发器实时计算功率因素平均值但对于用电量这种需要分时段统计的场景存在明显不足。核心痛点在于尖峰平谷时段定义存储在客户JSON配置中触发器实时解析会极大影响性能设备注册流程中存在先上报数据后分配客户的时间差导致触发器无法获取时段配置月报统计通常只需截至昨日数据实时性并非必要需求实测表明当设备数量超过5000台时触发器方案会导致数据库CPU负载长期超过80%严重影响系统稳定性。这促使我们转向更高效的定时批处理方案。2. 数据库结构优化方案2.1 表结构扩展设计为支持分时段统计我们对ts_kv_daily表进行了垂直分片扩展ALTER TABLE ts_kv_daily ADD COLUMN sum_value_a DOUBLE PRECISION DEFAULT 0; ALTER TABLE ts_kv_daily ADD COLUMN sum_value_b DOUBLE PRECISION DEFAULT 0; ALTER TABLE ts_kv_daily ADD COLUMN sum_value_c DOUBLE PRECISION DEFAULT 0; ALTER TABLE ts_kv_daily ADD COLUMN sum_value_d DOUBLE PRECISION DEFAULT 0;字段设计考虑保留原sum_value作为总量汇总新增a/b/c/d四个时段字段对应尖/峰/平/谷采用DOUBLE PRECISION确保高精度计算默认值0避免NULL值计算问题2.2 时段规则解析函数创建专用函数解析客户配置的JSON规则CREATE OR REPLACE FUNCTION public.parse_electric_quantity_rule( p_rule jsonb ) RETURNS TABLE( month integer, time_type text, start_time time, end_time time ) LANGUAGE sql AS $BODY$ SELECT (e-month)::integer, e-timeType, (e-startTime)::time, (e-endTime)::time FROM jsonb_array_elements(p_rule-rules) e $BODY$;该函数处理形如以下的JSON配置{ rules: [ { month: 7, timeType: times_a, startTime: 08:00:00, endTime: 11:00:00 } ] }3. 核心聚合函数实现3.1 函数主体结构CREATE OR REPLACE FUNCTION public.put_ts_kv_daily_b( p_date date, p_key integer ) RETURNS void LANGUAGE plpgsql AS $BODY$ DECLARE v_start_ts BIGINT; v_end_ts BIGINT; v_daily_ts BIGINT; BEGIN -- 时间戳计算 v_start_ts : (EXTRACT(EPOCH FROM (p_date::text || 00:00:00 Asia/Shanghai)::timestamptz ) * 1000)::BIGINT; v_end_ts : v_start_ts (24 * 60 * 60 * 1000) - 1; v_daily_ts : v_start_ts; -- 主聚合逻辑 WITH customer_parsed_rules AS ( -- 规则解析子查询 ) INSERT INTO ts_kv_daily ( -- 字段列表 ) SELECT -- 聚合计算 FROM ts_kv a -- 多表关联 WHERE a.key p_key AND a.ts v_start_ts AND a.ts v_end_ts GROUP BY a.entity_id ON CONFLICT (entity_id, key, ts) DO UPDATE SET -- 冲突更新 RAISE NOTICE 日统计完成: 日期%, key%, p_date, p_key; END; $BODY$;3.2 关键实现细节时间范围计算使用EXTRACT(EPOCH FROM...)获取Unix时间戳乘以1000转换为毫秒级时间戳考虑时区转换Asia/Shanghai规则解析CTESELECT ak.entity_id as customer_id, per.month, per.time_type, per.start_time, per.end_time FROM attribute_kv ak INNER JOIN key_dictionary kd ON kd.key_id ak.attribute_key JOIN public.parse_electric_quantity_rule(ak.json_v::jsonb) per ON TRUE WHERE kd.key 尖峰平谷分时段聚合逻辑SUM(CASE WHEN cpr.time_type times_a THEN COALESCE(a.dbl_v, a.long_v::DOUBLE PRECISION) ELSE 0 END)::NUMERIC(22,2) as sum_value_a冲突处理ON CONFLICT (entity_id, key, ts) DO UPDATE SET sum_value EXCLUDED.sum_value, -- 其他字段更新4. 定时任务配置4.1 pgAgent作业设置-- 每天凌晨2点执行 SELECT pgagent.pga_job_add( 每日电能量汇总, 00:02:00::interval, true, SELECT put_ts_kv_daily_b(CURRENT_DATE - 1, 98); );4.2 执行策略优化错峰执行选择业务低峰期凌晨2点分批处理超过10万设备时按customer_id分片失败重试配置最多3次重试机制5. 性能对比测试测试环境配置CPU: 16核 Intel Xeon内存: 32GBPostgreSQL 12.5测试数据量: 1000万条ts_kv记录方案执行时间CPU峰值锁等待时间触发器方案持续高负载85%1200ms批处理方案23分钟62%50ms优化效果查询性能提升4倍数据库负载降低30%锁争用减少98%6. 常见问题处理6.1 时区不一致问题现象统计结果时间范围偏移 解决方案-- 确保函数内时区与系统一致 SET TIME ZONE Asia/Shanghai;6.2 规则解析失败排查步骤检查attribute_kv表中key尖峰平谷的记录验证JSON格式是否符合规范测试parse_electric_quantity_rule函数6.3 数据重复计算预防措施-- 执行前清除当日已有数据 DELETE FROM ts_kv_daily WHERE key p_key AND ts v_daily_ts;7. 扩展应用场景本方案同样适用于分时段计费的水气数据统计生产设备分班次运行时长统计交通流量高峰时段分析实际案例某电网项目应用后月报生成时间从3小时缩短到15分钟同时支持了更灵活的分时电价统计需求。
阅读完成 · 觉得有帮助?