Received: from localhost (maia-1.hub.org [200.46.204.191]) by postgresql.org (Postfix) with ESMTP id 0B8C19FB61A for ; Mon, 11 Jun 2007 22:10:33 -0300 (ADT) Received: from postgresql.org ([200.46.204.71]) by localhost (mx1.hub.org [200.46.204.191]) (amavisd-maia, port 10024) with ESMTP id 15426-07 for ; Mon, 11 Jun 2007 22:10:25 -0300 (ADT) X-Greylist: from auto-whitelisted by SQLgrey-1.7.5 Received: from mexforwardwc.lss.emc.com (unknown [137.69.117.200]) by postgresql.org (Postfix) with ESMTP id 3A4159FA25B for ; Mon, 11 Jun 2007 22:10:24 -0300 (ADT) Received: from mailhubwc.lss.emc.com (buto.lss.emc.com [137.69.224.85]) by mexforwardwc.lss.emc.com (Switch-3.2.5/Switch-3.1.7) with ESMTP id l5C19ol4008900; Mon, 11 Jun 2007 18:09:50 -0700 (PDT) Received: from avaexch01.avamar.com (avaexch01.avamar.com [128.222.199.14]) by mailhubwc.lss.emc.com (Switch-3.2.5/Switch-3.1.7) with ESMTP id l5C19kYg004638; Mon, 11 Jun 2007 18:09:47 -0700 (PDT) Received: from 128.222.235.22 ([128.222.235.22]) by avaexch01.avamar.com ([128.222.199.14]) with Microsoft Exchange Server HTTP-DAV ; Tue, 12 Jun 2007 01:09:45 +0000 Received: from nickel.avamar.com by avaexch01; 11 Jun 2007 18:09:46 -0700 Subject: Re: Best way to delete unreferenced rows? From: Ed Tyrrill To: Craig James Cc: "Tyrrill, Ed" , pgsql-performance@postgresql.org In-Reply-To: <466D6DDE.7030006@emolecules.com> References: <46688BC5.3020006@emolecules.com> <466D6DDE.7030006@emolecules.com> Content-Type: text/plain Content-Transfer-Encoding: 7bit Date: Mon, 11 Jun 2007 18:09:46 -0700 Message-Id: <1181610586.27394.7.camel@nickel.avamar.com> Mime-Version: 1.0 X-Mailer: Evolution 2.0.2 (2.0.2-27.rhel4.6) X-PMX-Version: 4.7.1.128075, Antispam-Engine: 2.5.1.298604, Antispam-Data: 2007.6.11.172934 X-PerlMx-Spam: Gauge=, SPAM=1%, Reasons='EMC_FROM_0+ -3, __C230066_P5 0, __CT 0, __CTE 0, __CT_TEXT_PLAIN 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __MIME_TEXT_ONLY 0, __MIME_VERSION 0, __SANE_MSGID 0' X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=0.896 tagged_above=0 required=5 tests=BAYES_20=-0.74, FORGED_RCVD_HELO=0.135, RCVD_NUMERIC_HELO=1.5, UNPARSEABLE_RELAY=0.001 X-Spam-Level: X-Archive-Number: 200706/266 X-Sequence-Number: 25299 Craig James wrote: > Tyrrill, Ed wrote: > > QUERY PLAN > > > > > ------------------------------------------------------------------------ > > > ------------------------------------------------------------------------ > > ------------------- > > Merge Left Join (cost=38725295.93..42505394.70 rows=13799645 > width=8) > > (actual time=6503583.342..8220629.311 rows=93524 loops=1) > > Merge Cond: ("outer".record_id = "inner".record_id) > > Filter: ("inner".record_id IS NULL) > > -> Index Scan using backupobjects_pkey on backupobjects > > (cost=0.00..521525.10 rows=13799645 width=8) (actual > > time=15.955..357813.621 rows=13799645 loops=1) > > -> Sort (cost=38725295.93..39262641.69 rows=214938304 width=8) > > (actual time=6503265.293..7713657.750 rows=214938308 loops=1) > > Sort Key: backup_location.record_id > > -> Seq Scan on backup_location (cost=0.00..3311212.04 > > rows=214938304 width=8) (actual time=11.175..1881179.825 > rows=214938308 > > loops=1) > > Total runtime: 8229178.269 ms > > (8 rows) > > > > I ran vacuum analyze after the last time any inserts, deletes, or > > updates were done, and before I ran the query above. I've attached > my > > postgresql.conf. The machine has 4 GB of RAM. > > I thought maybe someone with more expertise than me might answer this, > but since they haven't I'll just make a comment. It looks to me like > the sort of 214 million rows is what's killing you. I suppose you > could try to increase the sort memory, but that's a lot of memory. It > seems to me an index merge of a relation this large would be faster, > but that's a topic for the experts. > > On a theoretical level, the problem is that it's sorting the largest > table. Perhaps you could re-cast the query so that it only has to > sort the smaller table, something like > > select a.id from a where a.id not in (select distinct b.id from b) > > where "b" is the smaller table. There's still no guarantee that it > won't do a sort on "a", though. In fact one of the clever things > about Postgres is that it can convert a query like the one above into > a regular join, unless you do something like "select ... offset 0" > which blocks the optimizer from doing the rearrangement. > > But I think the first approach is to try to tune for a better plan > using your original query. > > Craig Thanks for the input Craig. I actually started out with a query similar to what you suggest, but the performance was days to complete back when the larger table, backup_location, was still under 100 million rows. The current query is the best performance to date. I have been playing around with work_mem, and doubling it to 128MB did result in some improvement, but doubleing it again to 256MB showed no further gain. Here is the explain analyze with work_mem increased to 128MB: mdsdb=# explain analyze select backupobjects.record_id from backupobjects left outer join backup_location using (record_id) where backup_location.record_id is null; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Merge Left Join (cost=36876242.28..40658535.53 rows=13712990 width=8) (actual time=5795768.950..5795768.950 rows=0 loops=1) Merge Cond: ("outer".record_id = "inner".record_id) Filter: ("inner".record_id IS NULL) -> Index Scan using backupobjects_pkey on backupobjects (cost=0.00..520571.89 rows=13712990 width=8) (actual time=2.490..201516.228 rows=13706121 loops=1) -> Sort (cost=36876242.28..37414148.76 rows=215162592 width=8) (actual time=4904205.255..5440137.309 rows=215162559 loops=1) Sort Key: backup_location.record_id -> Seq Scan on backup_location (cost=0.00..3314666.92 rows=215162592 width=8) (actual time=4.186..1262641.774 rows=215162559 loops=1) Total runtime: 5796322.535 ms