假设我在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 太。但是,当然,我不希望我的应用程序因为这些更改而出现任何错误。
如果我执行以下步骤(在单个事务中)以执行所需的更改:
我是否获得了所需的行为(应用程序中没有错误)?
在整个交易过程中是否会在 vwfoo 上持有独占锁(这就是我想要的)?
是否有可能任何事务将尝试使用步骤1和3之间的视图然后失败(而不仅仅是阻塞,等待锁定)?
vwfoo 的“身份”在重新创建时是否会发生变化?换句话说:是否有可能任何事务将尝试使用步骤1和3之间的视图,阻止,在步骤3之后恢复然后因为重新创建视图而失败?
感谢。
答案 0 :(得分:1)
快速测试表明这会导致您的应用程序出现问题。要重新创建,请创建两个连接(A
和B
),然后运行:
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;