web-dev-qa-db-ja.com

ALTER TABLEでデータベースが「凍結」

テーブルを変更して実際に列を追加するとき、私たちの実稼働環境は今朝しばらくフリーズしました*。

問題のあるSQL:ALTER TABLE cliente ADD COLUMN topicos character varying(20)[];

*システムにログインするには、同じテーブルから選択する必要があるため、テーブルの変更中は誰もログインできません。システムを通常の操作に戻すには、プロセスを強制終了する必要がありました。


テーブル構造:

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

CONSTRAINTS、TRIGGERS、またはその他のものを無効にする必要がありますか?

おそらくDBのチューニングですか?

さらなる分析のために他に何を提供すべきですか?

バージョン:gccでコンパイルされたx86_64-unknown-linux-gnu上のPostgreSQL 9.4.5(Debian 4.9.2-10)4.9.2、64ビット

16
Gonzalo Vasquez

DDL操作は通常、操作対象のオブジェクトをロックするため、計画されたメンテナンスウィンドウ(ユーザーが中断を予期している場合、またはシステムが計画された時間まで完全にオフラインになることを予期している場合)以外では実行しないでください-何もできませんこれについて簡単に1

一部の操作は書き込みロックのみを保持するため、アプリケーションは影響を受けるオブジェクトのみを読み取るリクエストを処理し続けることができます。

ドキュメンテーションは、DDL操作によって保持される可能性が高いロックをリストするのに非常に適しているようです。

このブログエントリ には、列がnull可能で、デフォルト値または一意の制約がない場合、列の追加がオンライン操作である可能性があることを示唆する概要がありますロックなしで実行します(IIRC postgresは、明示的に指定しない限り、列をデフォルトでNULL可能にするため)。列の追加後に他の操作を実行しましたか?おそらくそれにインデックスを作成します(デフォルトでテーブルに書き込みロックがかかります)?

1一部のレプリケーション/クラスタリング/ミラーリングの配置では、ミラーの更新(変更中に更新を一時停止し、後で再生する)、そのコピーをライブコピーとして使用するように切り替えるなど、各コピーが更新されるまで切り替えることができます。ダウンタイムは、DDL操作中に行われた変更を再生するのにかかる時間に制限されます。ただし、そのようなライブ運用にはリスクがないわけではないので、どうしてもできない場合を除いて、適切なメンテナンスウィンドウを用意して、構造の更新を実行および確認することをお勧めします。

9
David Spillett

実行するコマンドは、テーブルに対するACCESS EXCLUSIVEロックを取得し、そのテーブルへの他のすべてのアクセスを防止します。ただし、このロックの期間は数ミリ秒である必要があります。追加する列のような列を追加する場合、テーブルを書き直す必要はなく、メタデータを更新する必要があるだけです。

問題が発生する可能性がある場所、そしてそれがあなたが見ている問題だとドーナツに多額のお金を賭けているのは、優先順位がロックされていることです。誰かがそのテーブルにACCESS SHAREロックのような弱いロックを持っていて、それらは無期限にその上でキャンプしています(たぶんリークされたアイドル状態のトランザクション接続?psqlを開いた誰かが繰り返し可能な読み取りモードでクエリを開始しました、そして休暇に行きましたか?).

ADD COLUMNは必要なACCESS EXCLUSIVEを取得しようとし、最初のロックの後ろにキューイングします。

これで、将来のすべてのロック要求が、待機中のACCESS EXCLUSIVE要求の後ろにキューイングされます。

概念的には、既に付与されたロックと互換性のある着信ロック要求は、待機中のACCESS EXCLUSIVEを飛び越えて順番に付与される可能性がありますが、PostgreSQLはそれを行いません。

あなたは長命の弱いロックを保持しているプロセスを見つける必要があります。

これを行うには、pg_locksテーブルをクエリします。

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

すべてがロックされているときにこれを行うと、1つの回答しか得られません(長命の原因が複数ある場合を除く)。 ADD COLUMNを強制終了した後でこれを行うと、多数の許可されたロックが表示される可能性がありますが、それを数回繰り返すと、毎回1つまたはいくつかが残っているはずです。

次に、pg_lockから取得したPIDを取得し、それをpg_stat_activityに照会して、違反者が何をしているかを確認できます。

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;

そのため、トランザクション内でクエリを実行し、トランザクションを閉じずにアイドル状態になりました。現在13:13なので、5分間アイドル状態です。

35
jjanes