【问题标题】:Will changing column from timestamp to timestamptz lock the table?将列从时间戳更改为时间戳会锁定表吗?
【发布时间】:2015-12-05 11:46:18
【问题描述】:

我想将一列从timestamp(无时区)迁移到timestamptz 类型。

我使用的是 Postgres 9.3.9。

我需要知道此操作是否会导致表重写(锁定表),因为我的表很大并且数据库处于活动状态。

我在9.2 release notes 中找到了这个:

增加 varchar 或 varbit 列的长度限制,或完全取消限制,不再需要重写表。同样,增加数值列的允许精度,或将列从受约束的数值更改为不受约束的数值,不再需要重写表。在涉及 interval、timestamp 和 timestamptz 类型的类似情况下,也可以避免表重写。

这听起来很有希望,但实际上并没有详细说明“类似案例”可能是什么。

如果此操作 将锁定表,我将不胜感激有关如何在实时数据库上解决此问题而不中断服务的建议。

【问题讨论】:

    标签: postgresql concurrency timestamp postgresql-9.3 alter-table


    【解决方案1】:

    首先,您似乎对锁和表重写感到困惑。发行说明中的​​说明谈到了 table rewrites - 总是在桌面上使用 ACCESS EXCLUSIVE lock。但是这里还有许多其他操作也需要锁定。

    你需要:

    ALTER TABLE tbl ALTER ts_col TYPE timestamptz;
    

    除非您想在转换中设置特定时区,而不是会话的当前时区:

    ALTER TABLE tbl ALTER ts_col TYPE timestamptz USING ts_col AT TIME ZONE 'Europe/London';
    

    请务必在这种情况下使用时区名称不是简单的偏移量或缩写。详情:

    The documentation:

    ALTER TABLE 更改现有表的定义。有 下面介绍几个子窗体。请注意,所需的锁定级别 每个子窗体可能不同。持有ACCESS EXCLUSIVE 锁,除非 明确指出。

    ALTER column_name TYPE data_type 采用了这样的 ACCESS EXCLUSIVE 锁定。虽然timestamptimestamptz 的内部存储格式 相同,但内部 通常会因转换而改变(取决于会话的时区设置! )。 Postgres 必须为表中的每一行编写一个新版本,因此这也需要表重写。由于该操作使用了 ACCESS EXCLUSIVE,因此无需保留旧的行版本,并且在转换后您不会看到死元组。

    SQL Fiddle 演示了时区设置对转换的作用。我还添加了一个将varchar 转换为text 的示例,这不需要 需要重写表 - 除非您移动到更短的长度修饰符。
    请注意我如何转换输出到 text (ts_col::text) 以防止 sqlfiddle 中的 JDBC 层添加更多(通常是不需要的!)表示魔法。

    在您的事务开始后尝试访问表的并发事务将等待直到该事务完成。

    可以尝试通过在后台准备新表、删除旧表并重命名新表来缩短锁定时间,但这会导致并发事务失败并出现如下错误:

    错误:无法打开与 OID 123456 的关系

    详情:

    timestamp / timestamptz 的“类似情况”

    varcharnumeric timestamp, time and interval types 允许修饰符。例如,时间戳在默认情况下最多存储 6 位小数秒,但您可以修改:timestamp(0) 不存储小数秒。

    varchar(10) -> varchar(20) 的转换不需要重写表,因为源类型中的值也保证适合(二进制兼容)目标类型。

    timestamp (0) -> timestamptimestamptz(3) -> timestamptz(5) 也是如此。这就是手册在quoted passage in the release notes 中所指的内容:

    在涉及 intervaltimestamptimestamptz 类型。

    【讨论】:

    • 所以...如果我在更改列之前执行SET LOCAL timezone='UTC'; 存储值将保持不变...我现在意识到我的问题是:在这种特殊情况下,它仍然会导致表重写?
    • @Anentropic:是的,设置timezone='UTC' 会产生相同的内部。由于转换后数据类型为timestamptz,但文本representation会发生变化,并取决于当前时区设置。
    • 我是否应该从您的 sql fiddle 中了解到,因为 xmin 值发生了变化,即使在本地是 UTC 并且存储值因此保持不变的情况下,行数据仍然被重写?因此在所有情况下更改列类型都可能导致表重写?
    • @Anentropic:是的。 Postgres 不会挑选一种特殊情况。我终于成功地在小提琴中添加了另一个演示(站点似乎超载 ATM)。一个varchar -> text 的转换,你可以看到xmin 没有改变。 varchar(10) -> varchar(20) 也不行。但是对于 varchar(20) -> varchar(10) 它会发生变化(该值可能会被截断)。
    • @Anentropic:我又添加了一章来解决您关于手册中段落的子项。
    猜你喜欢
    • 2021-08-12
    • 2018-11-27
    • 2013-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    相关资源
    最近更新 更多