多列空间+其他数据类型索引?需要在某个区域+某个时间范围内对 Postgres 查询进行极高的效率
Multi column spatial + other dtype indexes? Need extreme efficiency for Postgres queries within some area + some time range
我们需要极高的效率 运行ning 查询,我们需要在某个区域 + 某个时间范围内查找条目。两列都已编入索引。当 运行ning 这样查询时:
SELECT * FROM "tablename"
WHERE ("tablename"."geom" @ ST_GeomFromEWKB('\x0103000020e61000000100000005000000da194bfd61c81140605edb36e7db2040da194bfd61c81140745c3233d80d21409858cfff61a91240745c3233d80d21409858cfff61a91240605edb36e7db2040da194bfd61c81140605edb36e7db2040'::bytea)
AND "tablename"."date" >= '2019-01-01T00:00:00+00:00'::timestamptz)
解释显示 Postgres 运行 的两个位图索引扫描,然后是对这些子集的位图堆扫描。这是高性能但性能不够,因为数据库有数千万个条目,这些子集有数十万个条目,这个堆扫描 + 可能将子集写入磁盘导致不可接受的 ~1000 毫秒延迟。
多列索引会大大提高性能,但我们不知道如何创建 Postgres 将用于这些查询的索引。我们找到了 btree-gist,我们可以用它创建一个多列索引:
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE INDEX test_mult_idx ON tablename USING GIST (geom, date);
但是当我们运行 上面的查询使用这个多列索引时,它没有被使用,因为查询规划器使用每列的单列索引。我们放弃了这些单列索引来测试我们是否可以强制它并且它使用多列索引但没有性能改进。它只进行两次索引扫描,然后进行一次堆扫描。
我们已经搜索了 Internet,但在任何地方都找不到任何提及空间 + 其他字段多列索引的信息。我们不理解 运行s btree / gist / 其他类型的索引的数学 - 对于不同的数据类型,这在逻辑上是否可能?有什么我们可以做的吗?
评论者要求的额外信息:从 explain (analyze, buffers) select ...
生成的未格式化的执行计划
"Bitmap Heap Scan on historic (cost=1894.90..2485.28 rows=150 width=286) (actual time=110.183..226.016 rows=24115 loops=1)"
" Recheck Cond: ((geom @ '0103000020E610000001000000050000005E11FC6F25E26240ED2AA4FCA4FE40C05E11FC6F25E26240C0EC9E3C2CF440C0C58F31772DE96240C0EC9E3C2CF440C0C58F31772DE96240ED2AA4FCA4FE40C05E11FC6F25E26240ED2AA4FCA4FE40C0'::geometry) AND (date >= '2019-01-01 11:00:00+11'::timestamp with time zone))"
" Rows Removed by Index Recheck: 173436"
" Heap Blocks: exact=6673 lossy=7503"
" Buffers: shared read=18750"
" -> BitmapAnd (cost=1894.90..1894.90 rows=150 width=0) (actual time=108.636..108.637 rows=0 loops=1)"
" Buffers: shared read=4574"
" -> Bitmap Index Scan on geom_idx (cost=0.00..237.10 rows=5425 width=0) (actual time=81.729..81.729 rows=261632 loops=1)"
" Index Cond: (geom @ '0103000020E610000001000000050000005E11FC6F25E26240ED2AA4FCA4FE40C05E11FC6F25E26240C0EC9E3C2CF440C0C58F31772DE96240C0EC9E3C2CF440C0C58F31772DE96240ED2AA4FCA4FE40C05E11FC6F25E26240ED2AA4FCA4FE40C0'::geometry)"
" Buffers: shared read=4106"
" -> Bitmap Index Scan on date_idx (cost=0.00..1657.47 rows=150005 width=0) (actual time=21.662..21.662 rows=530545 loops=1)"
" Index Cond: (date >= '2019-01-01 11:00:00+11'::timestamp with time zone)"
" Buffers: shared read=468"
"Planning Time: 0.164 ms"
"Execution Time: 227.673 ms"
select column_name, data_type, character_maximum_length from INFORMATION_SCHEMA.COLUMNS
,不确定为什么空间字段是“USER-DEFINED”。其他列为整数、实数、文本和双精度:
"column_name" "data_type"
"geom" "USER-DEFINED"
"date" "timestamp without time zone"
您的 table 表示该列是“不带时区的时间戳”,但您的查询的值指定为“带时区的时间戳”。在我看来,仅此类型不匹配就足以阻止它使用 multi-column 索引。当然我无权访问您的真实数据集,所以我不知道那会发生什么。
我们需要极高的效率 运行ning 查询,我们需要在某个区域 + 某个时间范围内查找条目。两列都已编入索引。当 运行ning 这样查询时:
SELECT * FROM "tablename"
WHERE ("tablename"."geom" @ ST_GeomFromEWKB('\x0103000020e61000000100000005000000da194bfd61c81140605edb36e7db2040da194bfd61c81140745c3233d80d21409858cfff61a91240745c3233d80d21409858cfff61a91240605edb36e7db2040da194bfd61c81140605edb36e7db2040'::bytea)
AND "tablename"."date" >= '2019-01-01T00:00:00+00:00'::timestamptz)
解释显示 Postgres 运行 的两个位图索引扫描,然后是对这些子集的位图堆扫描。这是高性能但性能不够,因为数据库有数千万个条目,这些子集有数十万个条目,这个堆扫描 + 可能将子集写入磁盘导致不可接受的 ~1000 毫秒延迟。
多列索引会大大提高性能,但我们不知道如何创建 Postgres 将用于这些查询的索引。我们找到了 btree-gist,我们可以用它创建一个多列索引:
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE INDEX test_mult_idx ON tablename USING GIST (geom, date);
但是当我们运行 上面的查询使用这个多列索引时,它没有被使用,因为查询规划器使用每列的单列索引。我们放弃了这些单列索引来测试我们是否可以强制它并且它使用多列索引但没有性能改进。它只进行两次索引扫描,然后进行一次堆扫描。
我们已经搜索了 Internet,但在任何地方都找不到任何提及空间 + 其他字段多列索引的信息。我们不理解 运行s btree / gist / 其他类型的索引的数学 - 对于不同的数据类型,这在逻辑上是否可能?有什么我们可以做的吗?
评论者要求的额外信息:从 explain (analyze, buffers) select ...
"Bitmap Heap Scan on historic (cost=1894.90..2485.28 rows=150 width=286) (actual time=110.183..226.016 rows=24115 loops=1)"
" Recheck Cond: ((geom @ '0103000020E610000001000000050000005E11FC6F25E26240ED2AA4FCA4FE40C05E11FC6F25E26240C0EC9E3C2CF440C0C58F31772DE96240C0EC9E3C2CF440C0C58F31772DE96240ED2AA4FCA4FE40C05E11FC6F25E26240ED2AA4FCA4FE40C0'::geometry) AND (date >= '2019-01-01 11:00:00+11'::timestamp with time zone))"
" Rows Removed by Index Recheck: 173436"
" Heap Blocks: exact=6673 lossy=7503"
" Buffers: shared read=18750"
" -> BitmapAnd (cost=1894.90..1894.90 rows=150 width=0) (actual time=108.636..108.637 rows=0 loops=1)"
" Buffers: shared read=4574"
" -> Bitmap Index Scan on geom_idx (cost=0.00..237.10 rows=5425 width=0) (actual time=81.729..81.729 rows=261632 loops=1)"
" Index Cond: (geom @ '0103000020E610000001000000050000005E11FC6F25E26240ED2AA4FCA4FE40C05E11FC6F25E26240C0EC9E3C2CF440C0C58F31772DE96240C0EC9E3C2CF440C0C58F31772DE96240ED2AA4FCA4FE40C05E11FC6F25E26240ED2AA4FCA4FE40C0'::geometry)"
" Buffers: shared read=4106"
" -> Bitmap Index Scan on date_idx (cost=0.00..1657.47 rows=150005 width=0) (actual time=21.662..21.662 rows=530545 loops=1)"
" Index Cond: (date >= '2019-01-01 11:00:00+11'::timestamp with time zone)"
" Buffers: shared read=468"
"Planning Time: 0.164 ms"
"Execution Time: 227.673 ms"
select column_name, data_type, character_maximum_length from INFORMATION_SCHEMA.COLUMNS
,不确定为什么空间字段是“USER-DEFINED”。其他列为整数、实数、文本和双精度:
"column_name" "data_type"
"geom" "USER-DEFINED"
"date" "timestamp without time zone"
您的 table 表示该列是“不带时区的时间戳”,但您的查询的值指定为“带时区的时间戳”。在我看来,仅此类型不匹配就足以阻止它使用 multi-column 索引。当然我无权访问您的真实数据集,所以我不知道那会发生什么。