将 table 中的整数更改为 bigint 密钥,无需长排他锁
Change integer to bigint key in a table without long exclusive lock
在 PostgreSQL 9.1 中是否可以从具有相同类型 bigint
列的 table 继承具有类型 int
列的 table?
如果第一个 table 不是一个选项,则将列类型更改为 bigint
。
我有一个 table,其中包含数千万个 ID 为 int 的条目。现在这个ID慢慢接近2^32,我在想用bigint ID创建一个门面table,让原来的门面继承这个门面是否合理。这有意义吗?
不,当您从 table 继承时,无法更改父 table 的列的类型。
列类型必须匹配,因为当您查询父 table(在 FROM
子句中没有 ONLY
)时,PostgreSQL 隐式扫描子 tables也附上他们的结果。如果类型不同,它就无法做到这一点,就像您不能 UNION
table 不同类型一样。
根据评论更新:
使用视图和 DO INSTEAD
触发器
我建议使用一个视图,它是两个 table 的联合视图,旧 table 的内容向上转换为 bigint
。定义一个 INSTEAD OF
触发器,将 INSERT
重定向到新的 table.
如果您执行 UPDATE
s 和 DELETE
s,您可能应该在每个 table 上定义一个 CHECK
约束,将 ID 的范围限制为不重叠范围,然后根据 ID 决定将 DELETE
或 UPDATE
路由到哪个 table。
对于 UPDATE
,您甚至可以将其转换为 DELETE ... RETURNING
和 INSERT
(可能在 wCTE 中)以将行从旧的 table 移动到新的作为更新的一部分。
你 将 招致性能损失,但你将避免需要完全 table 重写。
逐步就地更改密钥类型
你说更改密钥类型不是一个选项,但你的意思似乎是 "changing the key type in a manner that requires a full table rewrite under an exclusive lock is not an option"。
你可以做的是:
ALTER TABLE ... ADD COLUMN new_key bigint;
。不要不要标记它NOT NULL
或给它一个DEFAULT
- 向 table 添加一个
BEFORE INSERT OR UPDATE ... BEFORE EACH ROW ...
触发器,将整数 ID 列复制到 bigint id 列,例如NEW.new_id := NEW.id
- 分批,
UPDATE
将table复制整数键到bigint列,每批后VACUUM
- 一旦所有新行和现有行都有一个 bigint 键,使用
create unique index ... concurrently
在其上创建一个唯一索引。
- 创建索引后,添加一个
not null
约束。不幸的是,这将进行顺序扫描以验证约束。如果你连这点都不能容忍,有一些技巧可以解决它,但我不准备在 public 中给他们建议,因为你需要 确切地 知道你在做什么这样做是为了安全地进行,并在适当的情况下使用它。
begin
一个事务,drop
触发器,drop
旧的主键约束和旧的id
列,并在上面添加一个新的主键约束bigint key,指定你并发创建的现有索引作为约束索引,然后commit
。这避免了在独占锁下建立索引的需要。
如果 PostgreSQL 支持将 not null
约束添加为 not valid
,则此过程会更好,然后让您使用较弱的锁对其进行验证。不幸的是,它还没有这样做。欢迎补丁或其他贡献。
alter table
为 PostgreSQL 9.5 上的某些操作采用的较弱的锁将使您受益匪浅。
理论上 PostgreSQL 可以通过在幕后完成所有这些工作来支持 alter table ... alter type ... concurrently
。不过要正确地完成它需要 很多 的工作,所以我预计在不久的将来不会看到一个简单的固定方法。
在 PostgreSQL 9.1 中是否可以从具有相同类型 bigint
列的 table 继承具有类型 int
列的 table?
如果第一个 table 不是一个选项,则将列类型更改为 bigint
。
我有一个 table,其中包含数千万个 ID 为 int 的条目。现在这个ID慢慢接近2^32,我在想用bigint ID创建一个门面table,让原来的门面继承这个门面是否合理。这有意义吗?
不,当您从 table 继承时,无法更改父 table 的列的类型。
列类型必须匹配,因为当您查询父 table(在 FROM
子句中没有 ONLY
)时,PostgreSQL 隐式扫描子 tables也附上他们的结果。如果类型不同,它就无法做到这一点,就像您不能 UNION
table 不同类型一样。
根据评论更新:
使用视图和 DO INSTEAD
触发器
我建议使用一个视图,它是两个 table 的联合视图,旧 table 的内容向上转换为 bigint
。定义一个 INSTEAD OF
触发器,将 INSERT
重定向到新的 table.
如果您执行 UPDATE
s 和 DELETE
s,您可能应该在每个 table 上定义一个 CHECK
约束,将 ID 的范围限制为不重叠范围,然后根据 ID 决定将 DELETE
或 UPDATE
路由到哪个 table。
对于 UPDATE
,您甚至可以将其转换为 DELETE ... RETURNING
和 INSERT
(可能在 wCTE 中)以将行从旧的 table 移动到新的作为更新的一部分。
你 将 招致性能损失,但你将避免需要完全 table 重写。
逐步就地更改密钥类型
你说更改密钥类型不是一个选项,但你的意思似乎是 "changing the key type in a manner that requires a full table rewrite under an exclusive lock is not an option"。
你可以做的是:
ALTER TABLE ... ADD COLUMN new_key bigint;
。不要不要标记它NOT NULL
或给它一个DEFAULT
- 向 table 添加一个
BEFORE INSERT OR UPDATE ... BEFORE EACH ROW ...
触发器,将整数 ID 列复制到 bigint id 列,例如NEW.new_id := NEW.id
- 分批,
UPDATE
将table复制整数键到bigint列,每批后VACUUM
- 一旦所有新行和现有行都有一个 bigint 键,使用
create unique index ... concurrently
在其上创建一个唯一索引。 - 创建索引后,添加一个
not null
约束。不幸的是,这将进行顺序扫描以验证约束。如果你连这点都不能容忍,有一些技巧可以解决它,但我不准备在 public 中给他们建议,因为你需要 确切地 知道你在做什么这样做是为了安全地进行,并在适当的情况下使用它。 begin
一个事务,drop
触发器,drop
旧的主键约束和旧的id
列,并在上面添加一个新的主键约束bigint key,指定你并发创建的现有索引作为约束索引,然后commit
。这避免了在独占锁下建立索引的需要。
如果 PostgreSQL 支持将 not null
约束添加为 not valid
,则此过程会更好,然后让您使用较弱的锁对其进行验证。不幸的是,它还没有这样做。欢迎补丁或其他贡献。
alter table
为 PostgreSQL 9.5 上的某些操作采用的较弱的锁将使您受益匪浅。
理论上 PostgreSQL 可以通过在幕后完成所有这些工作来支持 alter table ... alter type ... concurrently
。不过要正确地完成它需要 很多 的工作,所以我预计在不久的将来不会看到一个简单的固定方法。