Base de données «gelée» sur ALTER TABLE


15

Notre environnement de production s'est figé * ce matin pendant un certain temps lors de la modification d'une table, l'ajout d'une colonne en fait.

SQL incriminé:ALTER TABLE cliente ADD COLUMN topicos character varying(20)[];

* La connexion à notre système nécessite une sélection dans cette même table, donc personne ne pouvait se connecter pendant la table alter. Nous avons dû interrompre le processus pour permettre au système de reprendre ses opérations normales.


Structure de la table:

CREATE TABLE cliente
(
  rut character varying(30) NOT NULL,
  nombre character varying(150) NOT NULL,
  razon_social character varying(150) NOT NULL,
  direccion character varying(200) NOT NULL,
  comuna character varying(100) NOT NULL,
  ciudad character varying(100) NOT NULL,
  codigo_pais character varying(3) NOT NULL,
  activo boolean DEFAULT true,
  id serial NOT NULL,
  stock boolean DEFAULT false,
  vigente boolean DEFAULT true,
  clase integer DEFAULT 1,
  plan integer DEFAULT 1,
  plantilla character varying(15) DEFAULT 'WAYPOINT'::character varying,
  facturable integer DEFAULT 1,
  toolkit integer DEFAULT 0,
  propietario integer DEFAULT 0,
  creacion timestamp without time zone DEFAULT now(),
  codelco boolean NOT NULL DEFAULT false,
  familia integer DEFAULT 0,
  enabled_machines boolean DEFAULT false,
  enabled_canbus boolean DEFAULT false,
  enabled_horometro boolean DEFAULT false,
  enabled_comap boolean DEFAULT false,
  enabled_frio boolean DEFAULT false,
  enabled_panico boolean DEFAULT false,
  enabled_puerta boolean DEFAULT false,
  enabled_rpm boolean DEFAULT false,
  enabled_supervisor integer DEFAULT 0,
  demo boolean,
  interno boolean,
  mqtt_enable boolean NOT NULL DEFAULT false,
  topicos character varying(20)[],
  CONSTRAINT pk_cliente PRIMARY KEY (rut),
  CONSTRAINT fk_cliente_familiaid FOREIGN KEY (familia)
      REFERENCES cliente_familia (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT pk_pais FOREIGN KEY (codigo_pais)
      REFERENCES pais (codigo) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT unique_id_cliente UNIQUE (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE cliente
  OWNER TO waypoint;
GRANT ALL ON TABLE cliente TO waypoint;
GRANT ALL ON TABLE cliente TO waypointtx;
GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE cliente TO waypointtomcat;
GRANT SELECT ON TABLE cliente TO waypointphp;
GRANT SELECT ON TABLE cliente TO waypointpphppublic;
GRANT ALL ON TABLE cliente TO waypointsoporte;
GRANT SELECT, INSERT ON TABLE cliente TO waypointsalesforce;
GRANT SELECT ON TABLE cliente TO waypointadminuser;
GRANT SELECT ON TABLE cliente TO waypointagenda;
GRANT SELECT ON TABLE cliente TO waypointmachines;
GRANT SELECT ON TABLE cliente TO waypointreports;
GRANT SELECT ON TABLE cliente TO readonly;

CREATE INDEX index_cliente
  ON cliente
  USING btree
  (rut COLLATE pg_catalog."default");

CREATE INDEX index_cliente_activo
  ON cliente
  USING btree
  (activo);

CREATE INDEX index_cliente_id_activo
  ON cliente
  USING btree
  (id, activo);

CREATE INDEX index_cliente_rut_activo
  ON cliente
  USING btree
  (rut COLLATE pg_catalog."default", activo);


CREATE TRIGGER trigger_default_admin
  AFTER INSERT
  ON cliente
  FOR EACH ROW
  EXECUTE PROCEDURE crea_default_admin();

CREATE TRIGGER trigger_default_grupo
  AFTER INSERT
  ON cliente
  FOR EACH ROW
  EXECUTE PROCEDURE crea_default_clientegrupo();  

Dois-je désactiver les CONTRAINTES, LES DÉCLENCHEURS ou autre chose?

Peut-être un réglage DB?

Que dois-je fournir d'autre pour une analyse plus approfondie?

Version: PostgreSQL 9.4.5 sur x86_64-unknown-linux-gnu, compilé par gcc (Debian 4.9.2-10) 4.9.2, 64 bits


Tant qu'une instruction DDL est en cours d'exécution, la table est verrouillée et inaccessible. Vous ne pouvez rien y faire.
a_horse_with_no_name

enfin, pas aussi sympa que prévu, mais tout à fait compréhensible;)
Gonzalo Vasquez

Réponses:


8

Les opérations DDL verrouillent généralement l'objet sur lequel elles agissent, elles ne doivent donc pas être effectuées en dehors des fenêtres de maintenance planifiées (lorsque vos utilisateurs s'attendent à une interruption ou que le système soit complètement hors ligne pendant une durée planifiée) - vous ne pouvez rien faire à ce sujet facilement 1 .

Certaines opérations ne gardent qu'un verrou en écriture, de sorte que votre application peut continuer à servir des requêtes qui ne lisent que les objets affectés.

La documentation semble assez bonne pour répertorier les verrous susceptibles d'être détenus par les opérations DDL.

Cette entrée de blog a un résumé qui suggère que l'ajout d'une colonne peut être une opération en ligne si la colonne est nullable et n'a pas de valeur par défaut ou de contrainte unique, bien que cela implique que l'instruction que vous déclarez aurait dû être exécutée sans verrous (comme postgres IIRC la valeur par défaut des colonnes est NULLable, sauf indication contraire explicite). Avez-vous exécuté d'autres opérations après la colonne d'ajout? Peut-être créer un index dessus (ce qui prendrait un verrou en écriture sur la table par défaut)?

1 Certaines dispositions de réplication / clustering / mise en miroir vous permettraient de mettre à jour un miroir (suspendre les mises à jour pendant le changement et les relire après), passer à l'utilisation de cette copie comme live, et ainsi de suite jusqu'à ce que chaque copie soit mise à jour, donc le temps d'arrêt est limité au temps nécessaire à la relecture des modifications apportées au cours de l'opération DDL. Les opérations en direct comme celles-ci ne sont pas sans risque, donc à moins que vous ne puissiez pas le faire, il est recommandé d'organiser une fenêtre de maintenance appropriée pour effectuer et vérifier les mises à jour structurelles dans.


35

La commande que vous souhaitez exécuter prend un verrou ACCESS EXCLUSIVE sur la table, empêchant tout autre accès à cette table. Mais la durée de ce verrouillage ne devrait être que de quelques millisecondes, car l'ajout d'une colonne comme celle que vous souhaitez ajouter ne nécessite pas la réécriture de la table, il suffit de mettre à jour les métadonnées.

Là où le problème peut survenir, et je vous parie des dollars en beignets que c'est le problème que vous voyez, c'est dans les priorités de verrouillage. Quelqu'un a un verrou faible, comme le verrou ACCESS SHARE, sur cette table, et ils campent dessus indéfiniment (peut-être une connexion inactive dans la transaction qui a été divulguée? Quelqu'un qui a ouvert psql, a lancé une requête dans un mode de lecture répétable, et puis est parti en vacances?).

L'ADD COLUMN essaie de prendre l'ACCÈS EXCLUSIF dont il a besoin et il fait la queue derrière le premier verrou.

Maintenant, toutes les futures demandes de verrouillage sont placées en file d'attente derrière la demande ACCESS EXCLUSIVE en attente.

Conceptuellement, les demandes de verrouillage entrantes qui sont compatibles avec le verrou déjà accordé pourraient sauter par-dessus l'ACCESS EXCLUSIVE en attente et être accordées hors tour, mais ce n'est pas ainsi que PostgreSQL le fait.

Vous devez trouver le processus qui maintient le verrou faible de longue durée.

Vous pouvez le faire en interrogeant la table pg_locks.

select * from pg_locks where 
    granted and relation = 'cliente'::regclass \x\g\x

Si vous faites cela alors que tout est enfermé, vous ne devriez obtenir qu'une seule réponse (à moins qu'il y ait plusieurs coupables de longue durée). Si vous faites cela après avoir déjà tué la colonne ADD COLUMN, vous pouvez voir de nombreux verrous accordés, mais si vous le répétez plusieurs fois, il devrait y en avoir un ou quelques-uns qui restent à chaque fois.

Vous pouvez ensuite prendre le PID que vous avez obtenu de pg_lock et interroger celui-ci dans pg_stat_activity pour voir ce que fait le délinquant:

select * from pg_stat_activity where pid=28731 \x\g\x

...

backend_start    | 2016-03-22 13:08:30.849405-07
xact_start       | 2016-03-22 13:08:36.797703-07
query_start      | 2016-03-22 13:08:36.799021-07
state_change     | 2016-03-22 13:08:36.824369-07
waiting          | f
state            | idle in transaction
backend_xid      |
backend_xmin     |
query            | select * from cliente limit 4;

Ainsi, il a exécuté une requête, à l'intérieur d'une transaction, puis est devenu inactif sans jamais fermer la transaction. Il est maintenant 13h13, donc ils sont inactifs depuis 5 minutes.


6
Cette réponse m'a sauvé la vie
Mahendra

1
lock prioritiesJ'ai également sauvé la mienne, la partie sur était très bonne, car je n'ai pas lu à ce sujet dans d'autres endroits, merci!
Edson Horacio Junior
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.