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 1tu5iL-003Viw-9t for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 08:20:49 +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 1tu5iJ-008XoI-Dw for pgsql-performance@arkaria.postgresql.org; Mon, 17 Mar 2025 08:20:47 +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 1tsfZ5-009poj-Aq for pgsql-performance@lists.postgresql.org; Thu, 13 Mar 2025 10:13:24 +0000 Received: from sonic311-13.consmr.mail.bf2.yahoo.com ([74.6.131.123]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tsfZ1-002asY-1y for pgsql-performance@postgresql.org; Thu, 13 Mar 2025 10:13:22 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ymail.com; s=s2048; t=1741860797; bh=G4uStFQGBG9daASyAZrVT+hgIodzKjmrSZ7UW8Og08M=; h=From:To:References:In-Reply-To:Subject:Date:From:Subject:Reply-To; b=ULOJt9N7KHXsPQSxn4g8TKfTvowShXQDYbC2o07EKUKRvCMtCizKXcCHk77Vui4asUDcbnr/byKQlVQv0NInUFjKGpI3vWkKpPhpM6hfZFc1b6QtGxrFa0s63xGqFsJ3756OMDtIeq8cOh9tmwcBmEvoFFYIY24z5mLqrAq55U8wq9orDcMIXkglFo63eiOELtiLjNC2xX5HRKSCQHlWI5JXIGIkMwq9gCzq9JIiG5zqv4Ml7u8hQaAzs3df7Gih7FB7a4OiMpQdTFfS8HBrfDe0rtoC0Y6IVcZOQMqh1EjxlRopt5uKtE0q0cOXIiKx5uBQaJ1vV2m1pIlXos4peg== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1741860797; bh=fpPmfmfnQcGDPB1i6ha4MwFDYIg2bdl7bH7mxFn0C9p=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=fk+LmbOxVXZL98OPKZWgF8xl+iAO14+T7E8fLvJn33EIokA/sslhY76m5yxmZWK/xQcjp2iLYTJVKs6eToauB5ZOS6t5F13mpcjqBibNm2OTWRYei3ZQLFX1D0vei4ss84QxNkoTSsbZLe/93kdfGTl9tknrlVua6VS95Pc7PTaOby2rNhTcHIdhMDwvpNkVjlHw9pMscRLpXfa4pZKCO5BREDrkOU29jVBOJTUt1GnmaNCDOPoT4LdS8idSCEFa3zt4ZJYxvoMFpHT2jBuuytJjuH33TrCIrFEuJQ60VNWJ15BLwfg4DSZrt8xosM6O7imTeor2sNmWD9KofPF7Mw== X-YMail-OSG: 3P4B1hIVM1mx66VoiEk598q1PnGMsFiFEezyTPktqPFYITAIFsptnIJWmIo7e58 ufplJHZMEm2ABEcpxJIHH2VDsrUHkCz1rpEhtgV5MT302K6PgYAVrXG96Btc5f450nM10SV0os32 jE8iZ3arBOOxcHscERcc5gRmdsSaOatZ.xkmKBSPEuB14oVvMED1w1t.Qz6eBCdB0DneY5z8Xzpz Lvk4o0nxj0WzLCkjtv31ZuvIQ2tPWYPj4g8quwS7MzHd8hbGK1UE0CHtxCWfG7auyXReAObVMGnw t12SwMTIjXFapnMXkan6taSlPrpgn7U7N.SAB1CjSU26cjKau0wH0EAS3Xc2l6WCaYA_SGsLyRMH d3Xg3nzp521ZBAJ4Ja.FgbB59k6EjLXh8WMma.oLPgihPXIs.8fhGqyFIba.6pv9M72Tgb5swRBn i.swKSGfhWCC1G2ENBRL9i.B8xACfSxEmyghfdnMYWABtQZpWE5dAvi4Jhnbxtxa2Pq6qAfHp6zM 2MWaRhm1ouq2Qyswe_0fAtdES.BVMk76RLSYSGa0m6e.l7NT8I_wMB1n8M7YBy0_ZocbrGOhxi9n NOjZ28ni.JDzYP_ias.OAMzOj.m9LsIf6atbTe9fFqv0mldp6F4wWX9ga57ccTyn2GTW4o4lMjkq 4x3fkI54NUubeOUzIQ35gcbUv9yc33Rgxhsly.5NGZa14N1ft1nPzFwbmMt.XibssH5sam1bH41p 7wqNKRb5qdOKpFujCKRAmJoVT_XmDdkAxb7Q0dmWI0.zmwr_JTbmXYohqSFHDtH4AtK6DXvpPwXu UcVE0BJVh6pxGs3ofqCJ.X1n6tRCYmyKNwjKNjW4aDgNmkaJfBkoHivR0bloCVdw1ZOiPEvx_Rp4 qgVsDKBVroYWiqu28bROhjZd9zpt2txN2N9Gr_dfLPK5EJUiwjtwk85.406SzUnur5R40Nn4dWKy 5PLrdETt6T8bbi3hMuIu0OyqYGbhvcoDNHGhhz0Q5h.oPMZe5F7YnzFZXEm6W3vSOkY8XeEHzVcS rpNLheqxA_cvjoapFuK4Im7lDvMR8PIkph3NwfgmwjZxHYBPnMJkHnU38kRs43bhAyFnwdEwjWze HAt.n6v.5mmuEcfbpH3kl0MBuA2v61WcC63hNHM8C0lZlfNDgZzI_Cuo5kyYFDo0UH5Fgr7yy_gn ri4FEJ8rqBKmEoXBaVT59OUA2Djd1Xoo3d6msqAKDxR7Pb5hIL.Y7dUTDRY91tRznwsaRIHrTX7o fzZYN2wx3ONJBFvA6aiuZ82KCo0TGeB7C4n7cdkIA9PW3u.nVuhOfwHiUbH5vrWQowrO8Nicb3uH LV1nu6THev3Ciu1J6QHnTR1gOHK9pFMGwa7QN4k.wpWQ_TXdmucMsYQ6F_InLdZkiZ4L20d2yNOf 7b2TrhW_WSyHgxAP3Zer8nqrttlgVR4LLNkHyqcCfS.cGPaPQ.CqnqsY5Y4PzepnYLHIdqqc2Gau bITGsmmX2W70FFCp201swGR289VM.8_jzq9SEO25oHUwSTogml7JtaUpocJwvmIETh5t4I1Zah4j M5.WmDuyKUM7HpOL.R.0LoZLA3WxV9E0HLwHeRGzvpG2hrLbKVb12YBwvTx4lk0t2GCT__GmIgKF bQWQj4XskM9MxSFWt65LhZe0yCrdPZsNx3LhOE_9nYFMX7UT0118ekc8iTbxi.OaGoKHY4lI1EMq HKpDM6MFAQONAswHUigvaB9TokkXMxH7Jg3bQ_hUoteIvFDamGZeDazyJnoH2eOytJDVoLlLeJNC TrJRiGzv83fNC8rmXDcG6qYZgn85BGJHAq4PZg49Et4zwx9wNwBSuW71xEey0vkCfVFtuz4wbK97 TCm.vXOviI85U3wT8fWo1by.cAVtSutEAxlxK7z9daBI4jn9w8tw.RIaRlNTDlrOs2IB_gpu54XB t68fKd2tXZrq6FGD1U7ckFsdNRPJN.Hg4YTtMcEu_DuSQsySIYe5dLozk7iaiH6GD5RvS3h4JKA1 qnygSGXlexxLkWPsVSi6oVP74KelCng6Vtq8F3BcwPuPLDE_gd35W8YZnn0zSQHcl3ufIPfRWY_e gM_8q1ET0HzJRtD_fym.VpmxAoa7dMVx1FWX81LWow.Ngnf8UvliAboBrlWCDw1mQA6kJGozL.P3 45FwE.FXqZOyxhJpTlc8A.uvs2DD9KzByGa5K4yCLemeOYZNVzWqkMwGSeGvjGpq_3mclMewibMo qrfVVrUsngxfc1SsYovmbXfdkMQfE_rycFTc- X-Sonic-MF: X-Sonic-ID: b0f2d016-a204-43b7-be04-537205051536 Received: from sonic.gate.mail.ne1.yahoo.com by sonic311.consmr.mail.bf2.yahoo.com with HTTP; Thu, 13 Mar 2025 10:13:17 +0000 Received: by hermes--production-gq1-7d5f4447dd-vgkjt (Yahoo Inc. Hermes SMTP Server) with ESMTPA ID 18953b5a76612c0dbc7995201d235590; Thu, 13 Mar 2025 10:13:11 +0000 (UTC) From: To: "'Laurenz Albe'" , References: <008701db93cd$2eb404d0$8c1c0e70$.ref@ymail.com> <008701db93cd$2eb404d0$8c1c0e70$@ymail.com> <0942a47d7a89f1b181f7e306f8389d9717eb5fe0.camel@cybertec.at> In-Reply-To: <0942a47d7a89f1b181f7e306f8389d9717eb5fe0.camel@cybertec.at> Subject: RE: Bulk DML performance Date: Thu, 13 Mar 2025 18:13:08 +0800 Message-ID: <00a401db9400$86c240f0$9446c2d0$@ymail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Mailer: Microsoft Outlook 16.0 Thread-Index: AQCh7jmKC8Qb7CsNFahmY0MRK48+bwLkLSPcAykm/9G1s7YUwA== Content-Language: en-au Content-Length: 5319 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Thanks Laurenz for taking the time to look at this. > That is unavoidable, because PostgreSQL adds a new version for each = row to the table. To avoid that kind of bloat, you'd have to update in = smaller batches and run VACUUM between those to free the "dead" row = versions. Yes, I would expect the number of blocks/MBs to double due to executing = an update, but it is noteworthy that inserting 27 MB of data into a = newly created table creates 191 MB of data including the index and 127 = MB of data excluding the index. > It is the index maintenance that is killing you. Yes, but as you say I need the index to perform the INSERT ... ON = CONFLICT (...) DO UPDATE. Can you help me understand why performing 3 = million lookups on a b-tree index with all pages cached in memory takes = so long? > If you don't need crash safety, you could use UNLOGGED tables and be = somewhat faster (5.8 seconds for the initial INSERT here). Sadly, that is not an option for me. I'm building a production system. > 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 = million rows identified by 3 million IDs should be faster than updating = 3 million rows (also identified by 3 million IDs). > 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. > To get better performance, the best I can think of is to parallelize = loading the data until you saturate CPU, disk or hit internal contention = in the database. Sadly, I cannot do that because I need all rows to be inserted in a = single database transaction, which I cannot do over multiple database = connections. Regards, Bill -----Original Message----- From: Laurenz Albe =20 Sent: Thursday, 13 March 2025 5:21 PM To: bill.poole@ymail.com; pgsql-performance@postgresql.org Subject: Re: Bulk DML performance On Thu, 2025-03-13 at 12:05 +0800, bill.poole@ymail.com wrote: > The following INSERT .. ON CONFLICT (=E2=80=A6) DO UPDATE statement=20 > inserts/updates > 3 million rows with only 9 bytes per row and takes about 8 seconds on=20 > first run (to insert the rows) and about 14 seconds on subsequent runs = > (to update the rows), I can confirm these times on my not so new laptop with local NVME. That's the time it takes if you have an index on the table and want to = be crash safe. > but is only inserting 27 MB of data (3 million rows with 9=20 > bytes per row); although after the first run, SELECT=20 > pg_size_pretty(pg_total_relation_size('test')) reports the table size=20 > as 191 MB and after the second run reports the table size as 382 MB=20 > (adding another 191 MB). That is unavoidable, because PostgreSQL adds a new version for each row = to the table. To avoid that kind of bloat, you'd have to update in = smaller batches and run VACUUM between those to free the "dead" row = versions. > CREATE TABLE test ( > id bigint PRIMARY KEY, > text1 text > ); > =20 > INSERT INTO test (id, text1) > SELECT generate_series, 'x' > FROM generate_series(1, 3000000) > ON CONFLICT (id) DO UPDATE > SET text1 =3D 'x'; > =20 > If PostgreSQL is writing 191 MB on the first run and 382 MB on each=20 > subsequent run, then PostgreSQL is only writing about 28 MB/s.=20 > Although PostgreSQL is also able to write about 4.5 GB in about 35=20 > seconds (as stated above), which is about 128 MB/s, so it seems the=20 > performance constraint depends on the number of rows inserted more = than the size of each row. It is the index maintenance that is killing you. Without the primary key, the first insert takes under 1.8 seconds here. But you need the primary key index if you want to use INSERT ... ON = CONFLICT. The update has to do even more work, so it is slower. If you don't need crash safety, you could use UNLOGGED tables and be = somewhat faster (5.8 seconds for the initial INSERT here). Essentially, the moderate performance is the price you are paying for = data integrity (crash safety) and consistency (primary key). > Furthermore, deleting the rows takes about 18 seconds to perform=20 > (about 4 seconds longer than the time taken to update the rows): > =20 > DELETE FROM test > WHERE id in ( > SELECT * FROM generate_series(1, 3000000) > ) Well, that is not a great statement. The following takes only 1.6 seconds here: DELETE FROM test WHERE id BETWEEN 1 AND 3000000; And if you want to delete all rows, TRUNCATE is very, very fast. > It seems like it should be possible to do better than this on modern=20 > hardware, but I don=E2=80=99t have enough knowledge of the inner = workings of=20 > PostgreSQL to know whether my instinct is correct on this, so I=20 > thought I=E2=80=99d raise the question with the experts. With the table as it is you won't get better performance if you want the = features that a relational database provides. To get better performance, the best I can think of is to parallelize = loading the data until you saturate CPU, disk or hit internal contention = in the database. Yours, Laurenz Albe