车联网案例,轨迹清洗 - 阿里云RDS PostgreSQL最佳实践 - 窗口查询

标签

PostgreSQL , 窗口函数 , 车联网 , 轨迹 , 轨迹清洗 , lag , lead


背景

车联网中一个非常典型的场景是采集车辆的行驶轨迹,通常来说车辆的轨迹并不会实时上报,可能会堆积若干条轨迹记录,或者间隔多少时间上报一次。

一个典型的数据结构如下

(car_id, pos geometry, crt_time timestamp)

车辆在行驶,行驶过程中会遇到堵车,红绿灯,那么上报的轨迹记录可能是这样的

1, 位置1, '2017-01-01 12:00:00'
1, 位置1, '2017-01-01 12:00:05'
1, 位置1, '2017-01-01 12:00:10'
1, 位置1, '2017-01-01 12:00:15'
1, 位置1, '2017-01-01 12:00:20'
1, 位置2, '2017-01-01 12:00:30'

也就是说,在同一个位置,因为堵车、等红灯,可能会导致上传多条记录。

那么就涉及到在数据库中清洗不必要的等待记录的需求,在一个点,我们最多保留2条记录,表示到达这个位置和离开这个位置。

这个操作可以使用窗口函数实现。

当然从最佳效率角度来分析,轨迹清洗这个事情,在终端做是更合理的,一个位置的起始点,只留两条。

例子

1、设计表结构

create table car_trace (cid int, pos point, crt_time timestamp);

2、生成1000万测试数据,假设有1000量车,(为了让数据更容易出现重复,为了测试看效果,位置使用25个点)

insert into car_trace select random()*999, point((random()*5)::int, (random()*5)::int), clock_timestamp() from generate_series(1,10000000);

3、创建索引

create index idx_car on car_trace (cid, crt_time);

4、查询数据layout

select * from car_trace where cid=1 order by crt_time limit 1000;  

   1 | (3,1) | 2017-07-22 21:30:09.84984
   1 | (1,4) | 2017-07-22 21:30:09.850297
   1 | (1,4) | 2017-07-22 21:30:09.852586
   1 | (1,4) | 2017-07-22 21:30:09.854155
   1 | (1,4) | 2017-07-22 21:30:09.854425
   1 | (3,1) | 2017-07-22 21:30:09.854493  

观察到了几个重复。

5、使用窗口过滤单一位置记录,最多仅保留到达这个位置和离开这个位置的两条记录。

这里用到两个窗口函数:

lag,表示当前记录的前面一条记录。

lead,表示当前记录的下一条记录。

判断到达点、离去点的方法如下:

  • 当前pos 不等于 前一条pos,说明这条记录是当前位置的到达点。
  • 当前pos 不等于 下一条pos,说明这条记录是当前位置的离去点。
  • 前一条pos 为空,说明这条记录是第一条记录。
  • 下一条pos 为空,说明这条记录是最后一条记录。
select * from
(
select
  *,
  lag(pos) over (partition by cid order by crt_time) as lag,
  lead(pos) over (partition by cid order by crt_time) as lead
from car_trace
  where cid=1
  and crt_time between '2017-07-22 21:30:09.83994' and '2017-07-22 21:30:09.859735'
) t
  where pos <> lag
  or pos <> lead
  or lag is null
  or lead is null;  

 cid |  pos  |          crt_time          |  lag  | lead
-----+-------+----------------------------+-------+-------
   1 | (2,1) | 2017-07-22 21:30:09.83994  |       | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.839953 | (2,1) | (5,2)
   1 | (5,2) | 2017-07-22 21:30:09.840704 | (3,1) | (4,4)
   1 | (4,4) | 2017-07-22 21:30:09.84179  | (5,2) | (5,2)
   1 | (5,2) | 2017-07-22 21:30:09.843787 | (4,4) | (1,5)
   1 | (1,5) | 2017-07-22 21:30:09.844165 | (5,2) | (0,5)
   1 | (0,5) | 2017-07-22 21:30:09.84536  | (1,5) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.845896 | (0,5) | (3,3)
   1 | (3,3) | 2017-07-22 21:30:09.846958 | (4,1) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.84984  | (3,3) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.850297 | (3,1) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.854425 | (1,4) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.854493 | (1,4) | (3,2)
   1 | (3,2) | 2017-07-22 21:30:09.854541 | (3,1) | (2,0)
   1 | (2,0) | 2017-07-22 21:30:09.855297 | (3,2) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.857592 | (2,0) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.857595 | (4,1) | (0,4)
   1 | (0,4) | 2017-07-22 21:30:09.857597 | (4,1) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.858996 | (0,4) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.859735 | (3,1) |
(20 rows)

未加清洗轨迹,得到的结果如下:

select
  *,
  lag(pos) over (partition by cid order by crt_time) as lag,
  lead(pos) over (partition by cid order by crt_time) as lead
from car_trace
  where cid=1
  and crt_time between '2017-07-22 21:30:09.83994' and '2017-07-22 21:30:09.859735';  

 cid |  pos  |          crt_time          |  lag  | lead
-----+-------+----------------------------+-------+-------
   1 | (2,1) | 2017-07-22 21:30:09.83994  |       | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.839953 | (2,1) | (5,2)
   1 | (5,2) | 2017-07-22 21:30:09.840704 | (3,1) | (4,4)
   1 | (4,4) | 2017-07-22 21:30:09.84179  | (5,2) | (5,2)
   1 | (5,2) | 2017-07-22 21:30:09.843787 | (4,4) | (1,5)
   1 | (1,5) | 2017-07-22 21:30:09.844165 | (5,2) | (0,5)
   1 | (0,5) | 2017-07-22 21:30:09.84536  | (1,5) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.845896 | (0,5) | (3,3)
   1 | (3,3) | 2017-07-22 21:30:09.846958 | (4,1) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.84984  | (3,3) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.850297 | (3,1) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.852586 | (1,4) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.854155 | (1,4) | (1,4)
   1 | (1,4) | 2017-07-22 21:30:09.854425 | (1,4) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.854493 | (1,4) | (3,2)
   1 | (3,2) | 2017-07-22 21:30:09.854541 | (3,1) | (2,0)
   1 | (2,0) | 2017-07-22 21:30:09.855297 | (3,2) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.857592 | (2,0) | (4,1)
   1 | (4,1) | 2017-07-22 21:30:09.857595 | (4,1) | (0,4)
   1 | (0,4) | 2017-07-22 21:30:09.857597 | (4,1) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.858996 | (0,4) | (3,1)
   1 | (3,1) | 2017-07-22 21:30:09.859735 | (3,1) |
(22 rows)

使用lag, lead清洗掉了停留过程中的记录。

被跟踪对象散落导致的扫描IO放大的优化

因为业务中涉及的车辆ID可能较多,不同车辆汇聚的数据会往数据库中写入,如果不做任何优化,那么不同车辆的数据进入数据库后,可能是交错存放的,也就是说一个数据块中,可能有不同车辆的数据。

那么在查询单一车辆的轨迹时,会扫描很多数据块(扫描IO放大)。

优化思路有两种。

1、业务端汇聚分组排序后写入数据库。例如程序在接收到车辆终端提交的数据后,按车辆ID分组,按时间排序,写入数据库(insert into tbl values (),(),...();)。这样的话,同样车辆的数据,可能会尽可能的落在同一个数据块内。

2、数据库端使用分区,重组数据。例如,按车辆ID,每辆车、或者车辆HASH分区存放。

以上两种方法,都是要将数据按查询需求重组,从而达到降低扫描IO的目的。

这个方法与《PostgreSQL 证券行业数据库需求分析与应用》的方法类似,有兴趣的朋友可以参考。

时间: 2024-09-23 18:21:27

车联网案例,轨迹清洗 - 阿里云RDS PostgreSQL最佳实践 - 窗口查询的相关文章

医疗大健康行业案例(老人健康实时监测和预警) - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , pipelineDB , 流式计算 , 独立事件相关性 , 舆情分析 , 实时状态分析 , 递归查询 , 时序数据 背景 人的身体和机器差不多,随着年龄的增长,器官逐渐老化,毛病也会越来越多,注意保养是一方面,另一方面也需要注意实时的监测和发出预警,在问题萌芽状态就解决掉. 以往我们检查身体得去医院或专业的体检机构,很麻烦,随着科技的进步,一些健康指标的监测变得更加方便,例如手环也是一个普及很快的监控检测终端(目前已能够检测心跳.温度.运动等各项指标),未来这种终

菜鸟末端轨迹(解密支撑每天251亿个包裹的数据库) - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , PostGIS , 多边形 , 面 , 点 , 面点判断 , 菜鸟 背景 菜鸟末端轨迹项目中涉及的一个关键需求,面面判断. 在数据库中存储了一些多边形记录,约几百万到千万条记录,例如一个小区,在地图上是一个多边形. 不同的快递公司,会有各自不同的多边形划分方法(每个网点负责的片区(多边形),每个快递员负责的片区(多边形)). 用户在寄件时,根据用户的位置,查找对应快递公司负责这个片区的网点.或者负责该片区的快递员. 一.需求 1.在数据库中存储了一些静态的面信息,

(新零售)商户网格化运营 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , PostGIS , 地理位置 , KNN , 近邻检索 , 网格检索 , polygon中心点 , 半径搜索 背景 伟大的马老师说: "纯电商时代很快会结束,未来的十年.二十年,没有电子商务这一说,只有新零售这一说,也就是说线上线下和物流必须结合在一起,才能诞生真正的新零售" 线上是指云平台,线下是指销售门店或生产商,新物流消灭库存,减少囤货量. 电子商务平台消失是指,现有的电商平台分散,每个人都有自己的电商平台,不再入驻天猫.京东.亚马逊大型电子商务平

时间、空间、对象 海量极速多维检索 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , 时间 , 空间 , 对象属性 , 多维度检索 , 海量 , 空间索引 , 数据分区 , 块级索引BRIN , 多级索引 , GIN倒排索引 , JSON索引 , 多列索引 , 多索引扫描合并 , bitmapAnd , bitmapOr , 物理扫描 , ctid扫描 , intersect , partial index , partition index 背景 人类或者其他对象的活动产生了海量的时间.空间数据,如果有科技能实现回到过去,过去的世界状态会是什么样

数据寻龙点穴(空间聚集分析) - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , Greenplum , PostGIS , K-Mean , 热力图 背景 最近鬼吹灯热播,胡八一的<十六字阴阳风水秘术>到底是什么武功秘籍?寻龙点穴又是什么?别问我,不知道. PS:截取自互联网.- 寻龙点穴是风水学术语.古人说:三年寻龙,十年点穴.意思就是说,学会寻龙脉要很长的时间,但要懂得点穴,并且点得准则难上加难,甚至须要用"十年"时间. 但是,若没正确方法,就是用百年时间,也不能够点中风水穴心聚气的真点,这样一来,寻龙的功夫也白费了

空间索引(GiST、BRIN、R-Tree)选择、优化 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , Greenplum , PostGIS , GiST , R-Tree , BRIN , 相关性 , 网格 , BOX , K-Mean 背景 空间数据的搜索需求通常包括: 1.平面.三维.多维对象 几何相交.不相交.相邻. 2.平面.三维.多维对象的方位判断(相交或严格在左边.右边.上边.下边),类似数值的大于.小于.大于等于.小于等于. 3.平面.三维.多维对象 包含 另一个对象 4.平面.三维.多维对象 等于 另一个对象 5.平面.三维.多维对象 与另一个对

分区索引的应用和实践 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , partial index , partition index 背景 当表很大时,大家可能会想到分区表的概念,例如用户表,按用户ID哈希或者范围分区,拆成很多表. 又比如行为数据表,可以按时间分区,拆成很多表. 拆表的好处: 1.可以将表放到不同的表空间,表空间和块设备挂钩,例如历史数据访问量低,数据量大,可以放到机械盘所在的表空间.而活跃数据则可以放到SSD对应的表空间. 2.拆表后,方便维护,例如删除历史数据,直接DROP TABLE就可以了,不会产生REDO

机票业务(单实例 2700万行/s return)数据库架构设计 - 阿里云RDS PostgreSQL最佳实践

背景 机票业务的某个模块,数据量10亿+,写.更新.删除量较低.根据KEY查询一些数据,每次查询返回1万条左右的记录. 就是这样简单的需求,业务方发现读成为了巨大的瓶颈,每次返回1万条,100个并发请求,每秒就是100万条(500MB左右),主要的瓶颈: 1.网络是个较大的开销. 2.不同KEY的数据可能是分散存放的,存在查询时的IO放大,可能有一定的性能影响. 3.每次请求的返回记录数较多,数据库search buffer调用可能开销会上升. 就这几个问题,我们来看看如何优化或解决业务方的问题

(时序业务)证券交易系统数据库设计 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , 证券 , 时序数据 , JSON , HSTORE , 数组 , range索引 , BRIN块级索引 , 分时走势 , 线性回归 , MADlib , 机器学习 背景 证券行业产生的数据比较多,读写非常频繁. 以股票交易为例,一共有几千只股票.一年大概有240个交易日,交易日大概是从早上10点到下午4点. 1.数据写入需求: 实时的数据写入,按查询维度的实时数据合并(比如秒数据实时写入.分钟,几分钟,...则实时合并). 数据分为不同粒度的分时数据.(精确到秒,