深度解析:轨迹数据抽稀到底该放数据库还是 Java?(以 56800 条数据为例)

在低空飞服项目开发中,轨迹数据量通常非常大,一个飞行器一次飞行就可能产生数万条轨迹点。如果直接返回给前端,地图渲染会卡顿,因此必须做 抽稀(down sampling)

很多同学第一反应是:在 SQL 中抽稀

例如经典写法:

复制代码
SELECT *
FROM (
   SELECT t.*, ROW_NUMBER() OVER(ORDER BY update_time) AS rn
   FROM th_track t
   WHERE callsign = #{callsign}
) tmp
WHERE rn % 10 = 0
ORDER BY update_time

看上去减少了返回数据量,但实际是否真的减少了数据库压力?

查询真的会更快吗?

本文以实际项目中的 56800 条轨迹数据规模 进行分析。


❗ 结论先行:这种 SQL 抽稀方式不会减少数据库查询耗时

原因很简单:

数据库仍然必须执行:

  1. 全表扫描(56800 行)

  2. ORDER BY 排序(消耗 CPU)

  3. ROW_NUMBER() 窗口函数计算(消耗更多 CPU)

  4. 最后才过滤掉 90% 的数据

也就是说:

你只减少了前端接收的数据量,但数据库的工作量反而变大了。

特别是在 DM8 / DM7 上,窗口函数性能并不算强,这类 SQL 抽稀往往比简单查询更慢。


📊 56800 行轨迹数据的规模分析

轨迹表 th_track 一般包含:

  • 经纬度

  • 高度

  • 航向

  • 速度

  • UAV 状态

  • JSON 信号包

  • 扩展字段

如果 SELECT *,每条记录约 0.3KB~1.2KB。

56800 条数据约:

约 20MB ~ 60MB I/O 扫描量

这种 I/O 才是数据库最耗时的部分,

抽稀并不能减少这部分的消耗。


🚀 那应该怎么优化?

为了提升整体性能,我们必须明确目标:

层级 目标
数据库 尽可能减少扫描和排序
后端 控制前端数据量
前端 不卡顿、不掉帧

下面是几个行之有效的优化方案。


⭐ 方案 1:抽秒式 SQL 抽稀(数据库真正减少扫描量)【推荐】

如果 update_time 较规律(精确到秒),

可以用"按秒取样"代替窗口函数:

复制代码
SELECT id, lon, lat, altitude, update_time
FROM th_track
WHERE callsign = #{callsign}
  AND MOD(EXTRACT(SECOND FROM update_time), 10) = 0
ORDER BY update_time

优势:

  • 不需要排序

  • 不需要窗口函数

  • 扫描量明显减少

  • DM 数据库执行极快

这是 DM 的最佳实践之一。


⭐ 方案 2:只查询必要字段(最有效)

替换 SELECT *:

复制代码
SELECT id, lon, lat, altitude, speed, update_time
FROM th_track
WHERE callsign = #{callsign}
ORDER BY update_time

好处:

数据从 60MB → 10MB

DB I/O 直接降低 3~8 倍

这是所有优化中 影响最大 的。


⭐ 方案 3:Java 层抽稀(最稳健,推荐给 DM)

数据库只管查,不管抽稀:

复制代码
if (list.size() > 20000) {
    List<Track> slim = new ArrayList<>();
    for (int i = 0; i < list.size(); i += interval) {
        slim.add(list.get(i));
    }
    list = slim;
}

特点:

  • 不给数据库增加额外负担

  • 控制逻辑更灵活

  • 性能受 JVM 控制,易评估

  • 前端数据量仍然可控

对于 56800 条数据,Java 抽稀耗时只有 1~3ms


⭐ 方案 4:SQL + FETCH 限流(保障数据库不炸)

复制代码
SELECT t.*, ROW_NUMBER() OVER(ORDER BY update_time) AS rn
FROM th_track t
WHERE callsign = #{callsign}
FETCH FIRST 20000 ROWS ONLY

效果:

  • 限制最大扫描行数

  • 防止异常轨迹量导致数据库卡顿

不过仍然属于"治标不治本"。


🔥 最佳策略(推荐优先级)

① 数据库减负 > ② 后端控制 > ③ 前端不卡

最终最佳组合:

✔ 1. 不用窗口函数抽稀

(对 DM 性能不友好)

✔ 2. 不用 SELECT *

(避免大量字段 I/O)

✔ 3. 使用"按秒抽稀"或 Java 抽稀

(稳定、高效、无副作用)

✔ 4. back-end 抽稀控制最终返回数据量

(例如不超过 5000 点)


🎯 最后总结(非常重要)

方式 是否减轻 DB 压力 是否减少前端数据 是否推荐
窗口函数 SQL 抽稀 ❌ 否,反而更慢 ✔ 是 ⚠️ 不推荐
按秒抽稀 SQL ✔ 大幅降低 ✔ 是 ⭐ 强烈推荐
Java 抽稀 ✔ 不增加 DB 负担 ✔ 是 ⭐ 强烈推荐
LIMIT 限流 ✔ 避免爆表 ➖ 一般 可选
相关推荐
曹牧24 分钟前
Oracle中ROW_NUMBER() OVER()
java·数据库·sql
客梦26 分钟前
数据结构-哈希表
java·数据结构·笔记
Yolo566Q27 分钟前
基于ArcGIS、InVEST与RUSLE水土流失模拟及分析
开发语言·python
jnrjian28 分钟前
MOS oracle rman backup 脚本
数据库·oracle
草原印象29 分钟前
Spring SpringMVC Mybatis框架整合实战
java·spring·mybatis·spring mvc
八个程序员29 分钟前
c++——探讨a÷b(long long)
开发语言·c++
zhangfeng113330 分钟前
KAT-Coder-Pro V1免费活动继续,免费原来定于北京时间 2025年11月11日 ,快手也加入了模型集成商的队伍了,支持国内各种开原模型
数据库
四谎真好看33 分钟前
Java 黑马程序员学习笔记(进阶篇30)
java·笔记·学习·学习笔记
欢乐的小猪33 分钟前
win10如何更改mysql的密码
数据库·mysql