web-dev-qa-db-fra.com

Requête de mise à jour simple et lente sur la base de données PostgreSQL avec 3 millions de lignes

J'essaie un simple UPDATE table SET column1 = 0 sur une table avec environ 3 millions de lignes sur Postegres 8.4, mais cela prend une éternité. Il fonctionne depuis plus de 10 minutes. maintenant dans ma dernière tentative.

Avant, j’essayais d’exécuter des commandes VACUUM et ANALYZE sur cette table et j’essayais également de créer des index (bien que je doute que cela ferait une différence dans ce cas), mais aucune ne semble aider.

D'autres idées?

Merci, Ricardo

Mettre à jour:

Voici la structure de la table:

CREATE TABLE myTable
(
  id bigserial NOT NULL,
  title text,
  description text,
  link text,
  "type" character varying(255),
  generalFreq real,
  generalWeight real,
  author_id bigint,
  status_id bigint,
  CONSTRAINT resources_pkey PRIMARY KEY (id),
  CONSTRAINT author_pkey FOREIGN KEY (author_id)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT c_unique_status_id UNIQUE (status_id)
);

J'essaie d'exécuter UPDATE myTable SET generalFreq = 0;

26
Ricardo Lage

Regardez cette réponse: PostgreSQL ™ lent sur une grande table avec des tableaux et de nombreuses mises à jour

Commencez par commencer avec un meilleur FILLFACTOR, faites un VACUUM FULL pour forcer la réécriture de table et vérifiez les mises à jour HOT après votre requête UPDATE:

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';

Les mises à jour HOT sont beaucoup plus rapides lorsque vous avez beaucoup d'enregistrements à mettre à jour. Plus d'informations sur HOT peuvent être trouvées dans cet article article .

Ps. Vous avez besoin de la version 8.3 ou supérieure.

13
Frank Heikens

Je dois mettre à jour des tableaux de 1 ou 2 milliards de lignes avec différentes valeurs pour chaque ligne. Chaque exécution effectue environ 100 millions de modifications (10%). Mon premier essai a été de les regrouper en transaction de 300 000 mises à jour directement sur une partition spécifique car Postgresql n'optimise pas toujours les requêtes préparées si vous utilisez des partitions.

  1. Transactions du groupe de "UPDATE myTable SET myField = valeur WHERE MyId = id"
    Donne 1500 mises à jour/sec. ce qui signifie que chaque cycle prendrait au moins 18 heures.
  2. HOT met à jour la solution décrite ici avec FILLFACTOR = 50. Donne 1 600 mises à jour/sec. J'utilise les disques SSD, donc c'est une amélioration coûteuse, car Double la taille de stockage.
  3. Insérer dans une table temporaire de valeur mise à jour et les fusionner après Avec UPDATE ... FROM Gives 18 000 updates/s. si je fais un VACUUM pour chaque partition; 100 000 up/s sinon. Cooool.
    Voici la séquence d'opérations :

CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
CONSTRAINT tempTable_pkey PRIMARY KEY (id));

Accumuler une série de mises à jour dans une mémoire tampon en fonction de la RAM disponible Une fois remplie, si vous devez changer de table/partition, ou avoir terminé:

COPY tempTable FROM buffer;
UPDATE myTable a SET field(s)=value(s) FROM tempTable b WHERE a.id=b.id;
COMMIT;
TRUNCATE TABLE tempTable;
VACUUM FULL ANALYZE myTable;

Cela signifie qu’une exécution prend désormais 1,5 heure au lieu de 18 heures pour 100 millions de mises à jour, y compris le vide.

28
Le Droid

Après avoir attendu 35 min. pour que ma requête UPDATE se termine (et ne l’a toujours pas fait), j’ai décidé d’essayer quelque chose de différent. Alors ce que j'ai fait était une commande:

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable

Ajoutez ensuite des index, puis supprimez l'ancienne table et renommez la nouvelle pour prendre sa place. Cela n'a pris que 1,7 min. traiter plus un peu de temps supplémentaire pour recréer les index et les contraintes. Mais ça a aidé! :)

Bien sûr, cela n'a fonctionné que parce que personne d'autre n'utilisait la base de données. Je devrais d'abord verrouiller la table s'il s'agissait d'un environnement de production.

7
Ricardo Lage

Aujourd'hui, j'ai passé de nombreuses heures avec le même problème. J'ai trouvé un solution : supprimer toutes les contraintes/indices avant la mise à jour . Que la colonne mise à jour soit indexée ou non, il semble que psql mette à jour tous les index de toutes les lignes mises à jour. Une fois la mise à jour terminée, rajoutez les contraintes/indices.

2
Tregoreg

Essayez ceci (notez que generalFreq commence par le type REAL et reste identique):

ALTER TABLE myTable ALTER COLUMN generalFreq TYPE REAL USING 0;

Cela réécrira la table, comme un DROP + CREATE, et reconstruira tous les index. Mais tout en une seule commande. Beaucoup plus rapidement (environ 2x) et vous n’aurez pas à vous soucier des dépendances ni à la recréation d’index ou d’autres choses, bien que cela verrouille la table (accès exclusif - c’est-à-dire un verrouillage complet) pour la durée. Ou peut-être que c'est ce que vous voulez si vous voulez que tout le reste soit placé derrière. Si vous ne mettez pas à jour "trop" de lignes, cette procédure est plus lente qu'une mise à jour.

2
Fabiano Bonin

Comment ça se passe? Si vous bouclez chaque ligne et effectuez une instruction de mise à jour, vous exécutez potentiellement des millions de mises à jour individuelles, ce qui explique pourquoi son exécution sera incroyablement lente.

Si vous exécutez une seule instruction de mise à jour pour tous les enregistrements d'une seule instruction, elle s'exécutera beaucoup plus rapidement. Si ce processus est lent, c'est probablement votre matériel qui compte le plus 3 millions, c'est beaucoup de disques.

0
Tom Gullen

Lors de mes tests, j'ai remarqué qu'une grosse mise à jour, plus de 200 000 lignes, est plus lente que deux mises à jour de 100 000 lignes, même avec une table temporaire.

Ma solution est de boucler, dans chaque boucle de créer une table temporaire de 200 000 lignes, dans cette table je calcule mes valeurs, puis je mets à jour ma table principale avec les nouvelles valeurs etc.

Toutes les 2 000 000 de lignes, j’ai manuellement «VACUUM ANALYZE mytable» (table d’analyse), j’ai remarqué que l’aspirateur automatique ne fonctionnait pas correctement.

0
Rolintocour

La première chose que je suggérerais (de https://dba.stackexchange.com/questions/118178/does-updating-a-row-with-the-same-value-actually-update-the-row ) consiste à mettre à jour uniquement les lignes qui en "ont besoin", ex:

 UPDATE myTable SET generalFreq = 0 where generalFreq != 0;

(peut aussi avoir besoin d'un index sur generalFreq). Ensuite, vous mettrez à jour moins de lignes. Mais pas si les valeurs sont déjà non nulles, mais la mise à jour de moins de lignes "peut aider", sinon elle les met à jour ainsi que tous les index, que la valeur ait été modifiée ou non.

Une autre option: si les étoiles s'alignent en termes de valeurs par défaut et de contraintes non nulles, vous pouvez supprimer l'ancienne colonne et créer une autre en ajustant simplement les métadonnées, le temps instantané.

0
rogerdpack