Nosso ambiente de produção congelou * esta manhã por um tempo ao alterar uma tabela, adicionando uma coluna na verdade.
SQL ofensivo:ALTER TABLE cliente ADD COLUMN topicos character varying(20)[];
* O login em nosso sistema requer uma seleção da mesma tabela, para que ninguém possa fazer login durante a alteração da tabela. Na verdade, tivemos que interromper o processo para permitir que o sistema retomasse as operações normais.
Estrutura da tabela:
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();
Devo desativar CONSTRAINTS, TRIGGERS ou outra coisa?
Talvez qualquer DB Tuning?
O que mais devo fornecer para uma análise mais aprofundada?
Versão: PostgreSQL 9.4.5 no x86_64-unknown-linux-gnu, compilado pelo gcc (Debian 4.9.2-10) 4.9.2, 64 bits
postgresql
postgresql-9.4
alter-table
blocking
Gonzalo Vasquez
fonte
fonte
Respostas:
As operações DDL geralmente bloqueiam o objeto em que estão atuando; portanto, não devem ser executadas fora das janelas de manutenção planejada (quando os usuários esperam interrupções ou o sistema ficar completamente offline por um período planejado) - não há nada que você possa fazer sobre isso facilmente 1 .
Algumas operações mantêm apenas um bloqueio de gravação, para que seu aplicativo possa continuar atendendo solicitações que apenas leem os objetos afetados.
A documentação parece bastante boa para listar quais bloqueios provavelmente serão mantidos pelas operações DDL.
Esta entrada de blog possui um resumo que sugere que adicionar uma coluna pode ser uma operação online se a coluna for anulável e não tiver um valor padrão ou restrição exclusiva, embora isso implique que a instrução que você declara deveria ter sido executada sem bloqueios (como IIRC postgres o padrão é que as colunas sejam NULLable, a menos que você indique explicitamente o contrário). Você executou outras operações após a coluna adicionar? Talvez criando um índice nele (o que levaria um bloqueio de gravação na tabela por padrão)?
1 Algumas disposições de replicação / clustering / espelhamento permitem atualizar um espelho (pausar atualizações durante a alteração e reproduzi-las depois), passar a usar essa cópia como a ao vivo e assim por diante até que cada cópia seja atualizada. o tempo de inatividade é limitado ao tempo necessário para reproduzir as alterações feitas durante a operação DDL. Porém, operações ativas como essa não são isentas de riscos; portanto, a menos que você não possa, é recomendável que você organize uma janela de manutenção adequada para executar e verificar as atualizações estruturais.
fonte
O comando que você deseja executar possui um bloqueio ACCESS EXCLUSIVE na tabela, impedindo todos os outros acessos a essa tabela. Mas a duração desse bloqueio deve ser de apenas alguns milissegundos, pois adicionar uma coluna como a que você deseja adicionar não exige que a tabela seja reescrita, apenas exige que os metadados sejam atualizados.
Onde o problema pode surgir, e aposto em dólares para rosquinhas que esse é o problema que você está vendo, está nas prioridades. Alguém tem um bloqueio fraco, como o bloqueio ACCESS SHARE, nessa tabela, e eles estão acampando nele indefinidamente (talvez uma conexão inativa na transação que vazou? Alguém que abriu o psql, iniciou uma consulta em um modo de leitura repetível, e depois saiu de férias?).
A ADD COLUMN tenta usar o ACCESS EXCLUSIVE de que precisa e fica na fila atrás do primeiro bloqueio.
Agora, todas as solicitações de bloqueio futuras ficam na fila atrás da solicitação ACCESS EXCLUSIVE em espera.
Conceitualmente, solicitações de bloqueio de entrada compatíveis com o bloqueio já concedido podem pular o ACCESS EXCLUSIVE em espera e ser atendidas fora do turno, mas não é assim que o PostgreSQL faz isso.
Você precisa encontrar o processo que está mantendo o bloqueio fraco de longa duração.
Você pode fazer isso consultando a tabela pg_locks.
Se você fizer isso enquanto tudo estiver bloqueado, deverá obter apenas uma resposta (a menos que haja vários culpados de longa data). Se você fizer isso depois de já ter matado a ADD COLUMN, poderá ver vários bloqueios concedidos, mas se repetir algumas vezes, deve haver um ou alguns que permanecem sempre.
Você pode então pegar o PID obtido de pg_lock e consultá-lo em pg_stat_activity para ver o que o agressor está fazendo:
...
Então, ele executou uma consulta, dentro de uma transação, e ficou ocioso sem nunca fechar a transação. Agora são 13:13, então eles ficaram inativos por 5 minutos.
fonte
lock priorities
foi muito boa, porque eu não li sobre isso em outros lugares, obrigado!