1

我有以下选择,在大型数据库上,它很慢:

SELECT eventid 
FROM track_event 
WHERE inboundid IN (SELECT messageid FROM temp_message);

temp_message 表很小(100 行),只有一列(messageid varchar),列上有一个 btree 索引。

track_event 表有 19 列和近 1300 万行。此查询中使用的列(eventid bigint 和 inboundid varchar)都具有 btree 索引。

我无法从大数据库复制/粘贴解释计划,但这是来自具有相同架构的较小数据库(track_event 中只有 348 行)的计划:

 explain analyse SELECT eventid FROM track_event WHERE inboundid IN (SELECT messageid FROM temp_message);
                                                           QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop Semi Join  (cost=0.00..60.78 rows=348 width=8) (actual time=0.033..3.186 rows=348 loops=1)
->  Seq Scan on track_event  (cost=0.00..8.48 rows=348 width=25) (actual time=0.012..0.860 rows=348 loops=1)
->  Index Scan using temp_message_idx on temp_message  (cost=0.00..0.48 rows=7 width=32) (actual time=0.005..0.005 rows=1 loops=348)
      Index Cond: ((temp_message.messageid)::text = (track_event.inboundid)::text)
Total runtime: 3.349 ms
(5 rows)

在大型数据库上,此查询大约需要 450 秒。任何人都可以看到任何明显的加速吗?我注意到解释计划中的 track_event 上有一个 Seq Scan - 我想我想失去它,但无法确定我可以使用哪个索引。

编辑

Postgres 9.0

track_event 表是一个非常大的复杂模式的一部分,我无法对其进行重大更改。这是信息,包括我刚刚添加的新索引:

            Table "public.track_event"
       Column       |           Type           | Modifiers 
--------------------+--------------------------+-----------
 eventid            | bigint                   | not null
 messageid          | character varying        | not null
 inboundid          | character varying        | not null
 newid              | character varying        | 
 parenteventid      | bigint                   | 
 pmmuser            | bigint                   | 
 eventdate          | timestamp with time zone | not null
 routeid            | integer                  | 
 eventtypeid        | integer                  | not null
 adminid            | integer                  | 
 hostid             | integer                  | 
 reason             | character varying        | 
 expiry             | integer                  | 
 encryptionendpoint | character varying        | 
 encryptionerror    | character varying        | 
 encryptiontype     | character varying        | 
 tlsused            | integer                  | 
 tlsrequested       | integer                  | 
 encryptionportal   | integer                  | 
Indexes:
    "track_event_pk" PRIMARY KEY, btree (eventid)
    "foo" btree (inboundid, eventid)
    "px_event_inboundid" btree (inboundid)
    "track_event_idx" btree (messageid, eventtypeid)
Foreign-key constraints:
    "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid)
    "track_event_pmi_route_fk" FOREIGN KEY (routeid) REFERENCES pmi_route(routeid)
    "track_event_pmim_smtpaddress_fk" FOREIGN KEY (pmmuser) REFERENCES pmim_smtpaddress(smtpaddressid)
    "track_event_track_adminuser_fk" FOREIGN KEY (adminid) REFERENCES track_adminuser(adminid)
    "track_event_track_encryptionportal_fk" FOREIGN KEY (encryptionportal) REFERENCES track_encryptionportal(id)
    "track_event_track_eventtype_fk" FOREIGN KEY (eventtypeid) REFERENCES track_eventtype(eventtypeid)
    "track_event_track_host_fk" FOREIGN KEY (hostid) REFERENCES track_host(hostid)
    "track_event_track_message_fk" FOREIGN KEY (inboundid) REFERENCES track_message(messageid)
Referenced by:
    TABLE "track_event" CONSTRAINT "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid)
    TABLE "track_eventaddress" CONSTRAINT "track_eventaddress_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
    TABLE "track_eventattachment" CONSTRAINT "track_eventattachment_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
    TABLE "track_eventrule" CONSTRAINT "track_eventrule_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
    TABLE "track_eventthreatdescription" CONSTRAINT "track_eventthreatdescription_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
    TABLE "track_eventthreattype" CONSTRAINT "track_eventthreattype_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
    TABLE "track_quarantineevent" CONSTRAINT "track_quarantineevent_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid)
4

5 回答 5

1

它取决于数据库中的记录数(特定表)。如果有小型数据库,并且数据库的性质是静态的并且非常罕见的记录增加,那么连接使用率比INwhere更好, 因为连接后表现为一个表,并且在小表中连接需要几微秒。因为 Where 和IN有特定的执行时间,如果数据库很大,那么在大型数据库中保持更好,如果数据库很小,那么在使用 In Statment Query的情况下会花费更多时间

对于小型数据库

SELECT t1.column_name,t1.column_name,t2.column_name,t2.column_name FROM tbl1 t1
INNER JOIN tbl2 t2
ON tbl1.column_name=tbl2.column_name;

对于大型数据库

SELECT column_name,column_name FROM tbl1 t1 WHERE tbl1.column_name IN (SELECT column_name FROM tbl2 t2 where t2.column_name = t1.column_name);   
于 2013-08-29T11:38:43.470 回答
1

试试这个技术:

SELECT eventid FROM track_event te WHERE inboundid IN (SELECT messageid FROM temp_message where messageid = te.inboundid);   

或者您也可以使用以下代码以获得更好的结果

Select eventid From track_event te Where (Select count(*) from temp_message where messageid = te.inboundid) > 0
于 2013-08-29T10:57:14.047 回答
1

您的查询正在对较大的表进行全表扫描。一个明显的加速是在event_track(inboundid, eventid). Postgres 应该能够按照所写的方式使用查询中的索引。您可以将查询重写为:

SELECT te.eventid
FROM track_event te join
     temp_message tm
     on te.inboundid  = tm.messageid;

这绝对应该使用索引。select distinct te.eventid(如果表中有重复项,您可能需要temp_message。)

编辑:

最后一次尝试重写是反转查询:

select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid
from temp_message tm;

这应该强制使用索引。如果有不匹配,您可能需要:

select eventid
from (select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid
      from temp_message tm
     ) tm
where eventid is not null;
于 2013-08-29T10:51:56.017 回答
0

问题可能是(错误)调整的结果。

我可以通过禁用 hashjoin 和排序来重现该行为。work_mem对于足够大的查询,一旦达到,可能会在更大的输入数据集上调用相同的行为。

以下设置在此处重现 OP 的查询计划(PG9.3beta)

-- SET work_mem = 64 ;
-- SET enable_material = 0; -- only needed for pg9.3 ?
SET enable_hashjoin = 0 ;
SET enable_sort = 0 ;

effective_cache_size并且random_page_cost似乎没有影响(还)。

顺便说一句:问题说:(only 348 rows in track_event),这意味着 seqscan 将优于其他任何东西。该计划需要 348 行:使用索引无法获得选择性。(需要主表从主表中获取event_id值)

所以,我的猜测是,问题(在生产数据库上)可能可以通过将 work_mem 和 Effective_cache_size 设置为可用值来解决。这当然只有在索引是选择性的情况下才有效;对于检索 100% 行的查询,索引几乎没有用处。

于 2013-08-29T16:03:31.047 回答
0

可能是 NULL 没有被索引,所以如果 track_event.inboundid 和 temp_message.messageid 可以为空,那么它不能制定不涉及扫描的访问计划。

即使有索引,如果没有选择性,也不能保证使用它是最好的计划。

track_event 表/索引上的统计信息是什么?

于 2013-08-29T12:47:22.740 回答