Files
Web_BLS_Heartbeat_Server/docs/db-heartbeat-schema.md
XuJiacheng eb94aaf92b feat: 升级心跳数据库为高吞吐日分区模型(v2)
- 新增 heartbeat 数据库与表结构文档,描述心跳明细表设计及字段约束。
- 新增 OpenSpec 符合性说明文档,指出与规范的一致点及偏差。
- 新增 Kafka 心跳数据推送说明文档,定义消息格式与推送方式。
- 更新数据库创建脚本,支持 UTF-8 编码与中文排序规则。
- 更新心跳表结构脚本,定义主表及索引,采用 ts_ms 日分区。
- 实现自动分区机制,确保按天创建分区以支持高吞吐写入。
- 添加数据库应用脚本,自动执行 SQL 文件并验证表结构。
- 添加运行时烟雾测试脚本,验证数据库连接与基本操作。
- 添加完整的烟雾测试脚本,验证数据插入与分区创建。
2026-01-12 19:53:27 +08:00

88 lines
4.4 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.
# Heartbeat 数据库与表结构v2 草案)
本文档描述 PostgreSQL 中 `heartbeat` 数据库的心跳明细表设计,用于高吞吐写入与按酒店/时间范围检索。
## 1. 数据库与命名空间
- 数据库:使用既有业务库(默认 `log_platform`,以 `src/config/config.js` 为准)
- Schema`heartbeat`
- 编码:数据库需为 UTF-8执行器会输出并提示
- 排序规则/字符类型:若数据库不是中文 locale可通过 ICU collation 在列级/表达式级实现中文排序(如确有严格要求)。
## 2. 主表
- 表名:`heartbeat.heartbeat_events`
- 分区:按 `ts_ms`epoch 毫秒)**按天 RANGE 分区**
对应脚本:
- `scripts/db/010_heartbeat_schema.sql`
- `scripts/db/020_partitioning_auto_daily.sql`
### 2.1 字段列表
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| id | bigserial | 否(自动生成) | 自增序列号(写入时可不提供) |
| ts_ms | bigint | 是 | 毫秒级时间戳epoch ms |
| hotel_id | int2 | 是 | 酒店编号 |
| room_id | int4 | 是 | 房间编号(或房间唯一标识) |
| device_id | varchar(64) | 是 | 设备 ID序列号/MAC/混合编码);如明确为纯数字可改 bigint |
| ip | inet | 是 | 设备/上报方 IPPostgreSQL inet 类型自带格式校验) |
| power_state | int2 | 是 | 取电状态(枚举值待标准化) |
| guest_type | int2 | 是 | 住客身份(住客/空房/保洁/维修等,枚举值待标准化) |
| cardless_state | int2 | 是 | 无卡取电/无卡策略状态(枚举待定) |
| service_mask | bigint | 是 | 服务位图/场景位图(需求指定 BRIN 索引) |
| pms_state | int2 | 是 | PMS 状态(枚举待定) |
| carbon_state | int2 | 是 | 碳控状态(枚举待定) |
| device_count | int2 | 是 | 设备数量/上报设备数量(语义待确认) |
| comm_seq | int2 | 是 | 通讯序号(语义待确认) |
| extra | jsonb | 否 | 可扩展字段:电参/空调状态/版本/来源等 |
### 2.2 约束
- 所有必填字段:`NOT NULL`
- `ip`:使用 `inet` 类型(天然校验 IPv4/IPv6 格式)
-`int2/int4`:当前脚本采用“非负 + 上界”CHECK避免枚举未来扩展造成写入失败
- 如需更强的枚举约束,建议在确认枚举标准后改为 `IN (...)``BETWEEN` 更小范围。
### 2.3 主键(重要说明)
需求写“主键id(bigserial)”,但 **PostgreSQL 分区表的主键/唯一约束通常必须包含分区键**
脚本采用:
- `PRIMARY KEY (ts_ms, id)`
原因:保证分区表可创建、约束可落地。
## 3. 分区策略与自动分区
- 分区键:`ts_ms`
- 粒度按天Asia/Shanghai自然日
- 自动分区:通过“预创建分区”的方式实现(安装时预建昨天~未来 7 天),并提供函数供服务启动/定时任务调用
调用方式:
- SQL`SELECT heartbeat.ensure_partitions(current_date, current_date + 30);`
- Node执行 `npm run db:apply`(会应用脚本并预创建分区)
风险与建议:
- PostgreSQL 在单条 INSERT 执行过程中对父分区表执行 `CREATE TABLE .. PARTITION OF` 会触发锁/使用中限制,导致写入失败;因此不建议“插入时动态建分区”。
- 推荐每日提前创建未来 N 天分区(例如外部调度/运维脚本或服务启动时调用 `heartbeat.ensure_partitions`)。
## 4. 索引设计
需求指定:
- B-tree`hotel_id`, `power_state`, `guest_type`, `device_id`
- BRIN`service_mask`
额外建议(脚本默认包含,可按需移除):
- `btree (hotel_id, ts_ms)`:覆盖最常见过滤(酒店 + 时间范围),显著提升检索与分区内扫描效率。
## 5. 查询性能影响分析(分区)
- 优点:
- 时间范围查询触发分区裁剪(只扫命中的日分区)
- 冷热数据按分区自然分层,便于归档/清理
- 代价:
- 跨大量分区的查询会增加计划时间与元数据开销
- 需要运维策略预建分区、定期维护索引、vacuum/analyze
## 6. 性能优化建议(高吞吐)
- 写入使用批量写入COPY 或 multi-row INSERT并控制批大小例如 500~5000按网络与锁争用调优
- 分区:建议预创建未来 7~30 天分区;触发器只做兜底
- 统计:对 Grafana 读取的 1m/5m/1h 聚合建议做物化视图或汇总表(避免每次扫明细)
- 维护:
- 定期 `VACUUM (ANALYZE)` 各分区
- 监控 bloat 与 autovacuum 参数