Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vdgnV-003Vj8-0H for pgsql-performance@arkaria.postgresql.org; Thu, 08 Jan 2026 03:34:54 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vdgnU-00HCQp-05 for pgsql-performance@arkaria.postgresql.org; Thu, 08 Jan 2026 03:34:52 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vdgnT-00HCQh-27 for pgsql-performance@lists.postgresql.org; Thu, 08 Jan 2026 03:34:52 +0000 Received: from mail-pl1-x632.google.com ([2607:f8b0:4864:20::632]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vdgnS-005CbP-0D for pgsql-performance@lists.postgresql.org; Thu, 08 Jan 2026 03:34:52 +0000 Received: by mail-pl1-x632.google.com with SMTP id d9443c01a7336-2a2ea96930cso21782485ad.2 for ; Wed, 07 Jan 2026 19:34:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1767843287; x=1768448087; darn=lists.postgresql.org; h=content-transfer-encoding:in-reply-to:content-language:references :to:from:subject:user-agent:mime-version:date:message-id:from:to:cc :subject:date:message-id:reply-to; bh=6UrIeVZK/46UIzZVdqvoIWVViAkJLs46MK0moXw/liY=; b=QhfKGaEZJk/j1OAvzLzW6D6eHkyw9DWixUEYZzkl1LwS8wi6fOgaKK5yOYxuvI+z2N xMuICQy6eS9xghSbBxzctge7TgHcSpoSa0/BsKkpqvEfS52nIUNRX0o6tKlDlIOQf9Wm 70fTNvCgytaYWv3OnDka2s94taq59GSN7BYawN8lYnK29mMLymLMrCYMjVtp20SeZlct A4U5O6jm4DXRNnv7bIgHQNCn/yv1F5BOBmVFh4ISSq67i7jY7C36RPhhQW81SFeGs2ql 3lIvwg8WarJly2dsYrJWsfj5sraDlPRYbLuifLU7Dt2KNIjpDwbSNI1I4KszPHZKSxE4 uNCA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767843287; x=1768448087; h=content-transfer-encoding:in-reply-to:content-language:references :to:from:subject:user-agent:mime-version:date:message-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=6UrIeVZK/46UIzZVdqvoIWVViAkJLs46MK0moXw/liY=; b=kLidre8GueP20bCKjrXimFqoioDpDNLWii7K621ytVyD5wZMAftcxSXxVSJViBRoOZ JJGi2viRvN8nhwjp9HEj8SpNGDTUsMDES8FeHyxDh8Yub4aD+z36tbIxkMxzlx9kognN lRjYOiqBW0wqmKp68f6AKaP713+pkkZbx4Ow4E/WmoG8XwizTgtbDB56WbVXNicvbSOl /uMjLRU2VCljg4qhRoTb6CuuTEU0dxyNi3popkE75ejiKU6zlC4h3MuiXCJ0tXeyhR+M GTIwzgAtPRsoo7lwIbdxGary+wuCPF66Lxtoyku+tUAym/9SY4IxQHqKfaBRkf/k099B Ug5w== X-Gm-Message-State: AOJu0Yw7lQkjvXs9OOKS0jmHARdHPSVZf/RYwlQFe/ilCWmshLzcr4nk U3e/Vtjpq4KFzSp2tEm/2NrF/01qPAmHQGqCMkP6noZYGfnFaEGZJn2GxvfGNA== X-Gm-Gg: AY/fxX4qwPqU8jcmNnBticwK9qfu036t6wqvhI9p2UJdJIxHdA0yl71x6Mh2wTo9OlU li2SSGG/uevtej4GzKC5Oxyo2g13pkcUbGPFq/b/lhtcPt9n4N3D2N4k5vFgG+uhtxRwFzrWVWT N6n+SAo98Ir3VUliMx/ClavFFG1LcIC9b7HKM24QKDVajL76HjsZ0cTj1VjtBuoui+3jV6V55rM iixYkxRjjqkY553q8GW5L1rGiFsW4WzGNYoRc/RLmmsbk6bCB9a4sAPfsf+Zfcq5AHLGIgiLI+3 lBBYoKFhXcBKiaFq44854XpjwvCr5TdB+MIhfl7+TCXfbJqlisIMUThD9fYIWFpX4ITu7qONCyn 2IPDzfOlkV4Dvc0wL2c4kqpdne02sDxhfYpDtsBvjFhAa/478iX42X7U/oL4OlQed7P5IQvfbYu r1TNmeGTCUoxzw3wU14wwg+HoHozZEu8O3UIw5ZSXxe6tEWx6BBdwzvAGS6F8pk4AftA== X-Google-Smtp-Source: AGHT+IG8Jmp9XgsajjXjE9FuR2nYH+7jVQ5TNeHcXlYBXeJZuNlM8xjawA1I36QKjYn2vHfHPNS2rg== X-Received: by 2002:a17:903:1a84:b0:2a0:be68:9456 with SMTP id d9443c01a7336-2a3ee4fed1emr37116835ad.46.1767843287180; Wed, 07 Jan 2026 19:34:47 -0800 (PST) Received: from ?IPV6:2404:4400:414b:1700:b092:c6d0:7613:982b? ([2404:4400:414b:1700:b092:c6d0:7613:982b]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a3e3cc78c0sm63509525ad.69.2026.01.07.19.34.45 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 07 Jan 2026 19:34:46 -0800 (PST) Message-ID: Date: Thu, 8 Jan 2026 16:34:42 +1300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Unexpected planner choice in simple JOIN From: Mark Kirkwood To: pgsql-performance@lists.postgresql.org References: <71afdb09-0583-4af2-a3b3-50da4f987b9b@gmail.com> Content-Language: en-US In-Reply-To: <71afdb09-0583-4af2-a3b3-50da4f987b9b@gmail.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This does seem to be related to parallel planning: test0=# SET max_parallel_workers_per_gather=0; SET Time: 0.205 ms test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val FROM tab0 AS t0 JOIN tab1 AS t1 ON (t0.id0 = t1.id0) WHERE t0.id0 < 5;  QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------  Nested Loop  (cost=97.91..64508.51 rows=5000 width=98) (actual time=0.217..1.193 rows=3500.00 loops=1)    Buffers: shared hit=113 read=1    ->  Index Only Scan using tab0_pkey on tab0 t0 (cost=0.29..4.38 rows=5 width=4) (actual time=0.007..0.010 rows=5.00 loops=1)          Index Cond: (id0 < 5)          Heap Fetches: 0          Index Searches: 1          Buffers: shared hit=3    ->  Bitmap Heap Scan on tab1 t1  (cost=97.61..12867.78 rows=3305 width=98) (actual time=0.069..0.187 rows=700.00 loops=5)          Recheck Cond: (t0.id0 = id0)          Heap Blocks: exact=97          Buffers: shared hit=110 read=1          ->  Bitmap Index Scan on tab1_id0_hash (cost=0.00..96.79 rows=3305 width=0) (actual time=0.061..0.061 rows=700.00 loops=5)                Index Cond: (id0 = t0.id0)                Index Searches: 5                Buffers: shared hit=13 read=1  Planning:    Buffers: shared hit=216 read=6  Planning Time: 1.618 ms  Execution Time: 1.385 ms (19 rows) Not clear to me why removing 2 workers makes the seqscan more attractive when that part of the plan is 100x more expensive than an index scan.... On 08/01/2026 14:35, Mark Kirkwood wrote: > I have a 2 table parent child setup (tab0 -< tab1) with a fairly small > (100 K rows) parent and big child (100 M rows).. Exact setup is > included below. > > If I do a simple range scan on small part of the pk of tab0  the > planner chooses an index scan (pretty much as expected): > > test0=# EXPLAIN ANALYZE SELECT t0.id0 FROM tab0 AS t0 WHERE t0.id0 < 5; >                                                         QUERY PLAN > --------------------------------------------------------------------------------------------------------------------------- > >  Index Only Scan using tab0_pkey on tab0 t0  (cost=0.29..4.38 rows=5 > width=4) (actual time=0.006..0.007 rows=5.00 loops=1) >    Index Cond: (id0 < 5) >    Heap Fetches: 0 >    Index Searches: 1 >    Buffers: shared hit=3 >  Planning: >    Buffers: shared hit=65 >  Planning Time: 0.383 ms >  Execution Time: 0.038 ms > (9 rows) > > However joining it to tab1 changes this to a parallel seq scan: > > test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val FROM tab0 AS t0 JOIN > tab1 AS t1 ON (t0.id0 = t1.id0) WHERE t0.id0 < 5; > QUERY PLAN > ------------------------------------------------------------------------------------------------------------------------------------------- > >  Gather  (cost=1097.61..42577.77 rows=5000 width=98) (actual > time=0.324..7.486 rows=3500.00 loops=1) >    Workers Planned: 1 >    Workers Launched: 1 >    Buffers: shared hit=1751 >    ->  Nested Loop  (cost=97.61..41077.77 rows=2941 width=98) (actual > time=1.273..3.723 rows=1750.00 loops=2) >          Buffers: shared hit=1751 >          ->  Parallel Seq Scan on tab0 t0 (cost=0.00..2375.29 rows=3 > width=4) (actual time=1.247..3.232 rows=2.50 loops=2) >                Filter: (id0 < 5) >                Rows Removed by Filter: 49998 >                Buffers: shared hit=1640 >          ->  Bitmap Heap Scan on tab1 t1 (cost=97.61..12867.78 > rows=3305 width=98) (actual time=0.031..0.148 rows=700.00 loops=5) >                Recheck Cond: (t0.id0 = id0) >                Heap Blocks: exact=97 >                Buffers: shared hit=111 >                ->  Bitmap Index Scan on tab1_id0_hash > (cost=0.00..96.79 rows=3305 width=0) (actual time=0.024..0.024 > rows=700.00 loops=5) >                      Index Cond: (id0 = t0.id0) >                      Index Searches: 5 >                      Buffers: shared hit=14 >  Planning: >    Buffers: shared hit=160 >  Planning Time: 0.506 ms >  Execution Time: 7.658 ms > (22 rows) > > However, disabling seq scan gets back to the index scan again, and > what looks to be a lower cost overall plan: > > test0=# SET enable_seqscan=off; > SET > Time: 0.133 ms > test0=# EXPLAIN ANALYZE SELECT t0.id0, t1.val FROM tab0 AS t0 JOIN > tab1 AS t1 ON (t0.id0 = t1.id0) WHERE t0.id0 < 5; >  QUERY PLAN > ------------------------------------------------------------------------------------------------------------------------------------- > >  Nested Loop  (cost=97.91..64508.51 rows=5000 width=98) (actual > time=0.044..1.093 rows=3500.00 loops=1) >    Buffers: shared hit=113 >    ->  Index Only Scan using tab0_pkey on tab0 t0 (cost=0.29..4.38 > rows=5 width=4) (actual time=0.004..0.007 rows=5.00 loops=1) >          Index Cond: (id0 < 5) >          Heap Fetches: 0 >          Index Searches: 1 >          Buffers: shared hit=3 >    ->  Bitmap Heap Scan on tab1 t1  (cost=97.61..12867.78 rows=3305 > width=98) (actual time=0.029..0.153 rows=700.00 loops=5) >          Recheck Cond: (t0.id0 = id0) >          Heap Blocks: exact=97 >          Buffers: shared hit=110 >          ->  Bitmap Index Scan on tab1_id0_hash (cost=0.00..96.79 > rows=3305 width=0) (actual time=0.022..0.022 rows=700.00 loops=5) >                Index Cond: (id0 = t0.id0) >                Index Searches: 5 >                Buffers: shared hit=13 >  Planning: >    Buffers: shared hit=9 >  Planning Time: 0.187 ms >  Execution Time: 1.262 ms > (19 rows) > >