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 1tuBF2-004cAu-OM for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 14:14:56 +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 1tuBF1-00DMKv-Bz for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 14:14:55 +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 1tuBF0-00DMKW-Tu for pgsql-performance@lists.postgresql.org; Mon, 17 Mar 2025 14:14:55 +0000 Received: from mail-il1-x12b.google.com ([2607:f8b0:4864:20::12b]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tuBEy-003Kka-2q for pgsql-performance@postgresql.org; Mon, 17 Mar 2025 14:14:54 +0000 Received: by mail-il1-x12b.google.com with SMTP id e9e14a558f8ab-3d46fddf357so12124825ab.2 for ; Mon, 17 Mar 2025 07:14:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742220892; x=1742825692; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=gjKWdlh7hD6nQzqXUgRAzwXq/p195VEkQyaytJnEzxc=; b=UNIq0Cbd9zkk9dgnBusUxvhw735Mur7PdDA6qYlZ05feYHJ4y556hoWen2aGpQsQyJ Tt0iCrk5LrFKC7/e8z5F8oerqxLvyZ6KCubGWLkSFdufkFTLzvRMXPdQ5CIS2vIjUogc wsGsSKvrOd+lIJw//IYjRp3JSuDl4iwfYBKNL/Vlh3uDQwKhfyRZbwUvatLrpjLwZF6P U6xh1mGnNjl0c1Ez5SDrGc6jfgpOL5svRFoSq7HR1fCm4Sq3OrTJ3oihuYUHeOXYgLgl f6F/YHJj2rZ/rOJfX2gyFMQTEqkKXTDAMPiLYgBq2bgml7CcaLrakbFR9M7FQcsIviN4 IfKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742220892; x=1742825692; h=cc: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=gjKWdlh7hD6nQzqXUgRAzwXq/p195VEkQyaytJnEzxc=; b=BsXMnEY9XEUtD5mmqObQp7YhsHhzSsBAL6/hahWuu8lv+v3Ll1EZvq8FAHS0dTQb87 N2BNnbLLytRj/JjQ/K69+yaAE2esPmtSHPo8GSNj/IEJoIlzyRh6Be5u9nvtfN7UaTCe WGXuNk7fQFv+7cI4LcjfZey3CuoSH2A+CogqC7zqTBXITHHucQtdRHDG/XFbsvsCYerM djLbtja3e/hkUTMSjLk1pY+9Zr7XbzPBaC+YECZLWfljjcvU7vPQ2drjlY7AKvgxovas vJxPfBZ5JAdz+N66jGqVUR7bNq7mi1bRrMyBy45vN0YtMbsGvyEP1SUsGlVFjg5nmlau ct5g== X-Forwarded-Encrypted: i=1; AJvYcCUBQQ7QpRZVnCN7HYB5/0vezyJDpgExFZnYL/LkB+F5IMQQeMECl7ZHm2MZZ55/idXF3FX+O1LVoK3DjTyVMRT3Gw==@postgresql.org X-Gm-Message-State: AOJu0YzHQYt9GnZ4KxxDyP8t0ZY74FjUUfl8eFxEnGy3544mEqtx2AcC 5A0GuOSj/SWlL9SK0S59U79daan+bxF+BlFJOzqD6z60EMNqs2VmXu2+kXsfp0TA8w0QLNtYF1i aQxNKJnaAJVsAie48ZHehl82bPqY= X-Gm-Gg: ASbGnctDHoiC3XlKBZGg/sfUW/cZQgJefGNJOInZHOzrrsTlfx4l6xVHODHla8XxAhN zOMwiXfgAK609OW4jppLZMNrZ79nW/DTmCxC4xAt8eiQMz1Wi8y1gG++FDki5/Vk0cXWkSNmtLf K4map04T4WFhax8Bpk+ncKX/K39U2QZQvYzsyVCtPW7Q/+Uw11+v7EQ+tlZbTN X-Google-Smtp-Source: AGHT+IFFrZeoteU1egs7NpIApyM8+leOpoa0D41103WPA7O/eqszhEe9Yby14xWKOdpmx8Qgt/pgkxi7yztq2b66dmk= X-Received: by 2002:a05:6e02:16cf:b0:3cf:bc71:94f5 with SMTP id e9e14a558f8ab-3d483a8c27cmr152790185ab.22.1742220892322; Mon, 17 Mar 2025 07:14:52 -0700 (PDT) MIME-Version: 1.0 References: <008701db93cd$2eb404d0$8c1c0e70$.ref@ymail.com> <008701db93cd$2eb404d0$8c1c0e70$@ymail.com> <0942a47d7a89f1b181f7e306f8389d9717eb5fe0.camel@cybertec.at> <00a401db9400$86c240f0$9446c2d0$@ymail.com> In-Reply-To: <00a401db9400$86c240f0$9446c2d0$@ymail.com> From: Greg Sabino Mullane Date: Mon, 17 Mar 2025 10:14:14 -0400 X-Gm-Features: AQ5f1Jqv5P64QE5sbhdTxpC97yFQMKvLkqlCYzr47HwOO9tQZoOnEMXQTEIvMhs Message-ID: Subject: Re: Bulk DML performance To: bill.poole@ymail.com Cc: Laurenz Albe , pgsql-performance@postgresql.org Content-Type: multipart/alternative; boundary="00000000000068441306308a6936" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000068441306308a6936 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, Mar 17, 2025 at 4:19=E2=80=AFAM wrote: Can you help me understand why performing 3 million lookups on a b-tree > index with all pages cached in memory takes so long? It's not the lookup, it's writing the 3 million rows (and in this particular upsert case, deleting 3 million, then inserting 3 million) > Well, that is not a great statement. > > Understood, but I was highlighting the performance of deleting 3 million > rows identified by 3 million IDs, as opposed to deleting rows in a given > range of IDs or deleting the whole table. It seems like deleting 3 millio= n > rows identified by 3 million IDs should be faster than updating 3 million > rows (also identified by 3 million IDs). > It should indeed be faster. But keep in mind a delete immediately after that upsert now has twice as many rows to walk through as the upsert did. Also, a subselect like your original query can lead to a large nested loop. Try another variant such as this one: with ids as (select x from generate_series(1, 3_000_000) x) delete from test using ids where id=3Dx; > With the table as it is you won't get better performance if you want the > features that a relational database provides. > > Sorry to hear that. I had hoped there was room to improve this performanc= e. > If pure upsert performance is the goal, remove the unique index and store a timestamp along with your inserted data. Back to pure inserts again! (and a few new downsides). When querying, only use the version of the row with the highest timestamp. Other random ideas: * remove or consolidate columns you don't need, or can store in another table * pre-filter the rows in the app, so you can do a pure-insert (or COPY) of known-to-be-new rows, then upsert the remaining rows * use the smallest data types possible * avoid or minimize toasted values * pack your columns efficiently (e.g. reorder for 8 byte blocks) * put the indexes on a ram-based tablespace * boost your work_mem (for things like giant deletes which build hashes) * revisit unlogged tables and partitioning Cheers, Greg -- Crunchy Data - https://www.crunchydata.com Enterprise Postgres Software Products & Tech Support --00000000000068441306308a6936 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
On Mon, Mar 17, 2025 at 4:19=E2=80=AFAM &= lt;bill.poole@ymail.com> wro= te:

=C2=A0Can you help me understand why performing 3 million lookup= s on a b-tree index with all pages cached in memory takes so long?

It's not the lookup, it's writi= ng the 3 million rows (and in this particular upsert case, deleting 3 milli= on, then inserting 3 million)

> Well, that is not a great statement.

Understood, but I was highlighting the performance of deleting 3 million ro= ws identified by 3 million IDs, as opposed to deleting rows in a given rang= e of IDs or deleting the whole table. It seems like deleting 3 million rows= identified by 3 million IDs should be faster than updating 3 million rows = (also identified by 3 million IDs).

It = should indeed be faster. But keep in mind a delete immediately after that u= psert now has twice as many rows to walk through as the upsert did. Also, a= subselect like your original query can lead to a large nested loop. Try an= other variant such as this one:

with ids as (selec= t x from generate_series(1, 3_000_000) x) delete from test using ids where = id=3Dx;

> With the table as it is you won't get better performance if you= want the features that a relational database provides.

Sorry to hear that. I had hoped there was room to improve this performance.=

If pure upsert performance is the goal= , remove the unique index and store a timestamp along with your inserted da= ta. Back to pure inserts again! (and a few new downsides). When querying, o= nly use the version of the row with the highest timestamp.

Other random ideas:

* remove or consolida= te columns you don't need, or can store in another table
* pr= e-filter the rows in the app, so you can do a pure-insert (or COPY) of know= n-to-be-new rows, then upsert the remaining rows
* use the smalle= st data types possible
* avoid or minimize toasted values
* pack your columns efficiently (e.g. reorder for 8 byte blocks)
* put the indexes on a ram-based tablespace
* boost your work_= mem (for things like giant deletes which build hashes)
* revisit = unlogged tables and partitioning

Cheers,
Greg

--
= Enterprise Postgres Software Products & Tech Support

--00000000000068441306308a6936--