J'ai une table Postgres avec environ 2,1 millions de lignes. J'ai exécuté la mise à jour ci-dessous:
WITH stops AS (
SELECT id,
rank() OVER (ORDER BY offense_timestamp,
defendant_dl,
offense_street_number,
offense_street_name) AS stop
FROM consistent.master
WHERE citing_jurisdiction=1
)
UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;
Cette requête a pris 39 heures pour s'exécuter. J'exécute cela sur un processeur pour ordinateur portable i7 Q720 à 4 cœurs (physique), beaucoup de RAM, rien d'autre ne fonctionne la grande majorité du temps. Pas de contraintes d'espace disque dur. Le tableau avait récemment été aspiré, analysé et réindexé.
Pendant toute la durée de l'exécution de la requête, au moins après la fin de l'initialisation WITH
, l'utilisation du processeur était généralement faible et le disque dur était utilisé à 100%. Le disque dur était utilisé si durement que toute autre application fonctionnait beaucoup plus lentement que la normale.
Le paramètre d'alimentation de l'ordinateur portable était sur Haute performance (Windows 7 x64).
Voici l'EXPLIQUER:
Update on master (cost=822243.22..1021456.89 rows=2060910 width=312)
CTE stops
-> WindowAgg (cost=529826.95..581349.70 rows=2060910 width=33)
-> Sort (cost=529826.95..534979.23 rows=2060910 width=33)
Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
-> Seq Scan on master (cost=0.00..144630.06 rows=2060910 width=33)
Filter: (citing_jurisdiction = 1)
-> Hash Join (cost=240893.51..440107.19 rows=2060910 width=312)
Hash Cond: (stops.id = consistent.master.id)
-> CTE Scan on stops (cost=0.00..41218.20 rows=2060910 width=48)
-> Hash (cost=139413.45..139413.45 rows=2086645 width=268)
-> Seq Scan on master (cost=0.00..139413.45 rows=2086645 width=268)
citing_jurisdiction=1
exclut seulement quelques dizaines de milliers de lignes. Même avec cette WHERE
clause, j'opère toujours sur plus de 2 millions de lignes.
Le disque dur est entièrement chiffré avec TrueCrypt 7.1a. Que les choses de Ralentit un peu, mais pas assez pour causer une requête à prendre que de nombreuses heures.
La WITH
partie ne prend que 3 minutes environ pour fonctionner.
Le arrest_id
champ n'avait pas d'index pour la clé étrangère. Il y a 8 index et 2 clés étrangères sur cette table. Tous les autres champs de la requête sont indexés.
Le arrest_id
champ n'avait pas de contraintes sauf NOT NULL
.
Le tableau comprend 32 colonnes au total.
arrest_id
est de type variable (20) . Je me rends compte rank()
produit une valeur numérique, mais je dois utiliser la variation de caractère (20) parce que j'ai d'autres lignes où citing_jurisdiction<>1
qui utilisent des données non numériques pour ce champ.
Le arrest_id
champ était vide pour toutes les lignes avec citing_jurisdiction=1
.
Il s'agit d'un ordinateur portable personnel haut de gamme (il y a 1 an). Je suis le seul utilisateur. Aucune autre requête ou opération n'était en cours d'exécution. Le verrouillage semble peu probable.
Il n'y a aucun déclencheur nulle part dans cette table ou ailleurs dans la base de données.
Les autres opérations sur cette base de données ne prennent jamais un temps abornmal. Avec une indexation appropriée, les SELECT
requêtes sont généralement assez rapides.
la source
Seq Scan
un peu effrayant ...Réponses:
J'ai eu quelque chose de similaire récemment avec un tableau de 3,5 millions de lignes. Ma mise à jour ne se terminerait jamais. Après beaucoup d'expériences et de frustration, j'ai finalement trouvé le coupable. Il s'est avéré qu'il s'agissait des index de la table en cours de mise à jour.
La solution consistait à supprimer tous les index de la table en cours de mise à jour avant d'exécuter l'instruction de mise à jour. Une fois que j'ai fait cela, la mise à jour s'est terminée en quelques minutes. Une fois la mise à jour terminée, j'ai recréé les index et j'ai repris mes activités. Cela ne vous aidera probablement pas à ce stade, mais il se peut que quelqu'un d'autre cherche des réponses.
Je garderais les index sur la table dont vous tirez les données. Celui-ci n'aura pas à mettre à jour les index et devrait vous aider à trouver les données que vous souhaitez mettre à jour. Il fonctionnait bien sur un ordinateur portable lent.
la source
Votre plus gros problème est de faire d'énormes quantités de travail en écriture et en recherche sur un disque dur d'ordinateur portable. Cela ne sera jamais rapide, peu importe ce que vous faites, surtout si c'est le type de lecteur 5400RPM plus lent livré avec de nombreux ordinateurs portables.
TrueCrypt ralentit les choses plus que "un peu" pour les écritures. Les lectures seront raisonnablement rapides, mais les écritures rendent RAID 5 plus rapide. L'exécution d'une base de données sur un volume TrueCrypt sera une torture pour les écritures, en particulier les écritures aléatoires.
Dans ce cas, je pense que vous perdriez votre temps à essayer d'optimiser la requête. Vous réécrivez la plupart des lignes de toute façon, et ça va être lent avec votre horrible situation d'écriture. Ce que je recommanderais, c'est de:
Je soupçonne que ce sera plus rapide que de simplement supprimer et recréer les contraintes, car une MISE À JOUR aura des modèles d'écriture assez aléatoires qui tueront votre stockage. Deux insertions en masse, l'une dans une table non enregistrée et l'autre dans une table enregistrée WAL sans contraintes, seront probablement plus rapides.
Si vous avez des sauvegardes absolument à jour et que cela ne vous dérange pas de devoir restaurer votre base de données à partir de sauvegardes, vous pouvez également redémarrer PostgreSQL avec le
fsync=off
paramètre etfull_page_writes=off
temporairement pour cette opération en bloc. Tout problème inattendu comme une panne de courant ou un crash du système d'exploitation laissera votre base de données irrécupérable pendantfsync=off
.L'équivalent POSTGreSQL de «pas de journalisation» consiste à utiliser des tables non enregistrées. Ces tables non enregistrées sont tronquées si la base de données s'arrête de manière incorrecte alors qu'elles sont sales. L'utilisation de tables non enregistrées réduira au moins de moitié votre charge d'écriture et réduira le nombre de recherches, afin qu'elles puissent être BEAUCOUP plus rapidement.
Comme dans Oracle, il peut être judicieux de supprimer un index puis de le recréer après une mise à jour par lots importante. Le planificateur de PostgreSQL ne peut pas déterminer qu'une grosse mise à jour a lieu, suspendre les mises à jour d'index, puis reconstruire l'index à la fin; même si c'était le cas, il lui serait très difficile de déterminer à quel moment cela valait la peine, surtout à l'avance.
la source
HOT
il est préférable de laisser les index en place. Sinon, vous voudrez probablement supprimer et recréer. La colonne n'est pas indexée, mais pour pouvoir effectuer une mise à jour HOT, il doit également y avoir de l'espace libre sur la même page, cela dépend donc un peu de la quantité d'espace mort dans la table. Si c'est surtout en écriture, je dirais de supprimer tous les index. S'il s'agit de lots mis à jour, il pourrait y avoir des trous et vous pourriez être OK. Des outils commepageinspect
etpg_freespacemap
peuvent aider à déterminer cela.Quelqu'un donnera une meilleure réponse pour Postgres, mais voici quelques observations d'un point de vue Oracle qui peuvent s'appliquer (et les commentaires sont trop longs pour le champ de commentaire).
Ma première préoccupation serait d'essayer de mettre à jour 2 millions de lignes en une seule transaction. Dans Oracle, vous écririez une image avant chaque bloc mis à jour afin que les autres sessions aient toujours une lecture cohérente sans lire vos blocs modifiés et vous avez la possibilité de revenir en arrière. C'est un long retour en arrière qui se construit. Il est généralement préférable de faire les transactions en petits morceaux. Dites 1 000 enregistrements à la fois.
Si vous avez des index sur la table et que la table sera considérée comme hors service pendant la maintenance, il est souvent préférable de supprimer les index avant une grosse opération, puis de la recréer à nouveau par la suite. Moins cher, puis essayant constamment de maintenir les index avec chaque enregistrement mis à jour.
Oracle autorise les astuces "no logging" sur les instructions pour arrêter la journalisation. Il accélère beaucoup les déclarations, mais laisse votre base de données dans une situation "irrécupérable". Vous souhaitez donc effectuer une sauvegarde avant, puis une nouvelle sauvegarde immédiatement après. Je ne sais pas si Postgres a des options similaires.
la source
VACUUM
n'est que la moitié de la réponse; PostgreSQL utilise également un soi-disant "journal d'avance" (en fait un journal) pour fournir des validations atomiques et se protéger contre les écritures partielles, etc. Voir postgresql.org/docs/current/static/wal-intro.html