【问题标题】:Locks when adding a column to a table and to a view将列添加到表和视图时锁定
【发布时间】:2013-01-08 01:42:31
【问题描述】:

假设我在 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

现在,我想向 foo 添加一列 baz 并且我希望 bazvwfoo 中也。但是,当然,我不希望我的应用程序因为这些更改而出现任何错误。

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

  1. 放下vwfoo
  2. 将列 baz 添加到 foo
  3. 再次创建 vwfoo(现在包括 baz)。

我是否获得了所需的行为(应用程序中没有错误)?

在整个事务期间,vwfoo 上会不会有一个排他锁(这就是我想要的)?

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

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

谢谢。

【问题讨论】:

    标签: database postgresql locking postgresql-9.1


    【解决方案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;
    

    【讨论】:

    • 谢谢。虽然这不是我期望的答案,但这是我发布的问题的答案。 :) 不幸的是,我的现实有点复杂:我还需要将“foo.bar”(实际上是 VARCHAR(256))的类型更改为 TEXT。
    • 啊,好的!好吧,您也可以使用相同的策略(以及ALTER TABLE … RENAME …)来做到这一点:)这将涉及一个可能昂贵的副本然后DROP COLUMN,但您将有零停机时间!
    • 感谢您扩展您的答案。执行该复制不是我们的选择,因此我们的 DBA 决定直接在 pg_attribute 中增加 VARCHAR 列的大小。为了避免将来出现问题,我决定停止使用视图,因为它们的便利性不足以弥补它们带来的问题。再次,非常感谢。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多