海量轨迹数据存储与分区优化

轨迹数据是典型的”海量时序 + 空间”双重特征的数据,存储设计不好,查询直接卡死。本文复盘一次轨迹数据存储方案的演进。

轨迹数据的特征

以巡检人员定位为例,每个终端每隔几秒上报一个 GPS 点,一天下来就是海量数据:

  • 单终端:约 2 万点/天
  • 数百终端:数百万点/天
  • 一年:上亿条

轨迹点有三个维度的特征:时间(何时)、空间(在哪)、对象(谁的)。查询场景也围绕这三者展开。

演进一:单表裸奔

最初所有轨迹点堆在一张表里,按时间倒序查最近轨迹还算快(走时间索引)。但一旦查”某个时间段 + 某个区域”的历史轨迹,就要在几千万行里扫描,查询直接超时。

问题本质:单表没有利用时间这个天然的切分维度。

演进二:按时间分区

轨迹数据最有效的优化是按时间分区。PostgreSQL 原生支持分区表:

1
2
3
4
5
6
7
8
9
10
CREATE TABLE track_points (
id BIGSERIAL,
device_id VARCHAR(32),
geom GEOMETRY(Point, 4326),
ts TIMESTAMP
) PARTITION BY RANGE (ts);

-- 按月建分区
CREATE TABLE track_points_2024_11 PARTITION OF track_points
FOR VALUES FROM ('2024-11-01') TO ('2024-12-01');

按时间分区后,查某个月的数据只需扫描对应分区,而不是全表。

演进三:复合优化

时间分区解决了一半问题,但”某区域内的轨迹”仍是难点。进一步优化:

  1. 空间索引:分区内再建 GiST 空间索引,ST_DWithin 走索引
  2. 对象维度索引:(device_id, ts) 联合索引,加速单终端轨迹查询
  3. 冷热分离:热数据(近期)留 SSD,冷数据归档到成本更低的存储

轨迹点的抽稀

还有一个业务层面的优化——轨迹抽稀。原始 GPS 点大量是冗余的(车辆停着不动也在上报)。用道格拉斯-普克算法(Douglas-Peucker)抽稀,保留轨迹形态的前提下把点数量降低一个数量级,存储和渲染压力都大幅下降。

小结

海量轨迹数据的存储设计,核心是抓住时间分区这个主线,再叠加空间索引和对象索引。设计对了,上亿数据也能秒级查询;设计错了,几百万就卡死。这类”数据量驱动的架构决策”,正是后端工程师和架构师的分水岭。