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 1rxJDA-007ab8-C3 for pgsql-hackers@arkaria.postgresql.org; Thu, 18 Apr 2024 04:17:24 +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 1rxJD8-0009Lk-Og for pgsql-hackers@arkaria.postgresql.org; Thu, 18 Apr 2024 04:17:22 +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.94.2) (envelope-from ) id 1rxJD8-0009Lc-Bn for pgsql-hackers@lists.postgresql.org; Thu, 18 Apr 2024 04:17:22 +0000 Received: from mail-il1-x12c.google.com ([2607:f8b0:4864:20::12c]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rxJD3-001ZA4-MK for pgsql-hackers@postgresql.org; Thu, 18 Apr 2024 04:17:21 +0000 Received: by mail-il1-x12c.google.com with SMTP id e9e14a558f8ab-36b2fcd1fb6so1375885ab.3 for ; Wed, 17 Apr 2024 21:17:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713413835; x=1714018635; darn=postgresql.org; h=content-disposition:mime-version:message-id:subject:to:from:date :from:to:cc:subject:date:message-id:reply-to; bh=3IDBMC+Air5VFtHOpdy73mQV8PEnOsiEVaLiSRkip48=; b=XJTIy/UpOx0Arpqh+Wo5GNx5ab1WYCLo9ZpB+QAtHHnU7Jl/BqL20NwR3bpHfIBSfp gPo7VwzQul1QhfrN1tTYC/KWeF3YKvZthbhZC+3d/e53+SfOcCUrqnxPRdqoqOYrGMAe FdxPs5ylP2pOW64dQi7qOrnXs9Rm7vm09+hylD+6G6dOQCxGdmk5JIFtuDS/sKgDYNAK KLhOlH6uSLw12g+vQRxYaIfhWpw+Ar3XYorusGgbv6AbUbDh2BTmHUvS2L9pFdF9iapn 7xM1M3GX6C4Jb92acyzYKBb9J0Apm/sA/74dmizMG0I1KwFj4BAM4gqfdPlyr0gCNI2e 9FSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713413835; x=1714018635; h=content-disposition:mime-version:message-id:subject:to:from:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=3IDBMC+Air5VFtHOpdy73mQV8PEnOsiEVaLiSRkip48=; b=gyIFIIGS2dTvm0JgmaSMWZHMNZEIOkfAp3iJvFqsF+hFfddt2T52R/8V6s0l/5rcPw r4X2Ib8dGU9kjU++9Yf/qvl54P2C6fpTTbX6UjJ1oM98w0YkDNa4+NNn4m5NDBDx4srB ZFgUQWUf3zd8ATWELN5J3xatWeRMnPOPWaNTEMNTnR0iUZMQJuGkBr6nFMwZ1RJG5Qx1 NC68T51p9w366a+bsLoipl/L2C6ycffG8T8GzoJBRtnrbMcubHb46LcpT/Vb344lyFK2 Nyfp8P9c8FrkwhImUgY/3zcruDTYwA7rTQ5wIY6snIh+8KyQwecvuUB1TjJkhnc49fTL oJPw== X-Gm-Message-State: AOJu0YwpCVd6V8ECp5XGVw04oWclykbpqOUndFUybv2AfGbA8+CeXcGH TgdN1PEK/ito7B+JG0p5rW971KcnsBNFX+ysW0rTgM+2XTImDsNwr08uqA== X-Google-Smtp-Source: AGHT+IHQHj/M8NWY3YW75Svfp3kotCWp9pJpba07KznY22/2jNcFmmYM04nrLyl7So4mTNNVkhpnEg== X-Received: by 2002:a05:6e02:174e:b0:36a:686:b3bf with SMTP id y14-20020a056e02174e00b0036a0686b3bfmr2161094ill.17.1713413834812; Wed, 17 Apr 2024 21:17:14 -0700 (PDT) Received: from nathanxps13 (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id is38-20020a056638852600b00482eb16516csm198069jab.97.2024.04.17.21.17.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 17 Apr 2024 21:17:14 -0700 (PDT) Date: Wed, 17 Apr 2024 23:17:12 -0500 From: Nathan Bossart To: pgsql-hackers@postgresql.org Subject: improve performance of pg_dump --binary-upgrade Message-ID: <20240418041712.GA3441570@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nFreZHaLTZJo0R7j" Content-Disposition: inline List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --nFreZHaLTZJo0R7j Content-Type: text/plain; charset=us-ascii Content-Disposition: inline While examining pg_upgrade on a cluster with many tables (created with the command in [0]), I noticed that a huge amount of pg_dump time goes towards the binary_upgrade_set_pg_class_oids() function. This function executes a rather expensive query for a single row, and this function appears to be called for most of the rows in pg_class. The attached work-in-progress patch speeds up 'pg_dump --binary-upgrade' for this case. Instead of executing the query in every call to the function, we can execute it once during the first call and store all the required information in a sorted array that we can bsearch() in future calls. For the aformentioned test, pg_dump on my machine goes from ~2 minutes to ~18 seconds, which is much closer to the ~14 seconds it takes without --binary-upgrade. One downside of this approach is the memory usage. This was more-or-less the first approach that crossed my mind, so I wouldn't be surprised if there's a better way. I tried to keep the pg_dump output the same, but if that isn't important, maybe we could dump all the pg_class OIDs at once instead of calling binary_upgrade_set_pg_class_oids() for each one. [0] https://postgr.es/m/3612876.1689443232%40sss.pgh.pa.us -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --nFreZHaLTZJo0R7j Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-Improve-performance-of-pg_dump-binary-upgrade.patch" From 27b4a3249dd97376f13a7c99505330ab7cd78e3f Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Wed, 17 Apr 2024 22:55:27 -0500 Subject: [PATCH v1 1/1] Improve performance of pg_dump --binary-upgrade. --- src/bin/pg_dump/pg_dump.c | 113 +++++++++++++++++++------------ src/tools/pgindent/typedefs.list | 1 + 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c52e961b30..d93d974108 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -55,6 +55,7 @@ #include "catalog/pg_trigger_d.h" #include "catalog/pg_type_d.h" #include "common/connect.h" +#include "common/int.h" #include "common/relpath.h" #include "compress_io.h" #include "dumputils.h" @@ -99,6 +100,17 @@ typedef enum OidOptions zeroAsNone = 4, } OidOptions; +typedef struct +{ + Oid oid; + char relkind; + RelFileNumber relfilenode; + Oid reltoastrelid; + RelFileNumber toast_relfilenode; + Oid indexrelid; + RelFileNumber toast_index_relfilenode; +} BinaryUpgradeClassOids; + /* global decls */ static bool dosync = true; /* Issue fsync() to make dump durable on disk. */ @@ -5392,19 +5404,56 @@ binary_upgrade_set_type_oids_by_rel(Archive *fout, pg_type_oid, false, false); } +static int +BinaryUpgradeClassOidsCmp(const void *p1, const void *p2) +{ + BinaryUpgradeClassOids v1 = *((const BinaryUpgradeClassOids *) p1); + BinaryUpgradeClassOids v2 = *((const BinaryUpgradeClassOids *) p2); + + return pg_cmp_u32(v1.oid, v2.oid); +} + static void binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_class_oid, bool is_index) { - PQExpBuffer upgrade_query = createPQExpBuffer(); - PGresult *upgrade_res; - RelFileNumber relfilenumber; - Oid toast_oid; - RelFileNumber toast_relfilenumber; - char relkind; - Oid toast_index_oid; - RelFileNumber toast_index_relfilenumber; + static BinaryUpgradeClassOids *oids = NULL; + static int oids_len = 0; + BinaryUpgradeClassOids key = {0, '?', 0, 0, 0, 0, 0}; + BinaryUpgradeClassOids *entry; + + if (oids == NULL) + { + PGresult *res; + + res = ExecuteSqlQuery(fout, + "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, ct.relfilenode AS toast_relfilenode, " + "i.indexrelid, cti.relfilenode AS toast_index_relfilenode " + "FROM pg_catalog.pg_class c LEFT JOIN " + "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) " + "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) " + "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) " + "ORDER BY c.oid;", + PGRES_TUPLES_OK); + + oids_len = PQntuples(res); + oids = (BinaryUpgradeClassOids *) + pg_malloc(oids_len * sizeof(BinaryUpgradeClassOids)); + + for (int i = 0; i < oids_len; i++) + { + oids[i].oid = atooid(PQgetvalue(res, i, 0)); + oids[i].relkind = *PQgetvalue(res, i, 1); + oids[i].relfilenode = atooid(PQgetvalue(res, i, 2)); + oids[i].reltoastrelid = atooid(PQgetvalue(res, i, 3)); + oids[i].toast_relfilenode = atooid(PQgetvalue(res, i, 4)); + oids[i].indexrelid = atooid(PQgetvalue(res, i, 5)); + oids[i].toast_index_relfilenode = atooid(PQgetvalue(res, i, 6)); + } + + PQclear(res); + } /* * Preserve the OID and relfilenumber of the table, table's index, table's @@ -5417,29 +5466,9 @@ binary_upgrade_set_pg_class_oids(Archive *fout, * by the new backend, so we can copy the files during binary upgrade * without worrying about this case. */ - appendPQExpBuffer(upgrade_query, - "SELECT c.relkind, c.relfilenode, c.reltoastrelid, ct.relfilenode AS toast_relfilenode, i.indexrelid, cti.relfilenode AS toast_index_relfilenode " - "FROM pg_catalog.pg_class c LEFT JOIN " - "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) " - "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) " - "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) " - "WHERE c.oid = '%u'::pg_catalog.oid;", - pg_class_oid); - - upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); - - relkind = *PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "relkind")); - - relfilenumber = atooid(PQgetvalue(upgrade_res, 0, - PQfnumber(upgrade_res, "relfilenode"))); - toast_oid = atooid(PQgetvalue(upgrade_res, 0, - PQfnumber(upgrade_res, "reltoastrelid"))); - toast_relfilenumber = atooid(PQgetvalue(upgrade_res, 0, - PQfnumber(upgrade_res, "toast_relfilenode"))); - toast_index_oid = atooid(PQgetvalue(upgrade_res, 0, - PQfnumber(upgrade_res, "indexrelid"))); - toast_index_relfilenumber = atooid(PQgetvalue(upgrade_res, 0, - PQfnumber(upgrade_res, "toast_index_relfilenode"))); + key.oid = pg_class_oid; + entry = bsearch(&key, oids, oids_len, sizeof(BinaryUpgradeClassOids), + BinaryUpgradeClassOidsCmp); appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n"); @@ -5455,35 +5484,33 @@ binary_upgrade_set_pg_class_oids(Archive *fout, * partitioned tables have a relfilenumber, which should not be * preserved when upgrading. */ - if (RelFileNumberIsValid(relfilenumber) && relkind != RELKIND_PARTITIONED_TABLE) + if (RelFileNumberIsValid(entry->relfilenode) && entry->relkind != RELKIND_PARTITIONED_TABLE) appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n", - relfilenumber); + entry->relfilenode); /* * In a pre-v12 database, partitioned tables might be marked as having * toast tables, but we should ignore them if so. */ - if (OidIsValid(toast_oid) && - relkind != RELKIND_PARTITIONED_TABLE) + if (OidIsValid(entry->reltoastrelid) && + entry->relkind != RELKIND_PARTITIONED_TABLE) { appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n", - toast_oid); + entry->reltoastrelid); appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n", - toast_relfilenumber); + entry->toast_relfilenode); /* every toast table has an index */ appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n", - toast_index_oid); + entry->indexrelid); appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n", - toast_index_relfilenumber); + entry->toast_index_relfilenode); } - - PQclear(upgrade_res); } else { @@ -5493,12 +5520,10 @@ binary_upgrade_set_pg_class_oids(Archive *fout, pg_class_oid); appendPQExpBuffer(upgrade_buffer, "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n", - relfilenumber); + entry->relfilenode); } appendPQExpBufferChar(upgrade_buffer, '\n'); - - destroyPQExpBuffer(upgrade_query); } /* diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index d551ada325..c8062a1a20 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -255,6 +255,7 @@ BernoulliSamplerData BgWorkerStartTime BgwHandleStatus BinaryArithmFunc +BinaryUpgradeClassOids BindParamCbData BipartiteMatchState BitString -- 2.25.1 --nFreZHaLTZJo0R7j--