【问题标题】:Does PostgreSQL self join ignore indexes?PostgreSQL 自联接是否忽略索引?
【发布时间】:2015-06-10 20:42:55
【问题描述】:

我在 Postgresql 8.4.12 中有下表:

           Table "public.ratings"
 Column |          Type          | Modifiers
--------+------------------------+-----------
 userid | character varying(128) |
 item   | character varying(128) |
 score  | integer                |
Indexes:
    "ratings_item" btree (item)
    "ratings_ui" btree (userid, item)
    "ratings_userid" btree (userid)

我想执行自联接以查找由所有对特定项目评分的用户评分的项目。为简单起见,我将使用查询来获取每个可疑相似项目的评分数,如下所示;

select r2.item,sum(1)
from ratings r1
left join ratings r2 using (userid)
where r1.item='an3.php'
group by r2.item

查询有效,但对于我的包含 3600 万条记录的表,它需要很长时间。当我解释该声明时,我得到以下信息:

 GroupAggregate  (cost=8102958.42..8247621.18 rows=16978 width=17)    ->  Sort  (cost=8102958.42..8151108.60 rows=19260072 width=17)
         Sort Key: r2.item
         ->  Hash Left Join  (cost=1458652.29..4192647.43 rows=19260072 width=17)
               Hash Cond: ((r1.userid)::text = (r2.userid)::text)
               ->  Bitmap Heap Scan on ratings r1  (cost=868.20..77197.24 rows=24509 width
=22)
                     Recheck Cond: ((item)::text = 'an3.php'::text)
                     ->  Bitmap Index Scan on ratings_item  (cost=0.00..862.07 rows=24509 width=0)
                           Index Cond: ((item)::text = 'an3.php'::text)
               ->  Hash  (cost=711028.93..711028.93 rows=36763293 width=39)
                     ->  Seq Scan on ratings r2  (cost=0.00..711028.93 rows=36763293 width
=39)

根据经验,我认为“Seq Scan on rating r2”是罪魁祸首。

另一方面,如果我搜索一个不存在的项目:

select r2.item,sum(1) from ratings r1 left join ratings r2 using (userid)
where r1.item='targetitem' group by r2.item;

它似乎工作正常(即没有返回结果并且是立即的)

GroupAggregate  (cost=2235887.19..2248234.70 rows=16978 width=17)    ->  Sort  (cost=2235887.19..2239932.29 rows=1618038 width=17)
         Sort Key: r2.item
         ->  Nested Loop Left Join  (cost=0.00..1969469.94 rows=1618038 width=17)
               ->  Index Scan using ratings_item on ratings r1  (cost=0.00..8317.74 rows=2 059 width=22)
                     Index Cond: ((item)::text = 'targetitem'::text)
               ->  Index Scan using ratings_userid on ratings r2  (cost=0.00..947.24 rows= 419 width=39)
                     Index Cond: ((r1.userid)::text = (r2.userid)::text) 

同一个表和查询在 MySQL 中运行良好,但我无法将我的推荐系统迁移到另一个数据库。

我做错了什么还是 Postgres 出了问题?有解决办法吗?

【问题讨论】:

  • 有多少行匹配到an3.php?是否进行了真空分析?如果匹配的行非常多,索引可能就没有用了,
  • 可能有数百万个可能的结果。 Mysql 能够在几秒钟内为每个查询返回一些东西。我将运行真空分析评级并重试。
  • vacum 分析评级已完成,但仍希望对评级 r2 进行顺序扫描。
  • Postgresql 中的索引可以返回多少个结果有限制吗?

标签: sql postgresql indexing self-join postgresql-performance


【解决方案1】:

要回答标题中的(修辞)问题:否。

我在这里看到了很多问题,从第一行开始。

Postgres 8.4 has reached EOL last year。没有人应该再使用它了,它太旧了。尽可能升级到当前版本。

除此之外,您至少应该使用最新的次要版本。 8.4.12 于 2012-06-04 发布,缺少两年的错误和安全修复。 8.2.23 是死版的最后一个版本。
Read the versioning policy of the project.

接下来,varchar(128) 作为 PK / FK 效率非常低,尤其是对于具有数百万行的表。处理起来不必要的大且昂贵。请改用integer or bigint。或者UUID,如果你真的需要更大的数字空间(我怀疑)。

接下来,我看不到 (userid, item) (which would obsolete an additional index on the same) 上的 UNIQUEPRIMARY KEY 约束。要么您的表定义缺失,要么您的查询错误,或者您的问题有问题。

试试这个重写的查询:

SELECT r2.item, count(*) AS ct
FROM  (
   SELECT userid
   FROM   ratings
   WHERE  item = 'an3.php'
   GROUP  BY 1  -- should not be necessary, but constraint is missing
   ) r1
JOIN   ratings r2 USING (userid)
GROUP  BY 1;

在现代 Postgres 中,您需要两个索引才能获得最佳性能。在(item, userid)(userid, item)

在 Postgres 9.2+ 中,您甚至可以从中获得仅索引扫描。我不确定如何充分利用过时的版本。无论哪种方式,varchar(128) 也是一种昂贵的索引数据类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    相关资源
    最近更新 更多