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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 标签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数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
存储 SQL Cloud Native
深入了解云原生数据库CockroachDB的概念与实践
作为一种全球领先的分布式SQL数据库,CockroachDB以其高可用性、强一致性和灵活性等特点备受关注。本文将深入探讨CockroachDB的概念、设计思想以及实践应用,并结合实例演示其在云原生环境下的优越表现。
|
3月前
|
Cloud Native 关系型数据库 大数据
CockroachDB:云原生数据库的新概念与实践
本文将介绍CockroachDB,一种先进的云原生数据库,它具备分布式、强一致性和高可用性等特点。我们将探讨CockroachDB的基本原理、架构设计以及在实际应用中的种种优势和挑战。
|
7月前
|
存储 关系型数据库 物联网
沉浸式学习PostgreSQL|PolarDB 14: 共享单车、徒步、旅游、网约车轨迹查询
本文的目的是帮助你了解如何设计轨迹表, 如何高性能的写入、查询、分析轨迹数据.
608 0
|
1月前
|
SQL 关系型数据库 MySQL
923.【mysql】 only full group by 模式
923.【mysql】 only full group by 模式
20 1
|
6月前
|
关系型数据库 定位技术 分布式数据库
沉浸式学习PostgreSQL|PolarDB 18: 通过GIS轨迹相似伴随|时态分析|轨迹驻点识别等技术对拐卖、诱骗场景进行侦查
本文主要教大家怎么用好数据库, 而不是怎么运维管理数据库、怎么开发数据库内核.
1069 1
|
4月前
|
存储 关系型数据库 MySQL
存储成本最高降至原来的5%,PolarDB分布式冷数据归档的业务实践
国内某家兼具投资理财、文化旅游、票务为一体的大型综合型集团公司,2015年成立至今,由于业务高速发展,业务数据增长非常快,数据库系统屡次不堪重负。该公司数据库运维总监介绍,他们目前业务压力比较大的是票务和订单系统,他们的平台每天新增几千万的订单数据,订单的数据来自于各个终端,近几年每个月以300G的数据规模在高速增长,由于数据不断增加,数据库系统迄今为止迭代过了3次。
|
5月前
|
关系型数据库 MySQL 数据库
Mysql中key与index区别
Mysql中key与index区别
|
6月前
|
存储 SQL 关系型数据库
MySQL 优化 index merge(索引合并)引起的死锁分析(强烈推荐)
生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),如下:
|
6月前
|
SQL 缓存 关系型数据库
PolarDB-X 混沌测试实践:如何衡量数据库索引选择能力
随着PolarDB分布式版的不断演进,功能不断完善,新的特性不断增多,整体架构扩大的同时带来了测试链路长,出现问题前难发现,出现问题后难排查等等问题。原有的测试框架已经难以支撑实际场景的复杂模拟测试。因此,我们实现了一个基于业务场景面向优化器索引选择的混沌查询实验室,本文之后简称为CEST(complex environment simulation test)。
|
7月前
|
存储 SQL 关系型数据库
AnalyticDB PostgreSQL构建一站式实时数仓实践
本文介绍通过 AnalyticDB PostgreSQL 版基于实时物化视图,构建流批一体的一站式实时数仓解决方案,实现一套系统、一份数据、一次写入,即可在数仓内完成实时数据源头导入到实时分析全流程。
1885 5
AnalyticDB PostgreSQL构建一站式实时数仓实践

相关产品

  • 云原生数据库 PolarDB