PostgreSQL IoT,车联网 - 实时轨迹、行程实践 2 - (含index only scan类聚簇表效果)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 标签PostgreSQL , 实时轨迹 , IoT , 车联网 , GIS , 离散IO , 顺序IO , IO放大 , 聚合 , PGLZ压缩解压缩 , IN OUT函数消耗背景车联网场景,一个行程由若干个点组成,业务需要按行程查询,按。

标签

PostgreSQL , 实时轨迹 , IoT , 车联网 , GIS , 离散IO , 顺序IO , IO放大 , 聚合 , PGLZ压缩解压缩 , IN OUT函数消耗


背景

车联网场景,一个行程由若干个点组成,业务需要按行程查询,按。

但是问题来了:

一个行程包含多个点,那么一次查询意味着需要查询并返回多个点(多条记录)。

由于有多个设备(例如汽车),大概率可能同时活跃,同时上传点,因此以堆表存储的话,一个行程中的多条记录,实际上在数据库的表里面,存储在多个BLOCK里面。

如上分析,性能问题:IO放大。

如何优化?

1、行程记录,按行聚集存储。

类似cluster操作。

2、行程记录,合并到单条,聚集存储。

类似将一个行程多条记录聚合。

例子

下面分别测试几种优化方法带来的性能优化效果。

1、cluster

2、array 聚合带压缩

3、array 聚合不带压缩

4、jsonb 聚合带压缩

5、jsonb 聚合不带压缩

6、text 聚合带压缩

7、text 聚合不带压缩

1 原始状态

create unlogged table t_sensor(id serial8 primary key, sid int8, att text, crt_time timestamp);  
create index idx_t_sensor_1 on t_sensor (sid, crt_time);  
vi test.sql  
  
\set sid random(1,10000)  
insert into t_sensor(sid,att,crt_time) values (:sid, md5(random()::text), now());  
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 50 -j 50 -t 2000000  

查看一个行程,需要访问11227个数据块。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from t_sensor where sid=1 order by crt_time;  
                                                                 QUERY PLAN                                                                   
--------------------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t_sensor_1 on public.t_sensor  (cost=0.49..253.36 rows=11121 width=57) (actual time=0.020..47.591 rows=11189 loops=1)  
   Output: id, sid, att, crt_time  
   Index Cond: (t_sensor.sid = 1)  
   Buffers: shared hit=3406 read=7821  
 Planning Time: 0.092 ms  
 Execution Time: 48.303 ms  
(6 rows)  
  
postgres=# select 119*11227*8/1024.0;  
      ?column?        
--------------------  
 10437.601562500000  
(1 row)  

压测性能

vi test.sql  
  
\set sid random(1,10000)  
select * from t_sensor where sid=:sid order by crt_time ;  
  
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 56 -j 56 -T 120  
transaction type: ./test.sql  
scaling factor: 1  
query mode: prepared  
number of clients: 56  
number of threads: 56  
duration: 120 s  
number of transactions actually processed: 14307  
latency average = 470.175 ms  
latency stddev = 43.500 ms  
tps = 119.037842 (including connections establishing)  
tps = 119.098221 (excluding connections establishing)  
statement latencies in milliseconds:  
         0.003  \set sid random(1,10000)  
       470.178  select * from t_sensor where sid=:sid order by crt_time ;  

2 cluster

按行程,时间索引聚集。一次查询返回一条聚合后的记录。

postgres=# cluster t_sensor USING idx_t_sensor_1 ;  

查询一个行程,扫描174个数据块。IO 骤降。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from t_sensor where sid=1 order by crt_time;  
                                                                QUERY PLAN                                                                   
-------------------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t_sensor_1 on public.t_sensor  (cost=0.49..253.39 rows=11149 width=57) (actual time=0.018..2.212 rows=11189 loops=1)  
   Output: id, sid, att, crt_time  
   Index Cond: (t_sensor.sid = 1)  
   Buffers: shared hit=174  
 Planning Time: 0.094 ms  
 Execution Time: 2.816 ms  
(6 rows)  

压测性能,见末尾。

3 array 带压缩

create unlogged table t_sensor_agg(sid int8 primary key, agg t_sensor[]);  
insert into t_sensor_agg select sid,array_agg(t_sensor order by crt_time) from t_sensor group by sid;  

瓶颈:array类型的INPUT OUTPUT接口,以及压缩解压缩接口

Samples: 3M of event 'cpu-clock', Event count (approx.): 559749813667                   
Overhead  Shared Object                           Symbol                                
  24.59%  postgres                                [.] array_out                         
  20.70%  postgres                                [.] record_out                        
   6.74%  postgres                                [.] pglz_decompress                   
   3.78%  libc-2.17.so                            [.] __memcpy_ssse3_back               
   2.72%  postgres                                [.] pg_ltostr_zeropad                 
   2.34%  [kernel]                                [k] run_timer_softirq                 
   2.23%  postgres                                [.] pg_lltoa                          
   2.13%  postgres                                [.] 0x000000000035c350                
   1.76%  postgres                                [.] heap_deform_tuple                 
   1.49%  libc-2.17.so                            [.] __strlen_sse2_pminub              
   1.36%  postgres                                [.] appendStringInfoChar              
   1.36%  [kernel]                                [k] copy_user_enhanced_fast_string    
   1.29%  postgres                                [.] 0x000000000035c36c                
   1.28%  postgres                                [.] 0x000000000035c362                
   1.17%  postgres                                [.] FunctionCall1Coll                 
   0.92%  postgres                                [.] hash_search_with_hash_value       
   0.86%  [kernel]                                [k] _raw_spin_unlock_irqrestore       
   0.84%  postgres                                [.] j2date                            
   0.82%  postgres                                [.] 0x000000000035c357                
   0.76%  postgres                                [.] palloc                            
   0.76%  postgres                                [.] lookup_type_cache                 
   0.67%  postgres                                [.] 0x000000000035c360                
   0.66%  postgres                                [.] timestamp2tm                      
   0.64%  [kernel]                                [k] rcu_process_callbacks             
   0.64%  [kernel]                                [k] __do_softirq                     
vi test.sql  
  
\set sid random(1,10000)  
select * from t_sensor_agg4 where sid=:sid ;  

4 array 不带压缩

create unlogged table t_sensor_agg1(sid int8 primary key, agg t_sensor[]);  
alter table t_sensor_agg1 alter column agg set storage external;  
insert into t_sensor_agg1 select sid,array_agg(t_sensor order by crt_time) from t_sensor group by sid;  

瓶颈:array类型的INPUT OUTPUT接口

Samples: 1M of event 'cpu-clock', Event count (approx.): 310127790569                  
Overhead  Shared Object                           Symbol                               
  26.06%  postgres                                [.] array_out                        
  21.44%  postgres                                [.] record_out                       
   4.20%  libc-2.17.so                            [.] __memcpy_ssse3_back              
   2.86%  [kernel]                                [k] run_timer_softirq                
   2.75%  postgres                                [.] pg_ltostr_zeropad                
   2.65%  postgres                                [.] heap_deform_tuple                
   2.28%  postgres                                [.] pg_lltoa                         
   2.14%  postgres                                [.] 0x000000000035c350               
   1.87%  [kernel]                                [k] copy_user_enhanced_fast_string   
   1.52%  libc-2.17.so                            [.] __strlen_sse2_pminub             
   1.47%  postgres                                [.] appendStringInfoChar             
   1.32%  postgres                                [.] 0x000000000035c36c               
   1.30%  postgres                                [.] 0x000000000035c362               
   1.20%  postgres                                [.] FunctionCall1Coll                
   1.11%  postgres                                [.] hash_search_with_hash_value      
   0.87%  postgres                                [.] j2date                           
   0.85%  postgres                                [.] 0x000000000035c357               
   0.81%  [kernel]                                [k] _raw_spin_unlock_irqrestore      
   0.76%  postgres                                [.] lookup_type_cache                
   0.75%  postgres                                [.] 0x000000000046d33b               
   0.74%  postgres                                [.] palloc                           
   0.72%  [kernel]                                [k] rcu_process_callbacks            
   0.68%  postgres                                [.] timestamp2tm                     
   0.68%  postgres                                [.] pfree                         

5 jsonb 带压缩

create unlogged table t_sensor_agg2(sid int8 primary key, agg jsonb);  
insert into t_sensor_agg2 select sid,jsonb_agg(t_sensor order by crt_time) from t_sensor group by sid;  

6 jsonb 不带压缩

create unlogged table t_sensor_agg3(sid int8 primary key, agg jsonb);  
alter table t_sensor_agg3 alter column agg set storage external;  
insert into t_sensor_agg3 select sid,jsonb_agg(t_sensor order by crt_time) from t_sensor group by sid;  

7 text 带压缩

create unlogged table t_sensor_agg4(sid int8 primary key, agg text);  
insert into t_sensor_agg4 select sid,string_agg(t_sensor::text, '|' order by crt_time) from t_sensor group by sid;  

8 text 不带压缩

create unlogged table t_sensor_agg5(sid int8 primary key, agg text);  
alter table t_sensor_agg5 alter column agg set storage external;  
insert into t_sensor_agg5 select sid,string_agg(t_sensor::text, '|' order by crt_time) from t_sensor group by sid;  

9 index only scan 类似聚集表效果

所有内容作为INDEX的KEY,类似聚集表的效果(相邻内容在同一个INDEX PAGE里面)。查询时走INDEX ONLY SCAN扫描方法,扫描的BLOCK最少。

注意:btree 索引内容不能超过1/3 PAGE (因为BTREE是双向链表,一个PAGE至少要有一条有效记录,所以有这个限制。)。

写入数据

create table t_sensor (id serial8 primary key, sid int8, att text, crt_time timestamp);  
  
create index idx_t_sensor_1 on t_sensor (sid, crt_time, att, id);  
  
  
vi test.sql  
\set sid random(1,10000)    
insert into t_sensor(sid,att,crt_time) values (:sid, md5(random()::text), now());    
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 50 -j 50 -t 2000000  
  
  
transaction type: ./test.sql  
scaling factor: 1  
query mode: prepared  
number of clients: 50  
number of threads: 50  
number of transactions per client: 2000000  
number of transactions actually processed: 100000000/100000000  
latency average = 0.193 ms  
latency stddev = 0.461 ms  
tps = 257995.418591 (including connections establishing)  
tps = 258024.212148 (excluding connections establishing)  
statement latencies in milliseconds:  
         0.001  \set sid random(1,10000)    
         0.192  insert into t_sensor(sid,att,crt_time) values (:sid, md5(random()::text), now());  

生成VM文件(autovacuum触发时会自动生成,但是为了立马看效果,手工执行一下。)

vacuum analyze t_sensor;  

INDEX ONLY SCAN, IO减少效果如下:

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from t_sensor where sid=2 order by crt_time;  
                                                                 QUERY PLAN                                                                    
---------------------------------------------------------------------------------------------------------------------------------------------  
 Index Only Scan using idx_t_sensor_1 on public.t_sensor  (cost=0.60..70.41 rows=9960 width=57) (actual time=0.019..2.109 rows=9978 loops=1)  
   Output: id, sid, att, crt_time  
   Index Cond: (t_sensor.sid = 2)  
   Heap Fetches: 0  
   Buffers: shared hit=235  
 Planning Time: 0.090 ms  
 Execution Time: 2.652 ms  
(7 rows)  

查询性能:

vi test.sql  
  
\set sid random(1,10000)  
select * from t_sensor where sid=:sid order by crt_time;  
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 28 -j 28 -T 120  
  
  
transaction type: ./test.sql  
scaling factor: 1  
query mode: prepared  
number of clients: 28  
number of threads: 28  
duration: 120 s  
number of transactions actually processed: 283638  
latency average = 11.844 ms  
latency stddev = 1.931 ms  
tps = 2363.410561 (including connections establishing)  
tps = 2363.913145 (excluding connections establishing)  
statement latencies in milliseconds:  
         0.002  \set sid random(1,10000)    
        11.842  select * from t_sensor where sid=:sid order by crt_time;  

小结

目标数据分散在多个BLOCK中,引起IO放大的问题,通过聚集存储,或者通过聚合存储,可以很好的解决这个问题。

聚合后,另一个瓶颈则是聚合后的类型(array,jsonb,text)的IN OUT接口。

/ 表存储 行程查询 qps
原始(IO 放大) 8880 MB 119
顺序(无IO 放大) 8880 MB 2057
index only scan(类似聚集表)(无IO 放大) 8880 MB 2363
聚合array(压缩) 4523 MB 2362
聚合array(不压缩) 8714 MB 2515
聚合json(压缩) 5052 MB 3102
聚合json(不压缩) 13 GB 3184
聚合text(压缩) 4969 MB 6057
聚合text(不压缩) 7692 MB 5997

从上面的测试,可以看到IN OUT函数接口的开销,text<jsonb<array(composite array)。

实际的优化例子,可参考末尾的几篇文章。例如:

1、按时间分区,旧的分区使用cluster,按行程整理数据,使用AB表切换,解决IO放大的问题。

2、异步聚合,将点数据准实时按行程ID,聚合到聚合后的行程表。

3、使用INDEX ONLY SCAN, 达到聚集表效果。对业务无任何侵入性。(例如按天分区,加全量(业务需要查询的字段)索引。),相当于两倍存储空间(一份在堆表,一份在索引中)。

参考

《PostgreSQL IoT,车联网 - 实时轨迹、行程实践》

《PostgreSQL AB表切换最佳实践 - 提高切换成功率,杜绝雪崩 - 珍藏级》

《PostgreSQL 时序最佳实践 - 证券交易系统数据库设计 - 阿里云RDS PostgreSQL最佳实践》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
2月前
|
存储 JSON 运维
智能物联网平台:Azure IoT Hub在设备管理中的实践
【10月更文挑战第26天】随着物联网技术的发展,Azure IoT Hub成为企业管理和连接数百万台设备的强大平台。本文介绍Azure IoT Hub的设备管理功能,包括设备注册、设备孪生、直接方法和监控诊断,并通过示例代码展示其应用。
77 4
|
5月前
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
当今社会,物联网技术的发展带来了许多繁琐的挑战,尤其是在数据库管理系统领域,比如实时整合海量数据、处理流中的事件以及处理数据的安全性。例如,应用于智能城市的基于物联网的交通传感器可以实时生成大量的交通数据。据估计,未来5年,物联网设备的数量将达数万亿。物联网产生大量的数据,包括流数据、时间序列数据、RFID数据、传感数据等。要有效地管理这些数据,就需要使用数据库。数据库在充分处理物联网数据方面扮演着非常重要的角色。因此,适当的数据库与适当的平台同等重要。由于物联网在世界上不同的环境中运行,选择合适的数据库变得非常重要。 原创文字,IoTDB 社区可进行使用与传播 一、什么是IoTDB 我
202 9
Apache IoTDB进行IoT相关开发实践
|
6月前
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
IoTDB是专为物联网(IoT)设计的开源时间序列数据库,提供数据收集、存储、管理和分析。它支持高效的数据写入、查询,适用于处理大规模物联网数据,包括流数据、时间序列等。IoTDB采用轻量级架构,可与Hadoop和Spark集成,支持多种存储策略,确保数据安全和高可用性。此外,它还具有InfluxDB协议适配器,允许无缝迁移和兼容InfluxDB的API和查询语法,简化物联网项目的数据管理。随着物联网设备数量的快速增长,选择适合的数据库如IoTDB对于数据管理和分析至关重要。
266 12
|
6月前
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
The article introduces IoTDB, an open-source time-series database designed for efficient management of IoT-generated data. It addresses challenges like real-time integration of massive datasets and security. IoTDB supports high-performance storage,
154 0
Apache IoTDB进行IoT相关开发实践
|
6月前
|
分布式计算 NoSQL 物联网
麻省理工IOT教授撰写的1058页Python程序设计人工智能实践手册!
Python是世界上最流行的语言之一,也是编程语言中使用人数增长最快的一种。 开发者经常会很快地发现自己喜欢Python。他们会欣赏Python的表达力、可读性、简洁性和交互性,也会喜欢开源软件开发环境,这个开源环境正在为广泛的应用领域提供快速增长的可重用软件基础。 几十年来,一些趋势已经强有力地显现出来。计算机硬件已经迅速变得更快、更便宜、更小;互联网带宽已经迅速变得越来越大,同时也越来越便宜;优质的计算机软件已经变得越来越丰富,并且通过“开源”方式免费或几乎免费;很快,“物联网”将连接数以百亿计的各种可想象的设备。这将导致以快速增长的速度和数量生成大量数据。 在今天的计算技术中,最新的创新
|
6月前
|
分布式计算 NoSQL 物联网
麻省理工IOT教授撰写的1058页Python程序设计人工智能实践手册!
Python是世界上最流行的语言之一,也是编程语言中使用人数增长最快的一种。 开发者经常会很快地发现自己喜欢Python。他们会欣赏Python的表达力、可读性、简洁性和交互性,也会喜欢开源软件开发环境,这个开源环境正在为广泛的应用领域提供快速增长的可重用软件基础。
|
7月前
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
IoTDB是面向物联网的时序数据库,专注于时间序列数据管理,提供高效的数据处理、集成Hadoop和Spark生态、支持多目录存储策略。它还具有InfluxDB协议适配器,允许无缝迁移原本使用InfluxDB的业务。文章讨论了IoTDB的体系结构,包括数据文件、系统文件和预写日志文件的存储策略,并介绍了如何配置数据存储目录。此外,还提及了InfluxDB版本和查询语法的支持情况。IoTDB在物联网数据管理和分析中扮演关键角色,尤其适合处理大规模实时数据。
115 5
|
7月前
|
存储 分布式计算 物联网
Apache IoTDB进行IoT相关开发实践
物联网技术带来数据库管理挑战,特别是实时数据整合与安全性。IoTDB是一个专为时间序列数据设计的数据库,提供数据收集、存储和分析服务,适用于海量物联网数据。其架构包括数据文件、系统文件和预写日志文件的管理,并支持多目录存储策略。此外,IoTDB还开发了InfluxDB协议适配器,使得用户能无缝迁移原有InfluxDB业务。此适配器基于IoTDB的Java服务接口,转换InfluxDB的元数据格式,实现与IoTDB的数据交互。目前,适配器支持InfluxDB 1.x版本及部分查询语法。
206 5
|
7月前
|
自然语言处理 关系型数据库 数据库
技术经验解读:【转】PostgreSQL的FTI(TSearch)与中文全文索引的实践
技术经验解读:【转】PostgreSQL的FTI(TSearch)与中文全文索引的实践
78 0
|
8月前
|
SQL 关系型数据库 MySQL
923.【mysql】 only full group by 模式
923.【mysql】 only full group by 模式
250 1

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版