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.94.2) (envelope-from ) id 1uA3TW-009vtb-8g for pgsql-performance@arkaria.postgresql.org; Wed, 30 Apr 2025 09:11:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1uA3TT-00Chg3-RK for pgsql-performance@arkaria.postgresql.org; Wed, 30 Apr 2025 09:11:28 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uA3TT-00Chft-Ax for pgsql-performance@lists.postgresql.org; Wed, 30 Apr 2025 09:11:28 +0000 Received: from mail-yb1-xb34.google.com ([2607:f8b0:4864:20::b34]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1uA3TR-000K4Z-2R for pgsql-performance@lists.postgresql.org; Wed, 30 Apr 2025 09:11:27 +0000 Received: by mail-yb1-xb34.google.com with SMTP id 3f1490d57ef6-e589c258663so6316105276.1 for ; Wed, 30 Apr 2025 02:11:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1746004285; x=1746609085; darn=lists.postgresql.org; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=FM6T/8BM74Jhr5AgZvX4ac2r14cy0miY68mwbjMqs1o=; b=QHAxy98CMFVN2dpz+LpV9lrB5o+t/WucqeJ3XXsvlDsEaAbJmuvBzABuVrMwn8mKRP XyBHLeeacv3eRwTQzRGqCGWrqqc6qw1JXCGLfy+1BAhB8r3p7sCjwnTH11Zv4i2KpAPQ Gy4HirEE0dsIMX9YzgUKpNYKva70o/LEDNETDRpsUKBo63BmGCesK02ye39bu7iiLwDe w1j1Obmu0cmHKsLpIrIRkvNVvN62b8QvxAeaVyx3CVmQgxQUZHQ4MvxJvQJ2tjJgU9vU vxAKZc9f9NlA/eUhETKHlJfDvfnS/4cZIkuAn+KdEoFV+dWpHj/s9v3NdcPLxHwiFHP5 iE+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1746004285; x=1746609085; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=FM6T/8BM74Jhr5AgZvX4ac2r14cy0miY68mwbjMqs1o=; b=uGzuDxcfEYkbnqrmvuq+Rn5sTN6BmBFX544hbcRDZA5avxAzIuDNOq1NzLuXi/dJVU upciPCznxel4VtBZacXZT7TdmbRo7RWbEutDTW4yF6cJRwRnQkqM99jWrnWbp1FEHlf3 VmZ4zFajO0zPUm8zdClVX/+wX7ez1Fm2UQUG1K0Y+MUFez2QRG/yrj0gsMMw5CwhNe1L AEo7TpZu9rBkpit5aNaAmbIJW38MExLtaiOPfmErq7ZwoBVVwf/Lf1QooECG6T969Nvh STavyze2lxL2Ztxeqs4xvN3KSCAQs+LNoKH5ADBoXv1FulJcAVNknJw0sPSX82NI4Dpv xZFA== X-Forwarded-Encrypted: i=1; AJvYcCXwf8avWUbH3Nn8k5fw7xdtoUTrCEl5EmeeC91S6fhMzMZdnyivadI0oKFB47USgH6zoxABdypLIVDTBf8MLkVbzw==@lists.postgresql.org X-Gm-Message-State: AOJu0YwJvs3AL7j9var3ZLIzDqjTWRFxcTDBN0Qx/NliIRqO2V4TcP+U QK+SHTyf+7eU9OtC5tNcsVlCX830OK7/wfTzbz6WI9gy4TRd7EKwvOcjNDMqDlBa6cCb1YwAdLa G9rQgOgWNYOc4kM5kqcgasTWzU/hTBLwh X-Gm-Gg: ASbGncvAYaXrO07iy0R9VIxIosXI4LPDXzsyY/hRI2ELaHFhnBNSFKsubaQZG550U8E /jeX2UxEnWDXt5XZp9hVriEBIMAXb5tbaxfCg2O35M7Q1t1w1uEqu3vO6e7f/+rm/F+/5wFttxh wSfUOQhgDZy3hoxuiUgr/Y X-Google-Smtp-Source: AGHT+IG/ON+yW2ZaUxXG0+q345gVmzhSbIxRSZnRp/Qiud/sgbLluqVqXmrP7lVmih2/0dbwMc9wL3K2doaIvfLw5fQ= X-Received: by 2002:a05:6902:4790:b0:e6d:dfea:8f09 with SMTP id 3f1490d57ef6-e73eb2e6e86mr2914952276.26.1746004284808; Wed, 30 Apr 2025 02:11:24 -0700 (PDT) MIME-Version: 1.0 References: <79f3cb0ac1e221012df38d7baa72463662c37095.camel@cybertec.at> <783872.1745852832@sss.pgh.pa.us> In-Reply-To: <783872.1745852832@sss.pgh.pa.us> From: =?UTF-8?Q?Felipe_L=C3=B3pez_Montes?= Date: Wed, 30 Apr 2025 11:11:14 +0200 X-Gm-Features: ATxdqUGf-pVL_smVs6PCv3jECVR0J9Cc6Ojd0eaoml568SX4Y83OagrnvcL9vus Message-ID: Subject: Re: PostgreSQL Choosing Full Index Over Partial Index To: Tom Lane , pgsql-performance@lists.postgresql.org Content-Type: multipart/alternative; boundary="0000000000002c277b0633fb4d04" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000002c277b0633fb4d04 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Thanks a lot for your response Tom. May I ask how do you debug those functions? Or is it just that you read the code and more or less guess what should be the value for each variable with information coming from querying Postgres tables? Thanks a lot. El lun, 28 abr 2025 a las 17:07, Tom Lane () escribi=C3= =B3: > Laurenz Albe writes: > > On Mon, 2025-04-28 at 15:22 +0200, Felipe L=C3=B3pez Montes wrote: > >> Following the book PostgreSQL Query Optimization (Second Edition), > there is a > >> statement on page 90 talking about Partial Indexes that says that the > planner > >> will use the partial index rather than the full index on the flight > table, > >> however after doing my own tests I have checked that this is not true > and the > >> planner estimates that scanning the full index is cheaper than scannin= g > the > >> partial one and would like to understand why. > > > Which index is bigger (you can use \di+ in "psql")? > > I find that I can reproduce something similar with a very tiny > partial index: the cost estimate for the partial index comes out > higher than for an equivalent query using a non-partial index. > After tracing through it, the blame seems to affix to this > formula in genericcostestimate: > > /* > * Estimate the number of index pages that will be retrieved. > * > * We use the simplistic method of taking a pro-rata fraction of the > total > * number of index pages. In effect, this counts only leaf pages and > not > * any overhead such as index metapage or upper tree levels. > * > * In practice access to upper index levels is often nearly free > because > * those tend to stay in cache under load; moreover, the cost involve= d > is > * highly dependent on index type. We therefore ignore such costs he= re > * and leave it to the caller to add a suitable charge if needed. > */ > if (index->pages > 1 && index->tuples > 1) > numIndexPages =3D ceil(numIndexTuples * index->pages / > index->tuples); > else > numIndexPages =3D 1.0; > > (numIndexTuples is the estimated number of index entries to be > visited.) In the example I'm looking at, the query wants to > retrieve 5 rows and the index holds exactly those 5 rows, so > > (gdb) p numIndexTuples > $38 =3D 5 > (gdb) p index->pages > $39 =3D 2 > (gdb) p index->tuples > $40 =3D 5 > > and numIndexPages comes out to 2, ie we expect to visit the > whole index. But if we're considering a non-partial index, > > (gdb) p numIndexTuples > $44 =3D 5 > (gdb) p index->pages > $45 =3D 17 > (gdb) p index->tuples > $46 =3D 10000 > > and numIndexPages comes out to 1, so we estimate half as much > disk access cost and the partial index looks worse. > > I think what's wrong here is that index->pages is the entire > size of the index including the meta page, but the calculation > is being done (as the comment says) on the assumption that > only leaf pages are involved. If we were to exclude the meta > page from the calculation then we'd conclude numIndexPages =3D 1 > for both indexes. Felipe is considering a slightly larger index > but I bet it's fundamentally the same issue. With indexes having > more than a few hundred entries, the delta due to the meta page > would drop into the noise and we'd eventually prefer the partial > index. But I think the absolute index size only contributes to > our estimate of descentCost (in btcostestimate) so you'd need > fair-sized indexes before that reliably wins out. > > I don't consider this a serious defect: for this size of index > it barely matters which one the planner picks, as evidenced by > the fact that the true execution times are so close. But it could > be something to try to improve. We could trivially discount the > meta page for index types that have one. Discounting intermediate > upper pages would take more calculation (since we don't know a-priori > how many there are) and might not be worth the trouble. > > regards, tom lane > --0000000000002c277b0633fb4d04 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Thanks a lot for your response Tom.

May I ask how d= o you debug those functions?
Or is it just that you read the code and mo= re or less guess what should be the value for each variable with informatio= n coming from querying Postgres tables?

Thanks a lot.

El lun, 28 abr 2025 a las 17:07, Tom Lane (<tgl@sss.pgh.pa.us>) escribi=C3=B3:
Laurenz Albe <laurenz.albe@cybertec.at&= gt; writes:
> On Mon, 2025-04-28 at 15:22 +0200, Felipe L=C3=B3pez Montes wrote:
>> Following the book PostgreSQL Query Optimization (Second Edition),= there is a
>> statement on page 90 talking about Partial Indexes that says that = the planner
>> will use the partial index rather than the full index on the fligh= t table,
>> however after doing my own tests I have checked that this is not t= rue and the
>> planner estimates that scanning the full index is cheaper than sca= nning the
>> partial one and would like to understand why.

> Which index is bigger (you can use \di+ in "psql")?

I find that I can reproduce something similar with a very tiny
partial index: the cost estimate for the partial index comes out
higher than for an equivalent query using a non-partial index.
After tracing through it, the blame seems to affix to this
formula in genericcostestimate:

=C2=A0 =C2=A0 /*
=C2=A0 =C2=A0 =C2=A0* Estimate the number of index pages that will be retri= eved.
=C2=A0 =C2=A0 =C2=A0*
=C2=A0 =C2=A0 =C2=A0* We use the simplistic method of taking a pro-rata fra= ction of the total
=C2=A0 =C2=A0 =C2=A0* number of index pages.=C2=A0 In effect, this counts o= nly leaf pages and not
=C2=A0 =C2=A0 =C2=A0* any overhead such as index metapage or upper tree lev= els.
=C2=A0 =C2=A0 =C2=A0*
=C2=A0 =C2=A0 =C2=A0* In practice access to upper index levels is often nea= rly free because
=C2=A0 =C2=A0 =C2=A0* those tend to stay in cache under load; moreover, the= cost involved is
=C2=A0 =C2=A0 =C2=A0* highly dependent on index type.=C2=A0 We therefore ig= nore such costs here
=C2=A0 =C2=A0 =C2=A0* and leave it to the caller to add a suitable charge i= f needed.
=C2=A0 =C2=A0 =C2=A0*/
=C2=A0 =C2=A0 if (index->pages > 1 && index->tuples > 1= )
=C2=A0 =C2=A0 =C2=A0 =C2=A0 numIndexPages =3D ceil(numIndexTuples * index-&= gt;pages / index->tuples);
=C2=A0 =C2=A0 else
=C2=A0 =C2=A0 =C2=A0 =C2=A0 numIndexPages =3D 1.0;

(numIndexTuples is the estimated number of index entries to be
visited.)=C2=A0 In the example I'm looking at, the query wants to
retrieve 5 rows and the index holds exactly those 5 rows, so

(gdb) p numIndexTuples
$38 =3D 5
(gdb) p index->pages
$39 =3D 2
(gdb) p index->tuples
$40 =3D 5

and numIndexPages comes out to 2, ie we expect to visit the
whole index.=C2=A0 But if we're considering a non-partial index,

(gdb) p numIndexTuples
$44 =3D 5
(gdb) p index->pages
$45 =3D 17
(gdb) p index->tuples
$46 =3D 10000

and numIndexPages comes out to 1, so we estimate half as much
disk access cost and the partial index looks worse.

I think what's wrong here is that index->pages is the entire
size of the index including the meta page, but the calculation
is being done (as the comment says) on the assumption that
only leaf pages are involved.=C2=A0 If we were to exclude the meta
page from the calculation then we'd conclude numIndexPages =3D 1
for both indexes.=C2=A0 Felipe is considering a slightly larger index
but I bet it's fundamentally the same issue.=C2=A0 With indexes having<= br> more than a few hundred entries, the delta due to the meta page
would drop into the noise and we'd eventually prefer the partial
index.=C2=A0 But I think the absolute index size only contributes to
our estimate of descentCost (in btcostestimate) so you'd need
fair-sized indexes before that reliably wins out.

I don't consider this a serious defect: for this size of index
it barely matters which one the planner picks, as evidenced by
the fact that the true execution times are so close.=C2=A0 But it could
be something to try to improve.=C2=A0 We could trivially discount the
meta page for index types that have one.=C2=A0 Discounting intermediate
upper pages would take more calculation (since we don't know a-priori how many there are) and might not be worth the trouble.

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 regards, tom lane
--0000000000002c277b0633fb4d04--