web-dev-qa-db-fra.com

Améliorez les performances de la MISE À JOUR sur une grande table

J'utilise Postgres 9.5 sur Amazon RDS (2vCPU, 8 Go de RAM).
J'utilise pganalyze pour surveiller mes performances.
J'ai environ 200 000 enregistrements dans la base de données.

Dans mon tableau de bord, je vois que les requêtes suivantes prennent en moyenne 28 et 11 secondes pour s'exécuter:

UPDATE calls SET ... WHERE calls.uuid = ?   telephonist 28035.41    0.01    100%    0.03%

UPDATE calls SET sip_error = ? WHERE calls.uuid = ? telephonist 11629.89    0.44    100%    0.69%

J'ai déjà essayé VACUUM, trouvé et nettoyé 7 670 lignes mortes.
Avez-vous des idées pour améliorer les performances de UPDATE? Voici la requête:

UPDATE calls SET X=Y WHERE calls.uuid = 'Z'

Comment puis-je améliorer la requête ci-dessus? Puis-je ajouter un autre champ? Exemple:

UPDATE calls SET X=Y WHERE calls.uuid = 'Z' AND calls.campaign = 'W'

La colonne uuid n'est pas indexée.
https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm suggère que les index ne sont pas recommandés pour les opérations UPDATE.

CREATE TABLE public.calls (
    id int4 NOT NULL DEFAULT nextval('calls_id_seq'::regclass),
    callsid varchar(128),
    call_start timestamp(6) NOT NULL,
    call_end timestamp(6) NULL,
    result int4 DEFAULT 0,
    destination varchar(256),
    campaign varchar(128),
    request_data varchar(4096),
    uuid varchar(128) NOT NULL,
    status varchar(64),
    duration int4,
    recording_file varchar(256),
    recording_url varchar(256),
    recording_duration int4,
    recording_text varchar(4096),
    recording_download bool DEFAULT false,
    description varchar(4096),
    analysis varchar(4096),
    is_fax bool DEFAULT false,
    is_test bool,
    hangup_cause varchar(128),
    media_detected bool DEFAULT false,
    sip_callid varchar(256),
    hangup_cause_override varchar(256),
    is_blacklisted bool DEFAULT false,
    sip_error varchar(256),
    hangup_cause_report varchar(128),
    summary varchar(1024)
);
EXPLAIN ANALYZE 
    SELECT * FROM calls 
    WHERE calls.uuid='e2ce9eb4-v1lp-p14u-7kkk-lruy-e2ceaae46d';
 Scan Seq sur les appels (coût = 0,00..16716,25 lignes = 1 largeur = 3301) 
 (Temps réel = 81,637..81,637 lignes = 0 boucles = 1) 
 Filtre: ((uuid) :: text = 'e2ce9eb4-v1lp-p14u-7kkk-lruy-e2ceaae46d' :: text) 
 Lignes supprimées par filtre: 99970 
 Temps de planification: 0,482 ms 
 Temps d'exécution: 81,683 ms 
5
californian

En supposant la colonne uuid est censée être UNIQUE, cette définition de table devrait économiser de l'espace et améliorer les performances:

CREATE TABLE public.calls (
   id serial PRIMARY KEY,
   result int4 DEFAULT 0 NOT NULL,
   uuid uuid UNIQUE NOT NULL  -- creates the index you need automatically
   call_start timestamp NOT NULL,
   call_end timestamp,  -- so this can be NULL?
   duration int4,
   recording_duration int4,
   callsid varchar(128),
   destination varchar(256),
   campaign varchar(128),
   request_data varchar(4096),
   status varchar(64),
   recording_file varchar(256),
   recording_url varchar(256),
   recording_text varchar(4096),
   recording_download bool DEFAULT false,
   description varchar(4096),
   analysis varchar(4096),
   is_fax bool DEFAULT false,
   is_test bool,
   hangup_cause varchar(128),
   media_detected bool DEFAULT false,
   sip_callid varchar(256),
   hangup_cause_override varchar(256),
   is_blacklisted bool DEFAULT false,
   sip_error varchar(256),
   hangup_cause_report varchar(128),
   summary varchar(1024)
);

La caractéristique la plus importante ici est la contrainte UNIQUE, qui est implémentée avec un index unique , et un index est ce dont vous avez besoin plus que toute autre chose (comme @ ypercube déjà commenté ).

Si uuid n'est pas unique, créez un index btree simple dessus.

Si uuid n'est pas un uuid valide, laissez-le comme type de caractère (varchar ou text), mais créez toujours cet index.

Considérations relatives à la taille et aux performances pour le type de données varchar et uuid:

Toutes mes autres modifications suggérées sont des améliorations mineures. Explication détaillée ici:

Si vous n'avez pas besoin d'appliquer une longueur maximale particulière, j'utiliserais simplement text pour toutes vos colonnes de caractères. Mais cela n'a pratiquement aucun effet immédiat sur les performances. Certaines des colonnes peuvent être converties en un type plus approprié (avec un réel avantage en termes de performances).


Index et UPDATE

Alors pourquoi cette page de tutoriel dit:

Quand faut-il éviter les index?
[...]
- Tables qui ont des opérations de mise à jour ou d'insertion de lots fréquentes et importantes.

C'est trompeur par omission. Vous désespérément avez besoin d'un index sur uuid pour prendre en charge le prédicat de votre mise à jour. Tous les autres index ralentissent votre mise à jour, car ils nécessitent un travail supplémentaire pour les garder à jour après une mise à jour. Donc, si vous n'avez aucune utilité pour le PRIMARY KEY on id (pour autoriser les contraintes FK par exemple), vous pouvez supprimer cela (et faire de uuid le PK à la place - également indexé automatiquement).

7