Re: hash join vs nested loop join

From: Huan Ruan <huan(dot)ruan(dot)it(at)gmail(dot)com>
To: Kevin Grittner <kgrittn(at)mail(dot)com>
Cc: Huan Ruan <leohuanruan(at)gmail(dot)com>, pgsql-performance(at)postgresql(dot)org
Subject: Re: hash join vs nested loop join
Date: 2012-12-13 01:10:24
Message-ID: CAD1stZv-CsoBSpH3Oimu-OrhXPAf6ouA-pYLjbE6obgNpVNX8A@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-performance

Hi Kevin

On 13 December 2012 10:47, Kevin Grittner <kgrittn(at)mail(dot)com> wrote:

> Huan Ruan wrote:
>
> > is a lot slower than a nested loop join.
>
> Giving actual numbers is more useful than terms like "a lot". Even
> better is to provide the output of EXPLAIN ANALYZZE rather than
> just EXPLAIN. This shows estimates against actual numbers, and give
> timings. For more suggestions see this page:
>
> http://wiki.postgresql.org/wiki/SlowQueryQuestions

You are right. I realised my information wasn't accurate. Was a bit slack
and canceled the slower one. The full outputs are

Hash 1st run

"QUERY PLAN"
"Hash Join (cost=1681.87..6414169.04 rows=48261 width=171) (actual
time=2182.450..88158.645 rows=48257 loops=1)"
" Hash Cond: (bigtable.invtranref = smalltable.invtranref)"
" Buffers: shared hit=3950 read=3046219"
" -> Seq Scan on invtran bigtable (cost=0.00..4730787.28 rows=168121728
width=108) (actual time=0.051..32581.052 rows=168121657 loops=1)"
" Buffers: shared hit=3351 read=3046219"
" -> Hash (cost=1078.61..1078.61 rows=48261 width=63) (actual
time=21.751..21.751 rows=48261 loops=1)"
" Buckets: 8192 Batches: 1 Memory Usage: 4808kB"
" Buffers: shared hit=596"
" -> Seq Scan on im_match_table smalltable (cost=0.00..1078.61
rows=48261 width=63) (actual time=0.007..8.299 rows=48261 loops=1)"
" Buffers: shared hit=596"
"Total runtime: 88162.417 ms"

Hash 2nd run (after disconnect and reconnect)

"QUERY PLAN"
"Hash Join (cost=1681.87..6414169.04 rows=48261 width=171) (actual
time=2280.390..87934.540 rows=48257 loops=1)"
" Hash Cond: (bigtable.invtranref = smalltable.invtranref)"
" Buffers: shared hit=3982 read=3046187"
" -> Seq Scan on invtran bigtable (cost=0.00..4730787.28 rows=168121728
width=108) (actual time=0.052..32747.805 rows=168121657 loops=1)"
" Buffers: shared hit=3383 read=3046187"
" -> Hash (cost=1078.61..1078.61 rows=48261 width=63) (actual
time=62.161..62.161 rows=48261 loops=1)"
" Buckets: 8192 Batches: 1 Memory Usage: 4808kB"
" Buffers: shared hit=596"
" -> Seq Scan on im_match_table smalltable (cost=0.00..1078.61
rows=48261 width=63) (actual time=0.006..8.209 rows=48261 loops=1)"
" Buffers: shared hit=596"
"Total runtime: 87938.584 ms"

NL 1st run

"QUERY PLAN"
"Nested Loop (cost=0.00..6451637.88 rows=48261 width=171) (actual
time=0.056..551.438 rows=48257 loops=1)"
" Buffers: shared hit=242267"
" -> Seq Scan on im_match_table smalltable (cost=0.00..1078.61
rows=48261 width=63) (actual time=0.009..7.353 rows=48261 loops=1)"
" Buffers: shared hit=596"
" -> Index Scan using pk_invtran on invtran bigtable (cost=0.00..133.65
rows=1 width=108) (actual time=0.010..0.010 rows=1 loops=48261)"
" Index Cond: (invtranref = smalltable.invtranref)"
" Buffers: shared hit=241671"
"Total runtime: 555.336 ms"

NL 2nd run (after disconnect and reconnect)

"QUERY PLAN"
"Nested Loop (cost=0.00..6451637.88 rows=48261 width=171) (actual
time=0.058..554.215 rows=48257 loops=1)"
" Buffers: shared hit=242267"
" -> Seq Scan on im_match_table smalltable (cost=0.00..1078.61
rows=48261 width=63) (actual time=0.009..7.416 rows=48261 loops=1)"
" Buffers: shared hit=596"
" -> Index Scan using pk_invtran on invtran bigtable (cost=0.00..133.65
rows=1 width=108) (actual time=0.010..0.010 rows=1 loops=48261)"
" Index Cond: (invtranref = smalltable.invtranref)"
" Buffers: shared hit=241671"
"Total runtime: 558.095 ms"

>
>
> > I don't understand why the optimiser chooses the hash join in
> > favor of the nested loop. What can I do to get the optimiser to
> > make a better decision (nested loop in this case)? I have run
> > analyze on both tables.
>
> > Config changes are
> >
> > - shared_buffers = 6GB
> > - effective_cache_size = 18GB
> > - work_mem = 10MB
> > - maintenance_work_mem = 3GB
>
> As already suggested, there was a change made in 9.2 which may have
> over-penalized nested loops using index scans. This may be fixed in
> the next minor release.
>

Will keep this in mind.

>
> Also, as already suggested, you may want to reduce random_page
> cost, to bring it in line with the actual cost relative to
> seq_page_cost based on your cache hit ratio.
>
> Additionally, I just routinely set cpu_tuple_cost higher than the
> default of 0.01. I find that 0.03 to 0.05 better models the actual
> relative cost of processing a tuple.
>

I originally reduced random_page_cost to 2 to achieve the nested loop join.
Now I set cpu_tuple_cost to 0.05 and reset random_page_cost back to 4, I
can also achieve a nested loop join.

I'm still new in Postgres, but I'm worried about random_page_cost being 2
is too low, so maybe increasing cpu_tuple_cost is a better choice. All
these tuning probably also depends on the above mentioned possible fix as
well. Can you see any obvious issues with the other memory settings I
changed?

Thanks for your help.

Cheers
Huan

> -Kevin
>

In response to

Browse pgsql-performance by date

  From Date Subject
Next Message Bruce Momjian 2012-12-13 02:48:37 Re: [PERFORM] encouraging index-only scans
Previous Message Huan Ruan 2012-12-13 00:56:21 Re: hash join vs nested loop join