Utilisation de PG 9.1 sur Ubuntu 12.04.
Il nous faut actuellement jusqu'à 24h pour exécuter un grand ensemble d'instructions UPDATE sur une base de données, qui sont de la forme:
UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid
(Nous remplaçons simplement les champs des objets identifiés par ID.) Les valeurs proviennent d'une source de données externe (pas déjà dans la base de données dans une table).
Les tableaux ont chacun une poignée d'indices et aucune contrainte de clé étrangère. Aucun COMMIT n'est effectué jusqu'à la fin.
Il faut 2h pour importer un pg_dump
de la base de données entière. Cela semble être une référence que nous devrions raisonnablement cibler.
À moins de produire un programme personnalisé qui reconstruit en quelque sorte un ensemble de données pour que PostgreSQL soit réimporté, y a-t-il quelque chose que nous puissions faire pour rapprocher les performances de mise à jour en masse de celles de l'importation? (C'est un domaine que nous pensons que les arbres de fusion à structure logarithmique gèrent bien, mais nous nous demandons s'il y a quelque chose que nous pouvons faire dans PostgreSQL.)
Quelques idées:
Fondamentalement, il y a beaucoup de choses à essayer et nous ne savons pas quels sont les plus efficaces ou si nous oublions d'autres choses. Nous allons passer les prochains jours à expérimenter, mais nous avons pensé que nous poserions également la question ici.
J'ai une charge simultanée sur la table, mais elle est en lecture seule.
Comme il manque des informations dans le Q, je suppose:
COPY
, avec un uniqueid
par ligne pour correspondre à la table cible.COPY
pour gérer le format.Je vous suggère de suivre une approche similaire à celle décrite dans le lien depuis votre troisième puce . Avec des optimisations majeures.
Pour créer la table temporaire, il existe un moyen plus simple et plus rapide:
CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;
Un seul gros UPDATE
d'une table temporaire à l'intérieur la base de données sera plus rapide que les mises à jour individuelles de l'extérieur de la base de données de plusieurs ordres de grandeur.
Dans modèle MVCC de PostgreSQL , un UPDATE
signifie créer une nouvelle version de ligne et marquer l'ancienne comme supprimée. C'est à peu près aussi cher qu'un INSERT
et un DELETE
combinés. De plus, il vous laisse beaucoup de tuples morts. Comme vous mettez à jour la table entière de toute façon, il serait globalement plus rapide de simplement créer une nouvelle table et de supprimer l'ancienne.
Si vous en avez assez RAM disponible, définissez temp_buffers
(uniquement pour cette session!) suffisamment haut pour contenir la table temporaire dans RAM - avant de faire quoi que ce soit d'autre.
Pour obtenir une estimation de la quantité RAM est nécessaire, exécutez un test avec un petit échantillon et utilisez fonctions de taille d'objet db :
SELECT pg_size_pretty(pg_relation_size('tmp_tbl')); -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10; -- size of sample rows
SET temp_buffers = '1GB'; -- example value
CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;
COPY tmp_tbl FROM '/absolute/path/to/file';
CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM tbl t
JOIN tmp_tbl u USING (id);
-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);
-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;
DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically
Les opérations simultanées sur la table (que j'ai exclues dans les hypothèses au début) vont attendre, une fois que la table est verrouillée vers la fin et échouer dès que la transaction est validée, car le nom de la table est résolu à son OID immédiatement, mais la nouvelle table a un OID différent. La table reste cohérente, mais les opérations simultanées peuvent obtenir une exception et doivent être répétées. Détails dans cette réponse connexe:
Si vous (devez) suivre la route UPDATE
, supprimez tout index qui n'est pas nécessaire pendant la mise à jour et recréez-le ensuite. Il est beaucoup moins cher de créer un index en une seule pièce que de le mettre à jour pour chaque ligne individuelle. Cela peut également permettre mises à jour CHAUDES .
J'ai décrit une procédure similaire en utilisant UPDATE
dans cette réponse étroitement liée à SO .
Si les données peuvent être mises à disposition dans un fichier structuré, vous pouvez les lire avec un wrapper de données étranger et effectuer une fusion sur la table cible.