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 1tuEXQ-005kwf-AH for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 17:46:08 +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 1tuEXN-00GfPW-Ve for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 17:46:05 +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 1tuEXN-00GfO0-Gu for pgsql-performance@lists.postgresql.org; Mon, 17 Mar 2025 17:46:05 +0000 Received: from mail-pl1-x62e.google.com ([2607:f8b0:4864:20::62e]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tuEXK-003MSm-31 for pgsql-performance@postgresql.org; Mon, 17 Mar 2025 17:46:04 +0000 Received: by mail-pl1-x62e.google.com with SMTP id d9443c01a7336-223fb0f619dso78676205ad.1 for ; Mon, 17 Mar 2025 10:46:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1742233562; x=1742838362; darn=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=pvGkKASxkpVeXzJ2kX38E5uz3k22+Q8yICc5QuHyBBY=; b=j6TW3p0YYeSw2sISDMoZZBVFztNIJj+LmijSddLywZYWFlozJvcyoxdJjagJ2ecD8q yi0OQ9svyA8RENNqLw9GFzZsSn6UAFhs1tmkd3hhwB6TxfiUf/sl99t0ThFW7UzYdMZq JeKff9U0m1AG/pgBkGZun4Hx1TWQNc53aRFNCTietITCjHHeUbxdqNjXtCW8qzdd8fSR lMB/+2uLz/dcs41vHxTykM1qdy3dposWxexcz9pqu0nI+iaxp/uy0X9bj7sA63VK3/18 vhOX8AH8pNLyJDzQuK0soVhJaFdkKcA5Z452B/VOuP/S4voPYlQ7yi8gpvN7XG3xj4aO ZIaA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1742233562; x=1742838362; 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=pvGkKASxkpVeXzJ2kX38E5uz3k22+Q8yICc5QuHyBBY=; b=Pq6eJloGJHyiQZEWY2keR23XoAn/S//GqAjT5KTqtV6xWgV8XbAxV5py8SuphjtzCS alblXMXokolfGiwNENmIr17BJOWAA1oFPk4U6sIz40RSWzABjZkMrUmOOWnRpTtSqQzT +QDWFdj6mJqpjNBmSnb3wpDTh1l/7WnaRMEX+8hRZ4Jvd7Avb22AZfjGKevQvvnHc+l6 hp06huVC1ofNSSaA6etM6A5aOmkfeG1pZq+kv+12eLEyrfxXOwCAUqLpvXKWVg7gMzYc Ea7o2B3AU7eXk9Z8kDToLxxbo7rAzFfCIsCiUO258wJOMPlVduUupcIe0x26/RH++NFo KIHQ== X-Forwarded-Encrypted: i=1; AJvYcCUHxPDHnFz7gG1CwaMCCR1XuU/FjGucnh/JkPlT7olxNNnkb0O+pyvLjZL4YXzLpEa2eKsHjCuSIdjUFg6+mXL/ew==@postgresql.org X-Gm-Message-State: AOJu0YzHDka7Lwv/Kbp9T8HWLBvacqNKJyvo1zu4iXvN1CG9RGzBjmWj KLfsp9rG+3UQMGeGPslI6RebgdQVRXESB+8d3OvwKhhi0R6LaKbwVcz54iTpEDDQmzT7cbqGDlD PITWN+dKN/mBUnLW7BFHsz5HxZ5xnig== X-Gm-Gg: ASbGncsT2pVRkDSZqOS03z1TvNy0j4DUCCe8ID7ndwpoMCRoOe5znWknRxCiY9ABq/l NDF1Jey08t0gKeKl5efIo4YAFAN6Fstw/WQTN0mGEyfyIiGiyT4uYimxH1g/dK42Ze1CAj2POsg wif7HPxkzouMUDT22Lp/qdLOEZ8FA= X-Google-Smtp-Source: AGHT+IF/c8xZZbHxqmKwFm2KM30QfhaqZd/T10DolkjOVaTa7XeGQmqCyCfHYXJUXPapTlSjOFar908rifZWtroty1w= X-Received: by 2002:a17:902:d482:b0:224:1074:63af with SMTP id d9443c01a7336-225e0aef7c4mr181535735ad.34.1742233561994; Mon, 17 Mar 2025 10:46:01 -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> <00b001db9418$85c7c770$91575650$@ymail.com> In-Reply-To: <00b001db9418$85c7c770$91575650$@ymail.com> From: Renan Alves Fonseca Date: Mon, 17 Mar 2025 18:45:50 +0100 X-Gm-Features: AQ5f1Jqp0UOnZSGKPgTMat1ifvDhPUtsmLLnJFzdHh_rJkGb7QpuuR_zmULrZtA Message-ID: Subject: Re: Bulk DML performance To: bill.poole@ymail.com, pgsql-performance@postgresql.org Content-Type: multipart/alternative; boundary="00000000000094206f06308d5cbf" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000094206f06308d5cbf Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi, Here are some observations. Em seg., 17 de mar. de 2025 =C3=A0s 09:19, escreveu: > > PostgreSQL has a lot of overhead per row. > > Okay, thanks. I'm not actually too worried about this since in my > scenario, each row is about 1.5 kB, so the % overhead is negligible. > > > It is probably not the lookup, but the *modification* of the index that > is slow. > > Yes that makes sense for the original 3 million inserts, but when I > perform the update of the 3 million rows, the index doesn't change - they > are all HOT updates. > Using "perf" I can see that the overhead is indeed due to index lookup when we do HOT updates. > > Then the best you can do is to use COPY rather than INSERT. It will > perform better (but [not] vastly better). > > I need to perform a merge (INSERT ... ON CONFLICT ... DO UPDATE) on the > data, so sadly I cannot use COPY. > > I have discovered that for some reason, performing the original insert > without the ON CONFLICT statement is twice as fast as performing the > original insert with an ON CONFLICT ... DO UPDATE clause, completing in 4 > seconds instead of 8. That seems strange to me because I wouldn't have > thought it would be doing any additional work since a unique constraint i= s > on the primary key, so each inserted value would need to be checked in > either case, and there is no extra work to be done in either case. > > In the INSERT case, we do not check the unique constraint for each row. We run into an error when inserting a duplicate, aborting the operation. > INSERT INTO test (id, text1) > SELECT generate_series, 'x' > FROM generate_series(1, 3000000) > > It remains 4 seconds even when adding a clause to not insert duplicates. > > INSERT INTO test (id, text1) > SELECT generate_series, 'x' > FROM generate_series(1, 3000000) > WHERE NOT EXISTS ( > SELECT 1 > FROM test4 > WHERE id =3D generate_series > ) > > In this case, we are not checking duplicates inside the input dataset. If you can guarantee, at the application level, that there are no duplicates, this seems a good speedup. Perhaps the MERGE clause... > Furthermore, I have found that performing an UPDATE rather than an INSERT > ... ON CONFLICT ... DO UPDATE is twice as slow, completing in 16 seconds > instead of 14 seconds. > > UPDATE test > SET text1 =3D 'x' > FROM generate_series(1, 3000000) > WHERE test4.id =3D generate_series > > This also now means that updating 3 million rows takes 4x longer than > inserting those rows. Do we expect updates to be 4x slower than inserts? > > It is not the update that is slower. It is the attached where clause that makes it slower. Try: UPDATE test SET text1=3D'x'; In my tests, the update of non-indexed columns is slightly faster than an insert. Regards, Renan Fonseca --00000000000094206f06308d5cbf Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,
Here are some observations.

Em seg., 17 de mar. de 2025 =C3=A0s 09:19, <bill.poole@ymail.com> escreveu:
> PostgreSQL has a lot of overhead= per row.

Okay, thanks. I'm not actually too worried about this since in my scena= rio, each row is about 1.5 kB, so the % overhead is negligible.

> It is probably not the lookup, but the *modification* of the index tha= t is slow.

Yes that makes sense for the original 3 million inserts, but when I perform= the update of the 3 million rows, the index doesn't change - they are = all HOT updates.

Using "perf"= I can see that the overhead is indeed due to index lookup when we do HOT u= pdates.
=C2=A0
> Then the best you can do is to use COPY rather than INSERT. It will pe= rform better (but [not] vastly better).

I need to perform a merge (INSERT ... ON CONFLICT ... DO UPDATE) on the dat= a, so sadly I cannot use COPY.

I have discovered that for some reason, performing the original insert with= out the ON CONFLICT statement is twice as fast as performing the original i= nsert with an ON CONFLICT ... DO UPDATE clause, completing in 4 seconds ins= tead of 8. That seems strange to me because I wouldn't have thought it = would be doing any additional work since a unique constraint is on the prim= ary key, so each inserted value would need to be checked in either case, an= d there is no extra work to be done in either case.


In the INSERT case, we do not check th= e unique constraint for each row. We run into an error when inserting a dup= licate, aborting the operation.
=C2=A0
INSERT INTO test (id, text1)
SELECT generate_series, 'x'
FROM generate_series(1, 3000000)

It remains 4 seconds even when adding a clause to not insert duplicates.
INSERT INTO test (id, text1)
SELECT generate_series, 'x'
FROM generate_series(1, 3000000)
WHERE NOT EXISTS (
=C2=A0 SELECT 1
=C2=A0 FROM test4
=C2=A0 WHERE id =3D generate_series
)


In this case, we are not checking dupl= icates inside the input dataset. If you can guarantee, at the application l= evel, that there are no duplicates, this seems a good speedup. Perhaps the = MERGE clause...
=C2=A0
Furthermore, I have found that performing an UPDATE rather than an INSERT .= .. ON CONFLICT ... DO UPDATE is twice as slow, completing in 16 seconds ins= tead of 14 seconds.

UPDATE test
SET text1 =3D 'x'
FROM generate_series(1, 3000000)
WHERE test= 4.id =3D generate_series

This also now means that updating 3 million rows takes 4x longer than inser= ting those rows. Do we expect updates to be 4x slower than inserts?


It is not the update that is slower. I= t is the attached where clause that makes it slower. Try:
UPDATE test SE= T text1=3D'x';

In my tests, the update of non-indexed column= s=C2=A0is slightly faster than an insert.
=C2=A0
Regards,
R= enan Fonseca

--00000000000094206f06308d5cbf--