ALTER TABLE上的数据库“冻结”


15

今天早上,我们的生产环境只是冻结*一段时间,当更改表格时,实际上添加了一列。

令人反感的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();  

我应该禁用约束,触发器或其他功能吗?

也许有任何数据库优化?

我还应该提供什么进一步分析?

版本:x86_64-unknown-linux-gnu上的PostgreSQL 9.4.5,由gcc(Debian 4.9.2-10)4.9.2编译,64位


只要DDL语句正在运行,表就被锁定并且无法访问。您对此无能为力。
a_horse_with_no_name

好吧,不是预期的那么好,但绝对可以理解;)
贡萨洛·瓦斯奎兹

Answers:


8

DDL操作通常会锁定其作用的对象,因此不应在计划的维护时段之外执行操作(当用户期望中断或系统在计划的时间内完全脱机时)-您无能为力关于这个容易1

某些操作仅保持写锁定,因此您的应用程序可以保留仅读取受影响对象的请求。

该文档似乎很好地列出了DDL操作可能持有的锁。

该博客文章的摘要建议,如果该列可为空并且没有默认值或唯一约束,则添加列可以是一种在线操作,尽管这意味着您声明的语句应该没有锁运行(如IIRC postgres除非您另外明确声明,否则默认情况下会将列设置为NULLable。在添加列之后,您是否还执行其他任何操作?也许在其上创建一个索引(默认情况下会在表上执行写锁定)?

1 一些复制/群集/镜像安排将允许您更新镜像(在更改过程中暂停对它的更新,然后重播它们),切换到将该副本用作实时副本,依此类推,直到更新每个副本为止,因此停机时间仅限于重放DDL操作期间所做的更改所花费的时间。像这样的实时操作并不是没有风险的,因此,除非绝对不能这样做,否则建议您安排一个适当的维护时段来执行和验证结构更新。


35

您希望运行的命令确实对表进行了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

如果在一切都被锁定的情况下执行此操作,则您只会得到一个答案(除非有多个长期存在的罪魁祸首)。如果在杀死ADD COLUMN之后执行此操作,则可能会看到很多已授予的锁,但是如果重复几次,则每次应保留一个或几个。

然后,您可以获取从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分钟。


6
这个答案挽救了我的生命
Mahendra

1
也保存了我的,关于这一部分的内容lock priorities非常好,因为我在其他地方还没有读过,谢谢!
Edson Horacio Junior
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.