PostgreSQL 多维空间几何对象 相交、包含 高效率检索实践 - cube

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 标签PostgreSQL , cube , 空间 , 几何 , 相交 , 包含背景多维空间对象的几何运算,高效率检索实践。例如我们在数据库中存储了多维几何对象,可以使用lower, upper的数组来表达,例如3维度对象:CUBE [ xmin1 ymin1 zmin1 , xmax1 ymax1 zmax1 ] 在介绍CUBE类型前,我们可以使用6个字段(xmin,xmax,ymin,ymax,zmin,zmax)来表达一个立方体。

标签

PostgreSQL , cube , 空间 , 几何 , 相交 , 包含


背景

多维空间对象的几何运算,高效率检索实践。

例如我们在数据库中存储了多维几何对象,可以使用lower, upper的数组来表达,例如3维度对象:

CUBE  
[  
xmin1  
ymin1  
zmin1  
,  
xmax1  
ymax1  
zmax1  
]  

在介绍CUBE类型前,我们可以使用6个字段(xmin,xmax,ymin,ymax,zmin,zmax)来表达一个立方体。

包含和相交查询

在介绍CUBE类型前,我们如果使用6个字段来表达立方体,那么相交,包含分别如何标示呢?

包含:

(xmin1 <= xmin2 and xmax1 >= xmax2)  
and  
(ymin1 <= ymin2 and ymax1 >= ymax2)  
and  
(zmin1 <= zmin2 and zmax1 >= zmax2)  

相交:

每个坐标都相交,注意任意坐标相交的方位有

-----  
   -----    
  
或  
  
   -----    
------  
  
或  
  
--------  
  ---   
  
或  
  
---  
   ---  
  
或  
  
---  
---  
  
或  
  
   ---  
---  

每条边都有相交即CUBE相交,表达如下

((xmin1 >= xmin2 and xmin1 <= xmax2) or (xmax1 >= xmin2 and xmax1 <= xmax2) or (xmin1 <= xmin2 and xmax1 >= xmax2))  
and  
((ymin1 >= ymin2 and ymin1 <= ymax2) or (ymax1 >= ymin2 and ymax1 <= ymax2) or (ymin1 <= ymin2 and ymax1 >= ymax2))  
and  
((zmin1 >= zmin2 and zmin1 <= zmax2) or (zmax1 >= zmin2 and zmax1 <= zmax2) or (zmin1 <= zmin2 and zmax1 >= zmax2))  

使用6个字段的空间计算性能

1、创建测试表

create table test1 (  
  id int primary key,   
  x_min int,   
  y_min int,   
  z_min int,  
  x_max int,  
  y_max int,  
  z_max int  
);  

2、写入100万记录

insert into test1 select id, x, y, z, x+1+(random()*100)::int, y+1+(random()*100)::int, z+1+(random()*100)::int   
from (select id, (random()*1000)::int x, (random()*1000)::int y, (random()*1000)::int z from generate_series(1,1000000) t(id)) t ;  

记录如下

postgres=# select * from test1 limit 10;  
 id | x_min | y_min | z_min | x_max | y_max | z_max   
----+-------+-------+-------+-------+-------+-------  
  1 |    37 |   367 |   948 |    93 |   372 |   989  
  2 |   994 |   543 |   596 |  1031 |   613 |   617  
  3 |   399 |   616 |   897 |   444 |   624 |   959  
  4 |   911 |   624 |    67 |  1007 |   705 |    84  
  5 |   286 |   560 |   882 |   334 |   632 |   936  
  6 |   370 |   748 |   897 |   403 |   779 |   992  
  7 |   723 |   292 |   484 |   756 |   358 |   503  
  8 |   514 |    48 |   792 |   556 |    98 |   879  
  9 |    17 |   400 |   485 |    26 |   435 |   514  
 10 |   240 |   631 |   841 |   253 |   642 |   897  
(10 rows)  

3、包含查询

select * from test1 where   
(x_min <= 37 and x_max >= 93)  
and  
(y_min <= 367 and y_max >= 372)  
and  
(z_min <= 948 and z_max >= 989);  
  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test1 where   
(x_min <= 37 and x_max >= 93)  
and  
(y_min <= 367 and y_max >= 372)  
and  
(z_min <= 948 and z_max >= 989);  
                                                                         QUERY PLAN                                                                            
-------------------------------------------------------------------------------------------------------------------------------------------------------------  
 Seq Scan on public.test1  (cost=0.00..13220.05 rows=539 width=28) (actual time=0.024..79.397 rows=15 loops=1)  
   Output: id, x_min, y_min, z_min, x_max, y_max, z_max  
   Filter: ((test1.x_min <= 37) AND (test1.x_max >= 93) AND (test1.y_min <= 367) AND (test1.y_max >= 372) AND (test1.z_min <= 948) AND (test1.z_max >= 989))  
   Rows Removed by Filter: 999985  
   Buffers: shared hit=1835  
 Planning Time: 0.103 ms  
 Execution Time: 79.421 ms  
(7 rows)  
  
Time: 79.947 ms  
  
  
   id   | x_min | y_min | z_min | x_max | y_max | z_max   
--------+-------+-------+-------+-------+-------+-------  
      1 |    37 |   367 |   948 |    93 |   372 |   989  
 104882 |    17 |   327 |   924 |   111 |   389 |  1012  
 178185 |    31 |   315 |   897 |   104 |   380 |   990  
 228661 |     9 |   363 |   934 |   101 |   394 |  1001  
 275030 |    21 |   334 |   912 |   102 |   379 |  1012  
 405290 |    10 |   356 |   911 |   102 |   435 |   996  
 586417 |    35 |   362 |   930 |   128 |   454 |  1016  
 594367 |    23 |   312 |   943 |   112 |   395 |  1017  
 622753 |    11 |   365 |   916 |    93 |   427 |   995  
 645719 |    32 |   309 |   918 |    94 |   377 |  1015  
 757900 |    34 |   339 |   905 |    98 |   430 |   998  
 784203 |    36 |   344 |   945 |    95 |   390 |  1035  
 824046 |    23 |   367 |   946 |   115 |   423 |  1021  
 878257 |    37 |   339 |   948 |   123 |   398 |  1033  
 914020 |    26 |   358 |   918 |   109 |   379 |  1019  
(15 rows)  
  
Time: 80.269 ms  

4、相交查询

select * from test1 where   
((x_min >= 37 and x_min <= 93) or (x_max >= 37 and x_max <= 93) or (x_min <= 37 and x_max >= 93))  
and  
((y_min >= 367 and y_min <= 372) or (y_max >= 367 and y_max <= 372) or (y_min <= 367 and y_max >= 372))  
and  
((z_min >= 948 and z_min <= 989) or (z_max >= 948 and z_max <= 989) or (z_min <= 948 and z_max >= 989))  
;  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test1 where   
((x_min >= 37 and x_min <= 93) or (x_max >= 37 and x_max <= 93) or (x_min <= 37 and x_max >= 93))  
and  
((y_min >= 367 and y_min <= 372) or (y_max >= 367 and y_max <= 372) or (y_min <= 367 and y_max >= 372))  
and  
((z_min >= 948 and z_min <= 989) or (z_max >= 948 and z_max <= 989) or (z_min <= 948 and z_max >= 989))  
;  
                       QUERY PLAN                                                                                                                          
 Seq Scan on public.test1  (cost=0.00..39229.87 rows=4364 width=28) (actual time=0.026..119.539 rows=483 loops=1)  
   Output: id, x_min, y_min, z_min, x_max, y_max, z_max  
   Filter: ((((test1.x_min >= 37) AND (test1.x_min <= 93)) OR ((test1.x_max >= 37) AND (test1.x_max <= 93)) OR ((test1.x_min <= 37) AND (test1.x_max >= 93))) AND (((test1.y_min >= 367) AND (test1.y_min <= 372)) OR ((test1.y_max >= 367) AND (test1.y_max <= 372)) OR ((test1.y_min <= 367) AND (test1.y_max >= 372))) AND (((test1.z_min >= 948) AND (test1.z_min <= 989)) OR ((test1.z_max >= 948) AND (test1.z_max <= 989)) OR ((test1.z_min <= 948) AND (test1.z_max >= 989))))  
   Rows Removed by Filter: 999517  
   Buffers: shared hit=1835  
 Planning Time: 0.135 ms  
 Execution Time: 119.621 ms  
(7 rows)  
  
Time: 120.283 ms  

cube 类型

cube的多维体表达方法如下

It does not matter which order the opposite corners of a cube are entered in.

The cube functions automatically swap values if needed to create a uniform “lower left — upper right” internal representation.

When the corners coincide, cube stores only one corner along with an “is point” flag to avoid wasting space.

1、创建 cube 插件

create extension cube;  

2、创建测试表

create table test2 (  
  id int primary key,  
  cb cube  
);  

3、将数据导入test2 cube表

insert into test2 select id, cube(array[x_min,y_min,z_min], array[x_max,y_max,z_max]) from test1;  

4、给CUBE类型创建gist索引

create index idx_test2_cb on test2 using gist(cb);  

5、包含查询性能

explain (analyze,verbose,timing,costs,buffers) select * from test2 where cb @> cube '[(37,367,948), (93,372,989)]';  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test2 where cb @> cube '[(37,367,948), (93,372,989)]';  
                                                           QUERY PLAN                                                              
---------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_test2_cb on public.test2  (cost=0.25..20.65 rows=1000 width=60) (actual time=0.154..0.247 rows=15 loops=1)  
   Output: id, cb  
   Index Cond: (test2.cb @> '(37, 367, 948),(93, 372, 989)'::cube)  
   Buffers: shared hit=26  
 Planning Time: 0.196 ms  
 Execution Time: 0.269 ms  
(6 rows)  
  
postgres=# \timing  
Timing is on.  
postgres=# select * from test2 where cb @> cube '[(37,367,948), (93,372,989)]';  
   id   |               cb                  
--------+---------------------------------  
      1 | (37, 367, 948),(93, 372, 989)  
 228661 | (9, 363, 934),(101, 394, 1001)  
 586417 | (35, 362, 930),(128, 454, 1016)  
 824046 | (23, 367, 946),(115, 423, 1021)  
 914020 | (26, 358, 918),(109, 379, 1019)  
 104882 | (17, 327, 924),(111, 389, 1012)  
 594367 | (23, 312, 943),(112, 395, 1017)  
 645719 | (32, 309, 918),(94, 377, 1015)  
 784203 | (36, 344, 945),(95, 390, 1035)  
 275030 | (21, 334, 912),(102, 379, 1012)  
 757900 | (34, 339, 905),(98, 430, 998)  
 878257 | (37, 339, 948),(123, 398, 1033)  
 405290 | (10, 356, 911),(102, 435, 996)  
 622753 | (11, 365, 916),(93, 427, 995)  
 178185 | (31, 315, 897),(104, 380, 990)  
(15 rows)  
  
Time: 0.685 ms  

6、相交查询性能

select * from test2 where cb && cube '[(37,367,948), (93,372,989)]';  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test2 where cb && cube '[(37,367,948), (93,372,989)]';  
                                                            QUERY PLAN                                                              
----------------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_test2_cb on public.test2  (cost=0.25..76.66 rows=5000 width=60) (actual time=0.086..0.943 rows=483 loops=1)  
   Output: id, cb  
   Index Cond: (test2.cb && '(37, 367, 948),(93, 372, 989)'::cube)  
   Buffers: shared hit=505  
 Planning Time: 0.085 ms  
 Execution Time: 1.011 ms  
(6 rows)  
  
Time: 1.506 ms  

7、除此以外,CUBE还支持很多的几何计算操作符,也可以做包含点的查询。

https://www.postgresql.org/docs/devel/static/cube.html

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test2 where cb @> cube '(37,367,948)';
                                                            QUERY PLAN                                                            
----------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_test2_cb on public.test2  (cost=0.25..20.65 rows=1000 width=60) (actual time=0.153..0.420 rows=107 loops=1)
   Output: id, cb
   Index Cond: (test2.cb @> '(37, 367, 948)'::cube)
   Buffers: shared hit=121
 Planning Time: 0.077 ms
 Execution Time: 0.448 ms
(6 rows)

Time: 0.893 ms

优化

如果SQL请求返回的记录数非常多,建议流式返回,同时建议根据BLOCK设备的随机IO能力设置正确的random_page_cost参数。

《PostgreSQL 10 参数模板 - 珍藏级》

流式返回例子

postgres=# begin;
BEGIN
postgres=# declare cur1 cursor for select * from test2 where cb && cube '[(37,367,948), (93,372,989)]';
DECLARE CURSOR
postgres=# \timing
Timing is on.
postgres=# fetch 10 from cur1;
   id   |               cb               
--------+--------------------------------
  41724 | (65, 363, 939),(87, 425, 980)
 115087 | (72, 362, 977),(97, 454, 1005)
 235266 | (74, 362, 958),(133, 457, 994)
 489571 | (51, 362, 970),(101, 393, 989)
 655616 | (77, 359, 932),(79, 455, 1026)
 786710 | (73, 358, 942),(160, 374, 960)
      1 | (37, 367, 948),(93, 372, 989)
   6441 | (48, 368, 949),(88, 426, 964)
  59620 | (29, 364, 939),(60, 452, 997)
 153554 | (22, 367, 959),(75, 374, 997)
(10 rows)

Time: 0.297 ms
postgres=# end;
COMMIT
Time: 0.138 ms

如果是SSD盘,建议random_page_cost设置为1.1-1.3

alter system set random_page_cost=1.3;
select pg_reload_conf();

小结

使用cube插件,我们在对多维几何空间对象进行查询时,可以使用GIST索引,性能非常棒。

在100万空间对象的情况下,性能提升了100倍。

PS, test1表(分字段表达)即使使用BTREE索引,效果也不好,因为多字段的范围检索,初级索引是要全扫描的,以前有一个智能DNS的例子类似,使用GIST提升了20多倍性能。

《PostgreSQL 黑科技 range 类型及 gist index 20x+ speedup than Mysql index combine query》

使用CUBE插件,我们还可以用来计算多维对象的向量相似性,按向量相似性排序。参考末尾连接。

参考

《PostgreSQL 相似人群圈选,人群扩选,向量相似 使用实践》

《PostgreSQL 黑科技 range 类型及 gist index 20x+ speedup than Mysql index combine query》

《通过空间思想理解GiST索引的构造》

https://www.postgresql.org/docs/devel/static/cube.html

相关实践学习
使用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月前
|
关系型数据库 分布式数据库 数据库
PolarDB对比X-Engine与InnoDB空间效率
本实验带您体验创建X-Engine和InnoDB两种不同的表存储引擎,通过Sysbench模拟数据注入的过程对比俩种表引擎的空间效率。
545 0
|
6月前
|
存储 NoSQL 关系型数据库
深入探索地理空间查询:如何优雅地在MySQL、PostgreSQL及Redis中实现精准的地理数据存储与检索技巧
深入探索地理空间查询:如何优雅地在MySQL、PostgreSQL及Redis中实现精准的地理数据存储与检索技巧
645 0
|
4月前
|
存储 关系型数据库 MySQL
存储成本最高降至原来的5%,PolarDB分布式冷数据归档的业务实践
国内某家兼具投资理财、文化旅游、票务为一体的大型综合型集团公司,2015年成立至今,由于业务高速发展,业务数据增长非常快,数据库系统屡次不堪重负。该公司数据库运维总监介绍,他们目前业务压力比较大的是票务和订单系统,他们的平台每天新增几千万的订单数据,订单的数据来自于各个终端,近几年每个月以300G的数据规模在高速增长,由于数据不断增加,数据库系统迄今为止迭代过了3次。
|
6月前
|
SQL 缓存 关系型数据库
PolarDB-X 混沌测试实践:如何衡量数据库索引选择能力
随着PolarDB分布式版的不断演进,功能不断完善,新的特性不断增多,整体架构扩大的同时带来了测试链路长,出现问题前难发现,出现问题后难排查等等问题。原有的测试框架已经难以支撑实际场景的复杂模拟测试。因此,我们实现了一个基于业务场景面向优化器索引选择的混沌查询实验室,本文之后简称为CEST(complex environment simulation test)。
|
7月前
|
存储 关系型数据库 数据库
沉浸式学习PostgreSQL|PolarDB 13: 博客、网站按标签内容检索, 并按匹配度排序
本文主要教大家怎么用好数据库, 而不是怎么运维管理数据库、怎么开发数据库内核.
699 0
|
7月前
|
存储 SQL 关系型数据库
AnalyticDB PostgreSQL构建一站式实时数仓实践
本文介绍通过 AnalyticDB PostgreSQL 版基于实时物化视图,构建流批一体的一站式实时数仓解决方案,实现一套系统、一份数据、一次写入,即可在数仓内完成实时数据源头导入到实时分析全流程。
1880 5
AnalyticDB PostgreSQL构建一站式实时数仓实践
|
8月前
|
分布式数据库 调度 数据库
直播预告 | PolarDB-X 备份恢复原理与实践
备份恢复是生产级数据库必不可少的功能,而PolarDB-X 作为一款分布式数据库,备份数据的全局一致也是最基本的要求。本期分享将介绍PolarDB-X 开源版备份恢复功能的背景与原理,以及如何使用 PolarDB-X Operator 实现备份调度。
直播预告 | PolarDB-X 备份恢复原理与实践
|
8月前
|
安全 关系型数据库 数据库
创建 PostgreSQL 表空间时没有指定空间的总大小
创建 PostgreSQL 表空间时没有指定空间的总大小
96 1

相关产品

  • 云原生数据库 PolarDB