将列添加到表和视图时锁定

时间:2013-01-08 01:42:31

标签: database postgresql locking postgresql-9.1

假设我在PostgreSQL 9.1数据库中有这个 foo 表:

CREATE TABLE foo
(
  bar integer,
  flg_deleted boolean
);

vwfoo 视图:

CREATE VIEW vwfoo AS
  SELECT bar
  FROM foo
  WHERE flg_deleted = false;

还假设我有一个应用程序每秒运行几个短期事务,使用 vwfoo

现在,我想将 baz 列添加到 foo ,我希望 baz 位于 vwfoo 太。但是,当然,我不希望我的应用程序因为这些更改而出现任何错误。

如果我执行以下步骤(在单个事务中)以执行所需的更改:

  1. 删除 vwfoo
  2. baz 列添加到 foo
  3. 再次创建 vwfoo (现在包括 baz )。
  4. 我是否获得了所需的行为(应用程序中没有错误)?

    在整个交易过程中是否会在 vwfoo 上持有独占锁(这就是我想要的)?

    是否有可能任何事务将尝试使用步骤1和3之间的视图然后失败(而不仅仅是阻塞,等待锁定)?

    vwfoo 的“身份”在重新创建时是否会发生变化?换句话说:是否有可能任何事务将尝试使用步骤1和3之间的视图,阻止,在步骤3之后恢复然后因为重新创建视图而失败?

    感谢。

1 个答案:

答案 0 :(得分:1)

快速测试表明这会导致您的应用程序出现问题。要重新创建,请创建两个连接(AB),然后运行:

A: BEGIN;
A: DROP VIEW vwfoo;
B: SELECT * FROM vwfoo;
(B blocks… )
A: CREATE VIEW vwfoo AS SELECT * FROM foo;
A: COMMIT;
(B yields:
    ERROR:  could not open relation with OID 326418
    LINE 1: SELECT * FROM vwfoo
)

相反,您应该通过重命名视图来进行这种原子交换:

A: CREATE VIEW vwfoo_new AS SELECT * FROM foo;
A: BEGIN;
A: ALTER VIEW vwfoo RENAME TO vwfoo_old;
B: SELECT * FROM vwfoo;
(B blocks…)
A: ALTER VIEW vwfoo_new RENAME TO vwfoo;
A: COMMIT;
(B completes as expected)
A: DROP TABLE vwfoo_old;

这将按预期工作(您不需要在交易中运行(相对)昂贵的DROP TABLE!)

修改:您也可以使用相同的策略解决“真实”问题:

ALTER TABLE foo ADD COLUMN bar_new TEXT;
UPDATE foo SET bar_new = bar;
CREATE VIEW vwfoo_new AS SELECT bar_new AS bar FROM foo;
… do the view switcheroo …
DROP VIEW vwfoo_old;
BEGIN;
ALTER TABLE foo RENAME bar TO bar_old;
ALTER TABLE foo RENAME bar_new TO bar;
COMMIT;
ALTER TABLE foo DROP COLUMN bar_old;

引用bar_new的视图也会正确更新:

# \d vwfoo
View definition:
 SELECT foo.a
   FROM foo
  WHERE foo.b = false;
# ALTER foo RENAME a TO new_a;
ALTER TABLE
# \d vwfoo
View definition:
 SELECT foo.newa AS a
   FROM foo
  WHERE foo.b = false;