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

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: postgresql 数据库 多维空间


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

例如我们在数据库中存储了多维几何对象,可以使用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数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
28天前
|
SQL 关系型数据库 数据库
手把手教你管理PostgreSQL数据库及其对象
手把手教你管理PostgreSQL数据库及其对象
21 0
|
3月前
|
自然语言处理 关系型数据库 数据库
技术经验解读:【转】PostgreSQL的FTI(TSearch)与中文全文索引的实践
技术经验解读:【转】PostgreSQL的FTI(TSearch)与中文全文索引的实践
28 0
|
11月前
|
存储 NoSQL 关系型数据库
深入探索地理空间查询:如何优雅地在MySQL、PostgreSQL及Redis中实现精准的地理数据存储与检索技巧
深入探索地理空间查询:如何优雅地在MySQL、PostgreSQL及Redis中实现精准的地理数据存储与检索技巧
1721 0
|
4月前
|
弹性计算 关系型数据库 数据库
开源PostgreSQL在倚天ECS上的最佳优化实践
本文基于倚天ECS硬件平台,以自顶向下的方式从上层应用、到基础软件,再到底层芯片硬件,通过应用与芯片的硬件特性的亲和性分析,实现PostgreSQL与倚天芯片软硬协同的深度优化,充分使能倚天硬件性能,帮助开源PostgreSQL应用实现性能提升。
|
4月前
|
SQL 运维 关系型数据库
基于AnalyticDB PostgreSQL的实时物化视图研发实践
AnalyticDB PostgreSQL版提供了实时物化视图功能,相较于普通(非实时)物化视图,实时物化视图无需手动调用刷新命令,即可实现数据更新时自动同步刷新物化视图。当基表发生变化时,构建在基表上的实时物化视图将会自动更新。AnalyticDB PostgreSQL企业数据智能平台是构建数据智能的全流程平台,提供可视化实时任务开发 + 实时数据洞察,让您轻松平移离线任务,使用SQL和简单配置即可完成整个实时数仓的搭建。
143973 8
|
4月前
|
SQL 关系型数据库 MySQL
MySQL【实践 02】MySQL迁移到PostgreSQL数据库的语法调整说明及脚本分享(通过bat命令修改mapper文件内的SQL语法)
MySQL【实践 02】MySQL迁移到PostgreSQL数据库的语法调整说明及脚本分享(通过bat命令修改mapper文件内的SQL语法)
204 0
|
存储 关系型数据库 数据库
沉浸式学习PostgreSQL|PolarDB 13: 博客、网站按标签内容检索, 并按匹配度排序
本文主要教大家怎么用好数据库, 而不是怎么运维管理数据库、怎么开发数据库内核.
750 0
|
关系型数据库 测试技术 分布式数据库
PolarDB | PostgreSQL 高并发队列处理业务的数据库性能优化实践
在电商业务中可能涉及这样的场景, 由于有上下游关系的存在, 1、用户下单后, 上下游厂商会在自己系统中生成一笔订单记录并反馈给对方, 2、在收到反馈订单后, 本地会先缓存反馈的订单记录队列, 3、然后后台再从缓存取出订单并进行处理. 如果是高并发的处理, 因为大家都按一个顺序获取, 容易产生热点, 可能遇到取出队列遇到锁冲突瓶颈、IO扫描浪费、CPU计算浪费的瓶颈. 以及在清除已处理订单后, 索引版本未及时清理导致的回表版本判断带来的IO浪费和CPU运算浪费瓶颈等. 本文将给出“队列处理业务的数据库性能优化”优化方法和demo演示. 性能提升10到20倍.
795 4
|
存储 对象存储 块存储
|
SQL 存储 DataWorks
《阿里云认证的解析与实战-数据仓库ACP认证》——云原生数据仓库AnalyticDB PostgreSQL版解析与实践(上)——一、产品概述
《阿里云认证的解析与实战-数据仓库ACP认证》——云原生数据仓库AnalyticDB PostgreSQL版解析与实践(上)——一、产品概述

热门文章

最新文章