在没有任何停机时间的情况下将 PostgresQL 中的 `int` 迁移到 `bigint`?

Migrating `int` to `bigint` in PostgresSQL without any downtime?

我有一个数据库,它将体验 Basecamp 在 11 月著名地面对的 integer exhaustion problem。我有几个月的时间来弄清楚该怎么做。

是否有无需停机的主动解决方案来迁移此列类型?如果有,那是什么?如果不是,是否只是吃掉停机时间并尽可能迁移列的问题?

this article sufficient,假设我有几个days/weeks来执行迁移现在,然后我运行被迫这样做] 没有 ID?

使用logical replication.

通过逻辑复制,您可以在主数据库和备用数据库中拥有不同的数据类型。

使用 pg_dump -s 复制架构,更改副本上的数据类型,然后开始逻辑复制。

复制完所有数据后,将应用程序切换为使用备用。

对于零停机时间,应用程序必须能够重新连接并重试,但在这种情况下,这始终是一项要求。

您需要 PostgreSQL v10 或更高版本,以及您的数据库

  • 不应修改架构,因为 DDL 未被复制。
  • 不应使用序列(SERIALIDENTITY),因为最后使用的值不会是 replicated

创建旧 table 的副本,但修改了 ID 字段。接下来在旧 table 上创建一个触发器,将新数据插入到两个 table 中。最后将数据从旧的 table 复制到新的(如果是顺序的,最好用 post-trigger 区分预触发数据,例如通过 id)。完成后切换 tables 并删除旧的。

这显然需要两倍的 space(和复制时间),但无需 任何 停机时间即可工作。

v10 之前的数据库的另一种解决方案所有事务都很短:

  • bigint 列添加到 table。

  • 创建一个 BEFORE 触发器,每当添加或更新行时设置新列。

  • 运行 一系列更新,将新列设置为 IS NULL。保持这些批次较短,这样您就不会长时间锁定并且不会死锁太多。确保这些交易 运行 与 session_replication_role = replica 这样它们就不会触发触发器。

  • 更新所有行后,在新列上创建唯一索引CONCURRENTLY

  • 为刚刚创建的索引添加唯一约束USING。那会很快的。

  • 执行切换:

    BEGIN;
    ALTER TABLE ... DROP oldcol;
    ALTER TABLE ... ALTER newcol RENAME TO oldcol;
    COMMIT;
    

    那会很快。

您的新列没有 NOT NULL 集。如果没有长侵入锁,这是无法完成的。但是您可以添加检查约束 IS NOT NULL 并创建它 NOT VALID。这已经足够好了,您稍后可以在不中断的情况下对其进行验证。

如果有外键约束,事情会变得有点复杂。您必须删除这些并为新列创建 NOT VALID 个外键。