将列从 timestamp 更改为 timestamptz 是否会锁定 table?

Will changing column from timestamp to timestamptz lock the table?

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

我正在使用 Postgres 9.3.9。

我需要知道此操作是否会导致 table 重写(锁定 table),因为我的 table 很大并且数据库是实时的。

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

Increasing the length limit for a varchar or varbit column, or removing the limit altogether, no longer requires a table rewrite. Similarly, increasing the allowable precision of a numeric column, or changing a column from constrained numeric to unconstrained numeric, no longer requires a table rewrite. Table rewrites are also avoided in similar cases involving the interval, timestamp, and timestamptz types.

这听起来很有希望,但实际上并没有详细说明 'similar cases' 可能是什么。

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

首先,您似乎混淆了锁和 table 重写。发行说明中的​​注释谈到 table 重写 - 总是 [= =44=] 在 table 上。但是这里还有许多其他操作也需要锁。

你需要:

ALTER TABLE tbl ALTER ts_col TYPE timestamptz;

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

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

在这种情况下一定要使用时区名称不是一个简单的偏移量也不是缩写。详情:

  • Time zone names with identical properties yield different result when applied to timestamp
  • Ignoring timezones altogether in Rails and PostgreSQL

The documentation:

ALTER TABLE changes the definition of an existing table. There are several subforms described below. Note that the lock level required may differ for each subform. An ACCESS EXCLUSIVE lock is held unless explicitly noted.

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

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

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

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

ERROR: could not open relation with OID 123456

详情:

"Similar cases" 对于 timestamp / timestamptz

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

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

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

Table rewrites are also avoided in similar cases involving the interval, timestamp, and timestamptz types.