Sql 查询计划器选择嵌套联接不正确

Sql 查询计划器选择嵌套联接不正确,sql,postgresql,database-design,indexing,postgresql-performance,Sql,Postgresql,Database Design,Indexing,Postgresql Performance,我从解释分析中得到了这个 -> Nested Loop (cost=2173.66..30075.48 rows=77 width=4) (actual time=30.949..399.463 rows=95959 loops=1) 因此,预期行与实际行之间几乎存在3个数量级的差异,这导致查询速度非常慢 我将默认的_statistics _目标提升到10000,并运行真空/分析以使查询计划器与新的统计信息保持最新。如何让查询计划器选择更好的加入

我从解释分析中得到了这个

 ->  Nested Loop  (cost=2173.66..30075.48 rows=77 width=4)
                  (actual time=30.949..399.463 rows=95959 loops=1)
因此,预期行与实际行之间几乎存在3个数量级的差异,这导致查询速度非常慢

我将默认的_statistics _目标提升到10000,并运行真空/分析以使查询计划器与新的统计信息保持最新。如何让查询计划器选择更好的加入策略

我使用的是postgres 9.3.1。我的所有planner成本常量仍为默认值,因此:

seq_page_cost: 1 random_page_cost: 4 cpu_tuple_cost: .01 cpu_index_tuple_cost: .005 cpu_operator_cost: .0025 effective_cache_size: 128MB 我知道这太多了,感谢您花时间查看服务器配置 这一点很清楚:默认设置非常保守,适用于资源有限的小型安装。对于专用的DB服务器,一些默认设置是不够的。你必须调整你的设置

首先,如果您有足够的RAM来缓存全部或大部分数据库,请将
random\u page\u成本设置得大大降低。并增加CPU操作的相对成本。类似(这纯粹是猜测!):

要点

  • 冗余:
    和a.active\u on不为空
    ,因为您还有
    和a.active\u on,您需要提供更多信息。显然,您的Postgres版本、表定义、查询以及
    Postgres.conf
    :中的相关设置是最开始的。考虑标签<代码> PostgreSQL性能< /代码>中的说明。若要调试,请在会话中尝试设置enable_nestloop=FALSE,然后再次运行。嵌套循环:tango需要两个人。在最简单的情况下,一个较小,另一个具有可用的PK、FK或二级索引。表定义、基数、查询、RAM?不相关:
    左侧外部连接部门
    和departments.lft=8))
    ::您的左连接变为直连接。你的ORM可能不符合你的要求。顺便说一句:你提供的DDL是无用的。最好使用
    pg_dump--schema only…
    中的一段代码,经过多次编辑后,您的数据仍然不一致<代码>访问权限。部分
    引用了很多地方,但在表定义中缺少。感谢您在这方面的帮助。为什么缓存大部分数据库会使CPU操作更昂贵?@jvans:不会。页面查找变得更便宜。这些都是相对数,没有绝对意义。CPU操作变得相对昂贵。这很有道理。那么,可以公平地说,有效的缓存大小与cpu成本估算成反比吗?每当您增加缓存时,cpu成本就会相对较高,您应该增加cpu成本估算值?@jvans:通常是这样。但是请注意,
    effective\u cache\u size
    实际上不会更改缓存大小。它只告诉Postgres在规划查询时应该考虑多少有效缓存大小。Postgres从自己的专用RAM和系统缓存中获利。这方面的帮助真是太棒了。非常有用。
    Aggregate  (cost=30444.87..30444.88 rows=1 width=0) (actual time=535.077..535.077     rows=1 loops=1)
          ->  Nested Loop  (cost=2174.08..30444.68 rows=76 width=0) (actual time=23.208..527.062 rows=95451 loops=1)
            ->  Nested Loop  (cost=2173.66..30075.48 rows=77 width=4) (actual time=23.200..351.275 rows=95959 loops=1)
              ->  Hash Left Join  (cost=2173.24..28013.64 rows=401 width=4) (actual time=23.188..133.224 rows=103609 loops=1)
                    Hash Cond: (access_rights.target_id = departments.id)
                    Join Filter: ((access_rights.target_type)::text = 'Department'::text)
                    Filter: ((((access_rights.target_type)::text = 'Company'::text) AND (access_rights.target_id = 173)) OR (((access_rights.target_type)::text = 'User'::text) AND (access_rights.target_id = 11654)) OR (((access_rights.target_type)::text = 'UserGroup'::text) AND (access_rights.target_id = 126)) OR (((access_rights.target_type)::text = 'Department'::text) AND (departments.lft <= 7) AND (departments.rgt >= 8)))
                    Rows Removed by Filter: 59127
                    ->  Bitmap Heap Scan on access_rights  (cost=2135.97..27236.01 rows=26221 width=14) (actual time=22.844..79.391 rows=162736 loops=1)
                          Recheck Cond: ((((target_type)::text = 'Company'::text) AND (target_id = 173) AND ((section)::text = 'shop'::text)) OR (((target_type)::text = 'User'::text) AND (target_id = 11654) AND ((section)::text = 'shop'::text)) OR (((target_type)::text = 'UserGroup'::text) AND (target_id = 126) AND ((section)::text = 'shop'::text)) OR ((target_type)::text = 'Department'::text))
                          Filter: (((section)::text = 'shop'::text) AND (((active_on IS NOT NULL) AND (active_on <= '2013-10-29'::date) AND ((inactive_on IS NULL) OR (inactive_on > '2013-10-29'::date)) AND (frozen_activation IS NULL)) OR ((frozen_activation)::text = 'active'::text)))
                          Rows Removed by Filter: 9294
                          ->  BitmapOr  (cost=2135.97..2135.97 rows=80823 width=0) (actual time=22.530..22.530 rows=0 loops=1)
                                ->  Bitmap Index Scan on index_access_rights_on_tt_ti_cfc_cfv_ti_s  (cost=0.00..643.10 rows=6861 width=0) (actual time=16.106..16.106 rows=96993 loops=1)
                                      Index Cond: (((target_type)::text = 'Company'::text) AND (target_id = 173) AND ((section)::text = 'shop'::text))
                                ->  Bitmap Index Scan on index_access_rights_on_tt_ti_cfc_cfv_ti_s  (cost=0.00..4.77 rows=12 width=0) (actual time=0.033..0.033 rows=0 loops=1)
                                      Index Cond: (((target_type)::text = 'User'::text) AND (target_id = 11654) AND ((section)::text = 'shop'::text))
                                ->  Bitmap Index Scan on index_access_rights_on_tt_ti_cfc_cfv_ti_s  (cost=0.00..11.68 rows=112 width=0) (actual time=0.238..0.238 rows=1200 loops=1)
                                      Index Cond: (((target_type)::text = 'UserGroup'::text) AND (target_id = 126) AND ((section)::text = 'shop'::text))
                                ->  Bitmap Index Scan on index_access_rights_on_target_type  (cost=0.00..1450.21 rows=73837 width=0) (actual time=6.148..6.148 rows=73837 loops=1)
                                      Index Cond: ((target_type)::text = 'Department'::text)
                    ->  Hash  (cost=24.34..24.34 rows=1034 width=12) (actual time=0.331..0.331 rows=1034 loops=1)
                          Buckets: 1024  Batches: 1  Memory Usage: 45kB
                          ->  Seq Scan on departments  (cost=0.00..24.34 rows=1034 width=12) (actual time=0.004..0.179 rows=1034 loops=1)
              ->  Index Scan using tickets_pkey on tickets  (cost=0.42..5.13 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=103609)
                    Index Cond: (id = access_rights.ticket_id)
                    Filter: (((hold_until IS NULL) OR (hold_until <= '2013-10-29 00:00:00'::timestamp without time zone)) AND (company_id = 173))
                    Rows Removed by Filter: 0
        ->  Index Scan using events_pkey on events  (cost=0.42..4.78 rows=1 width=4) (actual time=0.001..0.002 rows=1 loops=95959)
              Index Cond: (id = tickets.event_id)
              Filter: ((NOT activity) AND ((canceled_at IS NULL) OR (canceled_at > '2013-10-29 23:11:37.486572'::timestamp without time zone)))
              Rows Removed by Filter: 0
    Total runtime: 535.165 ms
    
    EXPLAIN ANALYZE
    SELECT count(*) AS count_all
    FROM "events"
    INNER JOIN tickets ON events.id = tickets.event_id
    INNER JOIN access_rights ON access_rights.ticket_id = tickets.id
    LEFT OUTER JOIN departments ON departments.id = access_rights.target_id
         AND access_rights.target_type = 'Department'
    WHERE ((("events"."activity" = 'f') AND (events.canceled_at IS NULL OR events.canceled_at > '2013-10-29 23:11:37.486572'))
    AND ((((((access_rights.section = 'shop') AND (access_rights.target_type = 'Company'
    AND access_rights.target_id = 173)) OR ((access_rights.section = 'shop')
    AND (access_rights.target_type = 'User' AND access_rights.target_id = 11654)) OR ((access_rights.section = 'shop')
    AND (access_rights.target_type = 'UserGroup'
    AND access_rights.target_id IN ('126'))) OR ((access_rights.section = 'shop')
    AND (access_rights.target_type = 'Department'
    AND departments.lft <= 7 AND departments.rgt >= 8))) 
    AND ((access_rights.section = 'shop')
    AND ((((access_rights.section = 'shop')
    AND (access_rights.active_on IS NOT NULL
    AND access_rights.active_on <= '2013-10-29'
    AND (access_rights.inactive_on IS NULL OR access_rights.inactive_on > '2013-10-29')))
    AND (access_rights.frozen_activation IS NULL)) OR ((access_rights.section = 'shop')
    AND (access_rights.frozen_activation = 'active')))))
    AND (tickets.hold_until IS NULL OR tickets.hold_until <= '2013-10-29'))
    AND (tickets.company_id = 173)));
    
    CREATE TABLE tickets (
        hold_until timestamp without time zone,
        event_id integer,
        id integer NOT NULL
     );
    
    Indexes:
        "tickets_pkey" PRIMARY KEY, btree (id)
        "index_tickets_on_company_id" btree (company_id)
        "index_tickets_on_created_at" btree (created_at)
        "index_tickets_on_creation_id" btree (creation_id)
        "index_tickets_on_event_id" btree (event_id)
        "index_tickets_on_hold_until" btree (hold_until)
    
    Foreign-key constraints:
        "tickets_attendee_id_fk" FOREIGN KEY (attendee_id) REFERENCES attendees(id)
        "tickets_company_id_fk" FOREIGN KEY (company_id) REFERENCES companies(id)
        "tickets_event_id_fk" FOREIGN KEY (event_id) REFERENCES events(id)
    
    CREATE TABLE events (
         id integer NOT NULL,
         activity boolean DEFAULT false NOT NULL
     );
    
    Indexes:
        "events_pkey" PRIMARY KEY, btree (id)
        "index_events_on_id_and_te_id" UNIQUE, btree (id, te_id)
        "index_events_on_activity" btree (activity)
        "index_events_on_canceled_at" btree (canceled_at)
        "index_events_on_company_id" btree (company_id)
        "index_events_on_name" btree (name)
        "index_events_on_occurs_at" btree (occurs_at)
    
    Foreign-key constraints:
        "events_company_id_fk" FOREIGN KEY (company_id) REFERENCES companies(id)
    
    CREATE TABLE departments (
       id integer NOT NULL,
       parent_id integer,
       lft integer NOT NULL,
       rgt integer NOT NULL
    );
    
    Indexes:
       "departments_pkey" PRIMARY KEY, btree (id)
       "index_departments_on_company_id_and_parent_id_and_name" UNIQUE, btree (company_id, parent_id, name)
       "index_departments_on_company_id" btree (company_id)
       "index_departments_on_lft" btree (lft)
       "index_departments_on_name" btree (name)
       "index_departments_on_parent_id" btree (parent_id)
       "index_departments_on_rgt" btree (rgt)
    
    Foreign-key constraints:
       "departments_company_id_fk" FOREIGN KEY (company_id) REFERENCES companies(id)
    
    CREATE TABLE access_rights (
       id integer NOT NULL,
       target_type character varying(255) NOT NULL,
       target_id integer NOT NULL,
       ticket_id integer NOT NULL,
       active_on date,
       visible boolean,
       inactive_on date,
       frozen_activation character varying(255)
    );
    
    Indexes:
       "access_rights_pkey" PRIMARY KEY, btree (id)
       "index_access_rights_on_tt_ti_cfc_cfv_ti_s" UNIQUE, btree (target_type, target_id, custom_field_condition, custom_field_value, ticket_id, section)
       "index_access_rights_on_active_on" btree (active_on)
       "index_access_rights_on_custom_field_value" btree (custom_field_value)
       "index_access_rights_on_frozen_activation" btree (frozen_activation)
       "index_access_rights_on_inactive_on" btree (inactive_on)
       "index_access_rights_on_section" btree (section)
       "index_access_rights_on_target_id" btree (target_id)
       "index_access_rights_on_target_type" btree (target_type)
       "index_access_rights_on_target_type_and_target_id" btree (target_type, target_id) CLUSTER
       "index_access_rights_on_ticket_id" btree (ticket_id)
       "index_access_rights_on_visible" btree (visible)
    
    Foreign-key constraints:
       "access_rights_ticket_id_fk" FOREIGN KEY (ticket_id) REFERENCES tickets(id)
    
    seq_page_cost: 1 random_page_cost: 1.2 cpu_tuple_cost: .02 cpu_index_tuple_cost: .02 cpu_operator_cost: .005
    SELECT count(*) AS count_all
    FROM   events           e
    JOIN   tickets          t ON t.event_id = e.id
    JOIN   access_rights    a ON a.ticket_id = t.id
    LEFT   JOIN departments d ON d.id = a.target_id
                             AND a.target_type = 'Department'
    WHERE  e.activity = 'f'
    AND   (e.canceled_at IS NULL OR e.canceled_at > '2013-10-29 23:11:37')
    
    AND   (t.hold_until IS NULL OR t.hold_until <= '2013-10-29')
    AND    t.company_id = 173;
    
    AND    a.section = 'shop'
    AND   (a.target_type = 'Company'   AND a.target_id = 173
       OR  a.target_type = 'User'      AND a.target_id = 11654
       OR  a.target_type = 'UserGroup' AND a.target_id IN (126)
       OR                                  d.lft <= 7 AND d.rgt >= 8
        -- a.target_type = 'Department' is redundant
    ) 
    AND   (a.frozen_activation = 'active'
       OR     a.active_on <= '2013-10-29'
         AND (a.inactive_on IS NULL OR a.inactive_on > '2013-10-29')
         AND  a.frozen_activation IS NULL
    )
    
    CREATE TABLE access_rights (
       ...
      ,target_type_id integer NOT NULL REFERENCES target_type(target_type_id)
       ...
    );
    
    CREATE INDEX e_idx ON events (company_id, event_id, hold_until)
    WHERE activity = FALSE;
    
    CREATE INDEX t_idx ON tickets (company_id, event_id, hold_until DESC);
    
    CREATE INDEX a_idx1 ON access_rights (target_type_id, target_id)
    WHERE section = 'shop';
    
    CREATE INDEX a_idx2 ON access_rights
                       (frozen_activation, active_on DESC, inactive_on)
    WHERE section = 'shop';
    
    CREATE INDEX d_idx ON departments (target_type, lft DESC, rgt);