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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: postgresql 数据库 多维空间

2000元阿里云代金券免费领取,2核4G云服务器仅664元/3年,新老用户都有优惠,立即抢购>>>


阿里云采购季(云主机223元/3年)活动入口:请点击进入>>>,


阿里云学生服务器(9.5元/月)购买入口:请点击进入>>>,


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

例如我们在数据库中存储了多维几何对象,可以使用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数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
15天前
|
存储 SQL Cloud Native
深入了解云原生数据库CockroachDB的概念与实践
作为一种全球领先的分布式SQL数据库,CockroachDB以其高可用性、强一致性和灵活性等特点备受关注。本文将深入探讨CockroachDB的概念、设计思想以及实践应用,并结合实例演示其在云原生环境下的优越表现。
|
15天前
|
Cloud Native 关系型数据库 大数据
CockroachDB:云原生数据库的新概念与实践
本文将介绍CockroachDB,一种先进的云原生数据库,它具备分布式、强一致性和高可用性等特点。我们将探讨CockroachDB的基本原理、架构设计以及在实际应用中的种种优势和挑战。
|
15天前
|
关系型数据库 MySQL 分布式数据库
PolarDB MySQL版并行查询技术探索与实践
PolarDB MySQL版并行查询技术探索与实践 PolarDB MySQL版在企业级查询加速特性上进行了深度技术探索,其中并行查询作为其重要组成部分,已经在线稳定运行多年,持续演进。本文将详细介绍并行查询的背景、挑战、方案、特性以及实践。
230 2
|
8月前
|
关系型数据库 分布式数据库 数据库
PolarDB对比X-Engine与InnoDB空间效率
本实验带您体验创建X-Engine和InnoDB两种不同的表存储引擎,通过Sysbench模拟数据注入的过程对比俩种表引擎的空间效率。
548 0
|
9天前
|
监控 关系型数据库 分布式数据库
【PolarDB 开源】PolarDB HTAP 实践:混合事务与分析处理的性能优化策略
【5月更文挑战第21天】PolarDB开源后在HTAP领域表现出色,允许在同一系统处理事务和分析工作负载,提高数据实时性。通过资源分配、数据分区、索引优化等策略提升性能。示例代码展示了创建和查询事务及分析表的基本操作。PolarDB还提供监控工具,帮助企业优化系统并应对业务变化。其HTAP能力为开发者和企业提供了强大支持,推动技术进步,加速数字化时代的业务发展。
29 1
|
2天前
|
安全 关系型数据库 分布式数据库
【PolarDB 开源】PolarDB 在金融行业中的实践:高可用与安全合规解决方案
【5月更文挑战第28天】PolarDB,一款适用于金融行业的强大数据库,以其高可用性和安全合规性脱颖而出。通过多副本机制和自动故障转移确保业务连续性,结合严格的访问控制和数据加密技术保护信息安全。在实际应用中,如银行核心系统,PolarDB 负责处理海量交易数据,同时支持主从架构以备故障切换。此外,设置强密码策略和加密存储确保合规性,并通过监控预警及时解决问题。随着金融科技发展,PolarDB 将在云原生架构和人工智能等领域发挥更大作用,助力金融行业创新与进步。
83 0
|
4天前
|
负载均衡 关系型数据库 分布式数据库
【PolarDB开源】PolarDB读写分离实践:优化读取性能与负载均衡策略
【5月更文挑战第26天】PolarDB是云原生关系型数据库,通过读写分离优化性能和扩展性。它设置主节点处理写操作,从节点处理读操作,异步复制保证数据一致性。优化读取性能的策略包括增加从节点数量、使用只读实例和智能分配读请求。负载均衡策略涉及基于权重、连接数和地理位置的分配。实践示例中,电商网站通过主从架构、只读实例和负载均衡策略提升商品查询效率。PolarDB的读写分离与负载均衡为企业应对大数据和高并发提供了有效解决方案。
121 0
|
6天前
|
SQL 监控 关系型数据库
【PolarDB开源】PolarDB SQL优化实践:提升查询效率与资源利用
【5月更文挑战第24天】PolarDB是高性能的云原生数据库,强调SQL查询优化以提升性能。本文分享了其SQL优化策略,包括查询分析、索引优化、查询重写、批量操作和并行查询,以及性能监控与调优方法。通过这些措施,可以减少响应时间、提高并发处理能力和降低成本。文中还提供了相关示例代码,展示如何分析查询和创建索引,帮助用户实现更高效的数据库管理。
41 1
|
15天前
|
分布式计算 关系型数据库 大数据
MaxCompute产品使用合集之怎么才可以将 PostgreSQL 中的 geometry 空间类型字段同步到 MaxCompute 或另一个 PostgreSQL 数据库
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
9月前
|
SQL 关系型数据库 MySQL
PolarDB-X 针对跑批场景的思考和实践
金融行业和运营商系统,业务除了在线联机查询外,同时有离线跑批处理,跑批场景比较注重吞吐量,同时基于数据库场景有一定的使用惯性,比如直连MySQL分库分表的存储节点做本地化跑批、以及基于Oracle/DB2等数据库做ETL的数据清洗跑批等。
PolarDB-X 针对跑批场景的思考和实践
http://www.vxiaotou.com