相同的 postgres 查询在不同的数据库中产生不同的查询计划

Sam*_*aik 3 postgresql indexing

posgtres 版本 9.1.9

以下查询在两个不同的数据库中运行时会产生不同的计划。

explain (analyze,buffers) SELECT group_.groupid     AS groupId,
        group_.name        AS groupName,
        group_.type_       AS groupType,
        group_.friendlyurl AS groupFriendlyURL
 FROM   group_
        inner join groups_orgs
                ON ( groups_orgs.groupid = group_.groupid )
        inner join users_orgs
                ON ( users_orgs.organizationid = groups_orgs.organizationid )
 WHERE  ( group_.livegroupid = 0 )
        AND ( users_orgs.userid = '27091470' )
        AND ( group_.companyid = '20002' )
        AND ( group_.classnameid = 10001
               OR group_.classnameid = 10003 )
        AND ( group_.name != 'Control Panel' )
        AND ( group_.type_ != 4 )
;
Run Code Online (Sandbox Code Playgroud)

从生产数据库计划。

                                                                                      QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Merge Join  (cost=6.36..16.60 rows=1 width=37) (actual time=0.133..95.323 rows=3 loops=1)
   Merge Cond: (group_.groupid = groups_orgs.groupid)
   Buffers: shared hit=30829
   ->  Index Scan using group__pkey on group_  (cost=0.00..87997.62 rows=17244 width=37) (actual time=0.030..85.166 rows=13906 loops=1)
         Filter: (((name)::text <> 'Control Panel'::text) AND (type_ <> 4) AND (livegroupid = 0) AND (companyid = 20002::bigint) AND ((classnameid = 10001) OR (classnameid = 10003)))
         Buffers: shared hit=30824
   ->  Sort  (cost=6.36..6.37 rows=3 width=8) (actual time=0.076..0.079 rows=3 loops=1)
         Sort Key: groups_orgs.groupid
         Sort Method: quicksort  Memory: 25kB
         Buffers: shared hit=5
         ->  Merge Join  (cost=1.05..6.34 rows=3 width=8) (actual time=0.045..0.054 rows=3 loops=1)
               Merge Cond: (users_orgs.organizationid = groups_orgs.organizationid)
               Buffers: shared hit=5
               ->  Index Scan using users_orgs_pkey on users_orgs  (cost=0.00..10.47 rows=2 width=8) (actual time=0.012..0.014 rows=2 loops=1)
                     Index Cond: (userid = 27091470::bigint)
                     Buffers: shared hit=4
               ->  Sort  (cost=1.05..1.06 rows=3 width=16) (actual time=0.028..0.030 rows=3 loops=1)
                     Sort Key: groups_orgs.organizationid
                     Sort Method: quicksort  Memory: 25kB
                     Buffers: shared hit=1
                     ->  Seq Scan on groups_orgs  (cost=0.00..1.03 rows=3 width=16) (actual time=0.003..0.005 rows=3 loops=1)
                           Buffers: shared hit=1
Run Code Online (Sandbox Code Playgroud)

从通过从生产中导出/导入数据创建的数据库进行计划

                                                                                      QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..18.19 rows=1 width=36) (actual time=0.053..0.104 rows=3 loops=1)
   Buffers: shared hit=18
   ->  Nested Loop  (cost=0.00..9.77 rows=1 width=8) (actual time=0.036..0.065 rows=3 loops=1)
         Join Filter: (groups_orgs.organizationid = users_orgs.organizationid)
         Buffers: shared hit=6
         ->  Seq Scan on groups_orgs  (cost=0.00..1.03 rows=3 width=16) (actual time=0.007..0.010 rows=3 loops=1)
               Buffers: shared hit=1
         ->  Materialize  (cost=0.00..8.66 rows=2 width=8) (actual time=0.008..0.012 rows=3 loops=3)
               Buffers: shared hit=5
               ->  Index Scan using ix_fb646ca6 on users_orgs  (cost=0.00..8.65 rows=2 width=8) (actual time=0.016..0.021 rows=3 loops=1)
                     Index Cond: (userid = 27091470::bigint)
                     Buffers: shared hit=5
   ->  Index Scan using group__pkey on group_  (cost=0.00..8.41 rows=1 width=36) (actual time=0.008..0.010 rows=1 loops=3)
         Index Cond: (groupid = groups_orgs.groupid)
         Filter: (((name)::text <> 'Control Panel'::text) AND (type_ <> 4) AND (livegroupid = 0) AND (companyid = 20002::bigint) AND ((classnameid = 10001) OR (classnameid = 10003)))
         Buffers: shared hit=12
Run Code Online (Sandbox Code Playgroud)

生产查询需要大约 100 毫秒,而在其他数据库中需要 0.1 毫秒 差异似乎是group_表 ( Index Scan using group__pkey on group_)上的慢速索引扫描谁能解释执行时间的差异?生产中的表会定期进行清理和分析。生产数据库比其他数据库更忙。

谢谢,萨米尔

Tho*_*ena 6

我遇到了同样的问题,在“备份”数据库中,查询速度非常快,而在生产数据库中运行时相同的查询速度非常慢。事实证明,我只需要更频繁地执行例行数据库维护任务即可解决问题:

PostgreSQL 查询计划器依靠有关表内容的统计信息来生成良好的查询计划。这些统计信息由 ANALYZE 命令收集,该命令可以自行调用或作为 VACUUM 中的可选步骤调用。拥有合理准确的统计数据很重要,否则计划选择不当可能会降低数据库性能。

因此,如果您遇到类似问题,请尝试在“慢”数据库上运行以下命令:

$ vacuumdb --host localhost --port 5432 --username "MyUser" -d "MyDatabase" --analyze --verbose
Run Code Online (Sandbox Code Playgroud)

  • 谢谢一吨!我们的自动清理任务显然不足以改善极慢的查询计划。 (2认同)