PostgreSQL 复合索引中的列顺序(和查询顺序)

Dr.*_*YSG 14 postgresql postgis

我有一个有 50K 行的表。它实际上是一个 PostGIS 表。

查询有 4 个部分(1 个强制)(3 个可选)

  1. 具有 4 lat,long 的交叉框(地理矩形)(我使用 st_intersects)[强制]
  2. 日期字段上的日期范围(最小值、最大值)
  3. 当前使用 IN( .....) 的文件类型(一组最多 8 个文本值),但如果需要,我可以将其设为临时表。我看到很多人不喜欢IN。
  4. 国家(一个文本值)。

我预计大约有 100 - 4,000 行返回

如果我在表上创建复合索引,我应该先使用哪一列。细粒度的可能是位置(数据分布在世界各地)。我目前将其作为 GIST 索引。

其他索引将是 BTREE。

我的直觉是使用细粒度的,当然最后。例如,只有大约 12 种文件类型,因此对于索引来说这将是非常大的存储桶。

PostgreSQL 和 PostGIS 大师(了解系统内部结构的人)怎么说?


更新:

让我尖锐地回答这个问题。

  1. 我不希望任何人不得不做我应该做的工作。我太尊重你的时间了。所以我将在稍后进行解释分析。
  2. 我所寻找的只是一些指示、技巧和指导方针。
  3. 我读了这篇很棒的小帖子:https : //devcenter.heroku.com/articles/postgresql-indexes#managing-and-maintaining-indexes关于索引
  4. 我通常做的是创建 4 个单独的索引(地理框、国家/地区名称、文件类型和日期),但想看看复合查询会做什么。

请告诉我这些假设是否有误。(我对复合索引的想法很陌生)

  1. 顺序很重要。选择最能减少行的索引作为第一个索引(在我的情况下,位置(地理)是一个简单的多边形或多多边形会做得最好)。
  2. 有时查询会跳过索引。但是,如果我使用键 (#1, #2, #3, #4) 创建一个复合查询,那么即使用户创建了要求 #1, #3 的内容,规划器仍将使用单个复合查询,因为他们订购被维护。
  3. 通常我会创建三个 BTREE 查询和一个 GIST(用于地理类型)。PostGIS 不支持从多个索引类型中创建复合。所以我将不得不使用 GIST 复合索引。但这不应该伤害事情。
  4. 如果我确实创建了一些额外的复合或单值索引,那么规划器就足够聪明,可以选择最聪明的一个......
  5. Country Name 可以有大约 250 个不同的值,并且显然与位置 (geobox) 密切相关,但是如果减少行大小的下一个最佳索引是 file_type,我接下来应该使用它。我不希望用户经常在他们的查询集中使用国家或日期。
  6. 我不必担心创建 4 个键的复合索引会大大增加索引数据的大小。即,如果一键索引可以实现 90% 的性能提升,那么再添加 3 个项目使其复合也无妨。相反,我真的应该创建两个索引。一个单一的地理索引,也一个复合索引,让规划器找出哪个最好,它会考虑索引表的大小。

再说一次,我不是要求任何人设计我的解决方案,我也不会胡思乱想其他人的工作。但我确实需要 PostGreSQL 文档没有告诉我有关实现的东西

[我还没有 EXPLAIN 结果显示的原因是我必须从 24M 行表创建这个 25K 行表。这比我想象的要花费更多的时间。我将事物聚类为 1,000 个项目组,并让用户查询 25K 行表。但我的下一个问题,将涉及使用该查询的结果转到 MASTER 25M 行表并将其提取出来,这就是复合索引的性能真正发挥作用的地方]。


示例查询如下:


SELECT
    public.product_list_meta_mv.cntry_name       AS country,
    public.product_list_meta_mv.product_producer AS producer,
    public.product_list_meta_mv.product_name     AS prod_name,
    public.product_list_meta_mv.product_type     AS ptype,
    public.product_list_meta_mv.product_size     AS size,
    ST_AsGeoJSON(public.product_list_meta_mv.the_geom, 10, 2)          AS outline
FROM
    public.product_list_meta_mv 
WHERE
    public.product_list_meta_mv.cntry_name = 'Poland' 
AND
    ST_Intersects(public.product_list_meta_mv.the_geom,
    st_geogfromtext('SRID=4326;POLYGON((21.23107910156250 51.41601562500000,
                                        18.64379882812500 51.41601562500000,
                                        18.64379882812500 48.69415283203130,
                                        21.23107910156250 48.69415283203130,
                                        21.23107910156250 51.41601562500000))')) 
AND (date >= '1/2/1900 5:00:00 AM' 
 AND date <= '2/26/2014 10:26:44 PM')
AND (public.product_list_meta_mv.product_type in
    ('CIB10','DTED0','DTED1','DTED2','CIB01','CIB05')) ;
Run Code Online (Sandbox Code Playgroud)

EXPLAIN ANALYZE 结果(我没有输入任何复合索引,从我看到的速度来看,我不知道是否需要)。

"Bitmap Heap Scan on catalog_full cat  (cost=4.33..37.49 rows=1 width=7428) (actual time=1.147..38.051 rows=35 loops=1)"
"  Recheck Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"  Filter: (((type)::text = ANY ('{CADRG,CIB10,DTED1,DTED2}'::text[])) AND (_st_distance('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography, outline, 0::double precision, false) < 1e-005::double precision))"
"  Rows Removed by Filter: 61"
"  ->  Bitmap Index Scan on catalog_full_outline_idx  (cost=0.00..4.33 rows=8 width=0) (actual time=0.401..0.401 rows=96 loops=1)"
"        Index Cond: ('0103000020E61000000100000005000000000000005838354000000000AEB0494000000000A0A7324000000000AEB0494000000000A0A73240000000006C5D48400000000058383540000000006C5D4840000000005838354000000000AEB04940'::geography && outline)"
"Total runtime: 38.109 ms"
Run Code Online (Sandbox Code Playgroud)
EXPLAIN ANALYZE SELECT pid,product_name,type,country,date,size,cocom,description,egpl_date,ST_AsGeoJSON(outline, 10, 2) AS outline 
FROM portal.catalog_full AS cat 
WHERE ST_Intersects(st_geogfromtext('SRID=4326;POLYGON((21.2200927734375 51.38031005859375, 18.65478515625 51.38031005859375, 18.65478515625 48.7298583984375, 21.2200927734375 48.7298583984375, 21.2200927734375 51.38031005859375))'), cat.outline) 
AND (cat.type in ('CADRG','CIB10','DTED1','DTED2'))
Run Code Online (Sandbox Code Playgroud)

小智 8

作为我工作的一部分,我维护了一个相当大的 PostgreSQL 数据库(磁盘上大约 120GB,几个数百万行的表)并收集了一些关于如何加快查询速度的技巧。首先对你的假设发表一些评论:

  1. 是的,顺序很重要,但只有第一个真正不同,其余的都是二等索引。
  2. 我不确定它是否总是同时使用两者,我的猜测是查询规划器将使用 #1,然后对其余部分做一些聪明的事情。
  3. 我没有 GIST 的经验。
  4. 是的,首先添加所有索引,看看什么是使用最多的,什么提供了最好的性能。
  5. 我建议您同时尝试两种方法并衡量哪种效果最好。尝试使用不同的子查询重写 sql,可能将国家和时间合二为一,然后加入相交查询。我没有注意到 IN 子句有任何性能问题,只要 IN 列表不是数千个元素。我的猜测是,根据可用的输入标准专门调整一些不同的查询将提供最佳结果。
  6. 我建议不要制作 4 向索引。尝试创建一个然后检查大小,它们会变得非常大。根据我的经验,四个 1-key 索引几乎和单个 4-way 索引一样快。一个对某些特定查询效果很好的技巧是部分索引,即这样的东西:

    CREATE INDEX ON table_x (key1, key2, key3) WHERE some_x_column = 'XXXX';

我在我的 .psqlrc 文件中创建了别名,并带有查询以帮助查找要添加或删除的索引。请随意在 GitHub 上查看它们: .psql

我经常使用 :seq_scans 和 :bigtables,然后使用 \d table_name 来获取有关该表的详细信息。完成一些更改后不要忘记重置统计信息,选择 pg_stat_reset();