Datenbank "eingefroren" in ALTER TABLE

14

Unsere Produktionsumgebung fror heute Morgen für eine Weile ein, als sie eine Tabelle veränderte und tatsächlich eine Spalte hinzufügte.

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

* Für die Anmeldung in unserem System ist eine Auswahl aus derselben Tabelle erforderlich, sodass sich während der Änderungstabelle niemand anmelden kann. Wir mussten den Prozess tatsächlich abbrechen, damit das System den normalen Betrieb wieder aufnehmen konnte.


Tabellenstruktur:

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();  

Sollte ich CONSTRAINTS, TRIGGERS oder etwas anderes deaktivieren?

Vielleicht irgendein DB Tuning?

Was sollte ich sonst noch für weitere Analysen bereitstellen?

Version: PostgreSQL 9.4.5 auf x86_64-unknown-linux-gnu, kompiliert von gcc (Debian 4.9.2-10) 4.9.2, 64-Bit

Gonzalo Vasquez
quelle
Solange eine DDL-Anweisung ausgeführt wird, ist die Tabelle gesperrt und es kann nicht auf sie zugegriffen werden. Daran können Sie nichts ändern.
a_horse_with_no_name
Nun, nicht so schön es erwartet, aber absolut verständlich;)
Gonzalo Vasquez

Antworten:

8

DDL-Vorgänge sperren normalerweise das Objekt, auf das sie einwirken, und sollten daher nicht außerhalb der geplanten Wartungsfenster ausgeführt werden (wenn Ihre Benutzer eine Unterbrechung erwarten oder das System für einen geplanten Zeitraum vollständig offline ist) - Sie können nichts tun dazu leicht 1 .

Einige Vorgänge behalten nur eine Schreibsperre bei, sodass Ihre Anwendung weiterhin Anforderungen verarbeiten kann, die nur die betroffenen Objekte lesen.

Die Dokumentation scheint ziemlich gut darin zu sein, aufzulisten, welche Sperren wahrscheinlich von DDL-Operationen gehalten werden.

Dieser Blogeintrag enthält eine Zusammenfassung, aus der hervorgeht, dass das Hinzufügen einer Spalte eine Onlineoperation sein kann, wenn die Spalte nullwertfähig ist und keinen Standardwert oder keine eindeutige Einschränkung aufweist. Dies impliziert jedoch, dass die von Ihnen angegebene Anweisung ohne Sperren hätte ausgeführt werden müssen (als IIRC-Postgres) Standardmäßig sind Spalten NULL-fähig, sofern Sie nicht ausdrücklich etwas anderes angeben. Haben Sie nach dem Hinzufügen der Spalte noch weitere Vorgänge ausgeführt? Vielleicht einen Index dafür erstellen (was standardmäßig eine Schreibsperre für die Tabelle bedeuten würde)?

1 Bei einigen Replikations- / Clustering- / Spiegelungsanordnungen können Sie einen Spiegel aktualisieren (Aktualisierungen während der Änderung anhalten und anschließend wiedergeben), diese Kopie als Live-Kopie verwenden usw., bis jede Kopie aktualisiert wird Die Ausfallzeit ist auf die Zeit begrenzt, die zum Wiederholen der während des DDL-Vorgangs vorgenommenen Änderungen benötigt wird. Derartige Live-Vorgänge sind jedoch nicht ohne Risiko. Sofern Sie dies nicht unbedingt können, wird empfohlen, stattdessen ein ordnungsgemäßes Wartungsfenster einzurichten, in dem strukturelle Aktualisierungen durchgeführt und überprüft werden.

David Spillett
quelle
35

Der Befehl, den Sie ausführen möchten, nimmt eine ACCESS EXCLUSIVE-Sperre für die Tabelle an, wodurch jeder andere Zugriff auf diese Tabelle verhindert wird. Die Dauer dieser Sperre sollte jedoch nur wenige Millisekunden betragen, da für das Hinzufügen einer Spalte wie der gewünschten keine erneute Erstellung der Tabelle erforderlich ist, sondern lediglich die Aktualisierung der Metadaten.

Wo das Problem auftauchen kann und ich wette, dass es das Problem ist, das Sie sehen, das Geld für Doughnuts ist, ist vorrangig geregelt. Jemand hat eine schwache Sperre, wie die ACCESS SHARE-Sperre, für diese Tabelle und sie kampieren auf unbestimmte Zeit darauf (möglicherweise eine inaktive Verbindung, die durchgesickert ist? Jemand, der psql geöffnet hat, hat eine Abfrage in einem wiederholbaren Lesemodus gestartet. und dann in den urlaub gefahren?).

Die ADD COLUMN versucht, den von ihr benötigten ACCESS EXCLUSIVE zu nehmen, und sie steht hinter der ersten Sperre in der Warteschlange.

Jetzt stehen alle zukünftigen Sperranforderungen hinter der wartenden Anforderung ACCESS EXCLUSIVE in der Warteschlange.

Konzeptionell könnten eingehende Sperranforderungen, die mit der bereits erteilten Sperre kompatibel sind, über den wartenden ACCESS EXCLUSIVE springen und außer Kontrolle geraten, aber so macht es PostgreSQL nicht.

Sie müssen den Prozess finden, der die langlebige schwache Sperre hält.

Sie können dies tun, indem Sie die Tabelle pg_locks abfragen.

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

Wenn Sie dies tun, während alles eingesperrt ist, sollten Sie nur eine Antwort erhalten (es sei denn, es gibt mehrere langlebige Schuldige). Wenn Sie dies tun, nachdem Sie die ADD COLUMN bereits beendet haben, sehen Sie möglicherweise viele gewährte Sperren. Wenn Sie dies jedoch ein paarmal wiederholen, sollten jeweils eine oder mehrere Sperren verbleiben.

Sie können dann die PID, die Sie von pg_lock erhalten haben, in pg_stat_activity abfragen, um zu sehen, was der Täter tut:

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;

Es wurde also eine Abfrage innerhalb einer Transaktion ausgeführt und anschließend in den Leerlauf versetzt, ohne die Transaktion jemals zu schließen. Es ist jetzt 13:13 Uhr, sie waren also 5 Minuten lang untätig.

jjanes
quelle
5
Diese Antwort hat mein Leben gerettet
Mahendra
1
Hab auch meins gerettet, der Teil über lock prioritieswar sehr gut, weil ich an anderen Stellen nicht darüber gelesen habe, danke!
Edson Horacio Junior