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


10

我有一张有5万行的表格。它实际上是一个PostGIS表。

该查询分为4个部分(1个必填项)(3个可选)

  1. 长4纬度(我使用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个单独的索引(地理框,国家/地区名称,file_type和日期),但是要查看复合查询的作用。

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

  1. 顺序很重要。选择最能减少行数的索引作为第一个索引(在我的情况下,简单的多边形或多多边形的位置(地理位置)将是最好的)。
  2. 有时查询会跳过索引。但是,如果我使用键(#1,#2,#3,#4)创建了一个复合查询,那么即使用户创建了要求#1,#3的内容,计划者仍会使用单个复合查询,因为他们要订购被维持。
  3. 通常,我将创建三个BTREE查询和一个GIST(针对地理位置类型)。PostGIS不支持从多个索引类型创建复合。因此,我将不得不使用GIST复合索引。但这不应该伤害任何事情。
  4. 如果我确实创建了其他一些复合或单值索引,则计划程序足够聪明,可以选择最聪明的一个。
  5. 国家/地区名称可以有大约250个不同的值,并且显然与位置(地理框)紧密相关,但是如果要减小行大小的下一个最佳索引是file_type,我应该在下一个使用。我不希望用户在他们的查询集中经常使用国家或日期。
  6. 我不必担心创建4个键的复合索引会大大增加索引数据的大小。即,如果一键索引将是性能提升的90%,那么再添加3项使其复利也不会有什么坏处。相反,我应该真正创建两个索引。一个单一的地理索引,还有一个复合索引,然后让计划者确定哪一个是最佳的,并且它将考虑索引表的大小。

再说一次,我不是要任何人来设计我的解决方案,也不是要别人的工作。但是我确实需要PostGreSQL文档不会告诉我有关实现的内容

[我没有显示EXPLAIN结果的原因是,我必须从24M行表中创建此25K行表。这比我想象的要花费更多的时间。我将事物分为1,000个项目组,并让用户针对25K行表进行查询。但是,我的下一个问题将涉及使用该查询的结果转到MASTER 25M行表并提取内容,这就是复合索引的性能真正达到HIT的位置。


下面的示例查询:


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')) ;

解释分析结果(我没有放入任何复合索引,并且从速度来看,我不知道是否需要这样做)。

"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"

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'))

2
请提供实际查询。
ypercubeᵀᴹ

“ 3可选”是否意味着查询可以有8种不同的变化形式(取决于是否激活了2,3,4选项)?
ypercubeᵀᴹ

WHERE有4个AND组件。在st_intersects上是必需的,其他的可能在那儿,也可能不在。但是我想处理所有它们都存在的情况。

2
我投票决定将问题迁移到dba.se,这是一个具有多个范围条件的复杂查询。
ypercubeᵀᴹ

1
显示EXPLAIN ANALYZE查询。
Craig Ringer 2014年

Answers:


4

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

  1. 是的,顺序很重要,但是只有第一个确实不同,其余都是第二类索引。
  2. 我不确定它是否会同时使用两者,我猜想查询计划程序将使用#1,然后对其余部分进行巧妙的处理。
  3. 我没有GIST的经验。
  4. 是的,首先添加所有索引,查看使用最多的索引和提供最佳性能的索引。
  5. 我想您会同时尝试并评估最有效的方法。尝试用不同的子查询(可能是国家和时间)将sql重写为一个,然后与intersect-query联接。只要IN列表的长度不超过数千个元素,我就不会注意到IN子句的任何性能问题。我的猜测是,根据可用的输入条件专门调整的一些不同查询将提供最佳结果。
  6. 我建议不要制作4向索引。尝试创建一个,然后检查其大小,它们会变得非常大。以我的经验,四个1键索引几乎与单个4向索引一样快。对于某些特定查询而言,效果很好的一个技巧是局部索引,即像这样的东西:

    在table_x(key1,key2,key3)上创建索引WHERE some_x_column ='XXXX';

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

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


1
这些都是极好的技巧。我听取了您的建议,然后用它对我们维护的更大的表(4300万行)进行了实验。中的结果是:dba.stackexchange.com/questions/61084/...
Dr.YSG

1

我认为最有帮助的事情(如果有的话)是将product_type作为第二列添加到要点索引。但是,在不知道有多少行与您的典型/问题查询的每个AND条件(单独)匹配的情况下,我们只能猜测。

当我处理此问题时,我要做的第一件事是以简化形式运行查询,其中WHERE子句只有一个条件,每个条件都在EXPLAIN ANALYZE下轮流使用。查看每行的估计行和实际行。


请参阅上面的更新,但我认为您给我带来了很好的帮助,请考虑对索引进行排序,以最快的速度减少行输出。那正确吗?
YSG博士2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.