深度解析:轨迹数据抽稀到底该放数据库还是 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 限流 ✔ 避免爆表 ➖ 一般 可选
相关推荐
Larry_Yanan8 分钟前
Qt多进程(三)QLocalSocket
开发语言·c++·qt·ui
大布布将军11 分钟前
⚡️ 深入数据之海:SQL 基础与 ORM 的应用
前端·数据库·经验分享·sql·程序人生·面试·改行学it
醒过来摸鱼13 分钟前
Java classloader
java·开发语言·python
superman超哥15 分钟前
仓颉语言中元组的使用:深度剖析与工程实践
c语言·开发语言·c++·python·仓颉
专注于大数据技术栈16 分钟前
java学习--StringBuilder
java·学习
loosenivy19 分钟前
企业银行账户归属地查询接口如何用Java调用
java·企业银行账户归属地·企业账户查询接口·企业银行账户查询
小鸡吃米…20 分钟前
Python - 继承
开发语言·python
IT 行者41 分钟前
Spring Security 6.x 迁移到 7.0 的完整步骤
java·spring·oauth2
JIngJaneIL44 分钟前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
祁思妙想1 小时前
Python中的FastAPI框架的设计特点和性能优势
开发语言·python·fastapi