4. 数据存储方案设计
数据存储这块,说实话,是仿真回放系统里最容易翻车的地方。我见过不少团队,前期图省事直接上 MySQL,结果数据量一上来,查询慢得像蜗牛爬。今天咱们就聊聊时序数据库的选型、分区策略,还有冷热数据分离这些实战问题。
4.1 为什么非得用时序数据库?
仿真数据有个特点:时间戳密集、写入量大、查询多是范围扫描。普通关系型数据库处理这种场景,性能会急剧下降。我曾在项目中用 PostgreSQL 存过仿真日志,单表几千万条后,一个简单的范围查询就要十几秒。后来换了时序数据库,同样的查询,毫秒级返回。
说白了,时序数据库就是为这种场景量身定做的。它做了三件事:
- 列式存储:按时间列组织数据,压缩率高
- 预聚合:自动计算分钟级、小时级的统计值
- 自动过期:可以设置数据保留策略,省心
4.2 两大主流方案:InfluxDB vs TimescaleDB
目前市面上最常用的就是 InfluxDB 和 TimescaleDB。我两个都用过,说说我的感受。
| 对比维度 | InfluxDB | TimescaleDB |
|---|---|---|
| 底层引擎 | 自研 TSM 引擎 | 基于 PostgreSQL |
| 写入性能 | 极高,单机百万点/秒 | 较高,依赖 PG 调优 |
| 查询语法 | 类 SQL(Flux/InfluxQL) | 标准 SQL |
| 数据压缩 | 优秀,通常 10:1 | 良好,依赖 PG 压缩 |
| 运维复杂度 | 中等,集群版需付费 | 低,PG 生态成熟 |
| 适用场景 | 高频写入、轻量查询 | 复杂分析、关联查询 |
我的建议:如果仿真数据量巨大(每天上亿条),且查询模式简单(按时间范围取原始值),选 InfluxDB。如果数据需要和业务表做关联分析,或者团队对 SQL 更熟悉,选 TimescaleDB。
4.3 数据分区策略:别让一张表扛下所有
嗯,这里要注意。不管选哪个数据库,分区都是必须做的。我见过有人把一年数据塞进一张表,结果查询时全表扫描,CPU 直接飙到 100%。
分区策略一般有两种:
- 按时间分区:比如按天、按周分区。这是最常用的方式。查询时只扫描相关分区,性能提升明显。
- 按标签分区:比如按仿真场景 ID、按设备 ID 分区。适合多租户场景。
以 TimescaleDB 为例,创建超表并分区:
-- 创建普通表
CREATE TABLE sim_data (
time TIMESTAMPTZ NOT NULL,
device_id INT NOT NULL,
scenario_id INT NOT NULL,
value DOUBLE PRECISION,
metadata JSONB
);
-- 转换为超表,按天分区
SELECT create_hypertable('sim_data', 'time',
chunk_time_interval => INTERVAL '1 day');
-- 还可以按 device_id 做空间分区
SELECT add_dimension('sim_data',
by_hash('device_id', 4));
小技巧:分区粒度别太细。按小时分区的话,分区数太多,元数据管理开销反而大。我个人习惯按天分区,数据量特别大的场景按 6 小时分区。
4.4 冷热数据分离:省钱又高效
仿真数据有个特点:越新的数据越常被查。一周前的数据,访问频率可能下降 90%。这时候把冷热数据混在一起存,就是浪费钱。
我曾经在项目中踩过坑:把所有数据都放在 SSD 上,一个月后存储成本翻了三倍。后来做了冷热分离,成本直接降了 60%。
冷热分离的核心思路:
- 热数据:最近 7 天的数据,存在高性能存储(SSD),保留原始精度。
- 温数据:7 天到 3 个月的数据,存在普通 HDD,可以降采样(比如 1 秒精度降到 1 分钟精度)。
- 冷数据:3 个月以上的数据,归档到对象存储(如 S3、MinIO),只保留统计摘要。
InfluxDB 的保留策略配置:
-- 创建保留策略
CREATE RETENTION POLICY "hot_policy"
ON "simdb" DURATION 7d REPLICATION 1 SHARD DURATION 1d;
CREATE RETENTION POLICY "warm_policy"
ON "simdb" DURATION 90d REPLICATION 1 SHARD DURATION 7d;
-- 创建连续查询,自动降采样
CREATE CONTINUOUS QUERY "cq_1m" ON "simdb"
BEGIN
SELECT mean(value) INTO "simdb"."warm_policy"."sim_data_1m"
FROM "simdb"."hot_policy"."sim_data"
GROUP BY time(1m), device_id, scenario_id
END;
注意:降采样会丢失数据细节。如果后续需要回放原始数据,一定要保留一份原始副本。我建议在冷数据归档前,先做一次完整性校验。
4.5 整体架构图
下面这张图展示了数据存储的完整流程,从写入到冷热分离:
4.6 避坑指南
最后分享几个我踩过的坑:
- 分区键选择不当:我曾经用时间戳做唯一分区键,结果数据分布不均匀,某些分区特别大。后来加了设备 ID 做二级分区,问题解决。
- 忘记设置数据过期:InfluxDB 默认保留策略是永久。如果不设置,磁盘会慢慢被撑爆。我建议上线第一天就配好保留策略。
- 冷数据恢复测试:归档到对象存储后,一定要定期做恢复测试。我遇到过归档文件损坏,结果恢复时才发现,那叫一个惨。
总结一下:选型看场景,分区按时间,冷热要分离。这套方案我在多个项目中验证过,稳定运行两年没问题。