docs: define minimal vehicle storage contract

This commit is contained in:
lingniu
2026-07-02 19:38:24 +08:00
parent 8fd38eb87e
commit be559c0f99
3 changed files with 448 additions and 0 deletions

View File

@@ -41,6 +41,8 @@ flowchart LR
## Minimal Storage Contract
详细的落库边界见 [车辆数据最小落库合约](storage-minimal-contract.md)。
| Store | Table or key | Purpose | Keep | Avoid |
| --- | --- | --- | --- | --- |
| TDengine | `raw_frames` | Replay and audit evidence | raw hex/text, full parsed JSON, parse status, protocol tags | business-only duplicated columns |

View File

@@ -0,0 +1,70 @@
# 车辆数据最小落库合约
## 目标
车辆接入系统只持久化能回答业务问题、能回放纠错、能支撑高频查询的数据。协议细节默认保存在 raw不向上层业务表扩散。
## 第一性原则
1. raw 是证据层,必须能证明收到过什么、解析成什么。
2. Kafka 是回放层,消费者失败后优先靠 Kafka lag 追平。
3. Redis 是当前态缓存,不是历史库。
4. TDengine 只承担高写入时间序列raw、位置历史。
5. MySQL 只承担低基数业务状态:身份映射、实时快照、日指标。
6. 同一个事实只落一个主表,其他表只存查询必要的投影。
7. 新字段先进入 `parsed_json`,只有稳定查询需求出现后才提升为列。
## 目标表边界
| 存储 | 表/Key | 保留内容 | 不保留内容 |
| --- | --- | --- | --- |
| TDengine | `raw_frames` | raw hex/text、完整 `parsed_json`、解析状态、协议标签、车辆标识标签 | `fields_json` 这类可从 `parsed_json`/业务表再得到的重复字段 |
| TDengine | `raw_frame_payload_chunks` | 超长 raw/parsed payload 分片 | 业务查询字段 |
| TDengine | `vehicle_locations` | 时间、VIN/协议标签、经纬度、速度、方向、SOC、总里程等位置核心字段 | 完整协议 JSON、注册鉴权信息 |
| MySQL | `vehicle_realtime_snapshot` | 每个协议+VIN 的最新事件时间、接收时间、车牌、事件 ID | phone、device、source endpoint、完整 JSON |
| MySQL | `vehicle_realtime_location` | 每个协议+VIN 的最新位置核心字段 | raw、parsed JSON、消息头内部字段 |
| MySQL | `vehicle_daily_metric` | 日期、VIN/临时 vehicle key、协议、指标名、指标值、首末总里程、样本数 | 每帧细节、位置点列表 |
| MySQL | `vehicle_identity_binding` | 人工维护或导入的 VIN、车牌、phone、device_id 映射 | 注册历史 |
| MySQL | `jt808_registration` | JT808 phone 主键下的注册、鉴权、VIN 匹配状态 | GB32960/MQTT 注册信息 |
| Redis | `vehicle:realtime-raw:{protocol}:{vin}` | 每协议最新完整 parsed 状态 | 历史数据、统计结果 |
## 当前应收敛的重复点
1. `vehicle_mileage_points``vehicle_locations.total_mileage_km` 重复。
- 目标:停止写入 `vehicle_mileage_points`
- 兼容:`/api/history/mileage-points` 改为从 `vehicle_locations` 读取 `total_mileage_km IS NOT NULL`
- 删除:确认线上无直接读表后,再删除 TDengine stable 和子表。
2. `raw_frames.fields_json``raw_frames.parsed_json``vehicle_locations` 重复。
- 目标raw 只保留完整 `parsed_json`,核心查询字段进入 `vehicle_locations`
- 兼容:先让 raw 查询返回不依赖 `fields_json`
- 删除:等历史查询和前端不再展示 `fields_json` 后删除列。
3. `vehicle_daily_metric``vehicle_key``vin` 同时存在。
- 当前保留原因JT808 可能先用 phone 作为临时 key后续才绑定 VIN。
- 收敛方向:业务查询以 VIN 为主;没有 VIN 的数据只用于排查和待绑定,不作为正式车辆指标。
## 字段提升规则
协议字段进入系统后分三层:
1. `parsed_json`:默认入口,保存完整结构化解析。
2. 核心列只有跨协议稳定查询需要时才提升例如经纬度、速度、SOC、总里程。
3. 指标表:只有聚合口径稳定、产品需要分页/排序/报表时才持久化。
反例:
- 不因为某个协议字段存在就增加 MySQL 列。
- 不为了方便调试在 snapshot/location 放完整 JSON。
- 不为 telemetry field 配置服务提前复制一份字段表;字段配置服务应从 raw/Kafka 回放生成自己的结果。
## 删除或迁移顺序
1. 先写测试证明旧 API 能从新主表读到同等结果。
2. 改代码停止写重复表或重复列。
3. 部署后观察 Kafka lag、writer 成功计数、API 结果。
4. 查询生产库确认旧表不再新增。
5. 做一次备份或导出。
6. 删除旧表或旧列。
任何一步无法证明安全,就停在兼容状态,不直接删生产数据。