postgreSQL 同时将列类型从 int 更改为 bigint
postgreSQL concurrently change column type from int to bigint
我有一个相当大的 table(大约 10 亿行),我需要将 id 类型从 SERIAL
更新为 BIGSERIAL
;猜猜为什么?:)。
基本上这可以用这个命令来完成:
execute "ALTER TABLE my_table ALTER COLUMN id SET DATA TYPE bigint"
然而,这将永远锁定我的 table 并使我的 Web 服务停止运行。
是否有一种非常简单的方法可以同时执行此操作(无论需要多长时间)?
如果您没有指向您的 ID 的外键,您可以添加新列,填充它,删除旧列并将新列重命名为旧列:
alter table my_table add column new_id bigint;
begin; update my_table set new_id = id where id between 0 and 100000; commit;
begin; update my_table set new_id = id where id between 100001 and 200000; commit;
begin; update my_table set new_id = id where id between 200001 and 300000; commit;
begin; update my_table set new_id = id where id between 300001 and 400000; commit;
...
create unique index my_table_pk_idx on my_table(new_id);
begin;
alter table my_table drop constraint my_table_pk;
alter table my_table alter column new_id set default nextval('my_table_id_seq'::regclass);
update my_table set new_id = id where new_id is null;
alter table my_table add constraint my_table_pk primary key using index my_table_pk_idx;
alter table my_table drop column id;
alter table my_table rename column new_id to id;
commit;
Radek 的解决方案看起来很棒。如果我有这方面的声誉,我会添加评论,但我只想提一下,如果您这样做,您可能还想扩大主键的序列。
ALTER SEQUENCE my_table_id_seq AS bigint;
如果您只是扩大列类型,如果序列仍然是整数大小,当您达到 20 亿条记录时,您仍然会遇到问题。
我认为 James 指出的关于添加需要 table 扫描的主键的问题可以通过 NOT VALID/VALIDATE 舞蹈来解决。你可以做
而不是 alter table my_table add constraint my_table_pk primary key using index my_table_pk_idx;
ALTER TABLE my_table ADD UNIQUE USING INDEX my_table_pk_idx;
ALTER TABLE my_table ADD CONSTRAINT my_table_id_not_null CHECK (id IS NOT NULL) NOT VALID;
ALTER TABLE my_table VALIDATE CONSTRAINT my_table_id_not_null;
我认为还值得一提的是
create unique index my_table_pk_idx on my_table(new_id);
将执行完全 table 扫描,并在 my_table 上使用独占锁。最好做
CREATE UNIQUE INDEX CONCURRENTLY ON my_table(new_id);
我尝试了@radek-postołowicz 解决方案,但它对我来说失败了,因为我需要将 new_id 列设置为非空,并且长时间锁定 table .
我的解决方案:
- Select记录旧的table,插入到新的table
my_table_new
中,id为bigint。 运行 这是一个独立的交易。
- 在另一个事务中:对可能同时创建的记录再次执行步骤 1),删除
my_table
并将 my_table_new
重命名为 my_table
.
此解决方案的缺点是它 auto-scaled 我的 AWS RDS 的存储,并且无法缩减。
我有一个相当大的 table(大约 10 亿行),我需要将 id 类型从 SERIAL
更新为 BIGSERIAL
;猜猜为什么?:)。
基本上这可以用这个命令来完成:
execute "ALTER TABLE my_table ALTER COLUMN id SET DATA TYPE bigint"
然而,这将永远锁定我的 table 并使我的 Web 服务停止运行。
是否有一种非常简单的方法可以同时执行此操作(无论需要多长时间)?
如果您没有指向您的 ID 的外键,您可以添加新列,填充它,删除旧列并将新列重命名为旧列:
alter table my_table add column new_id bigint;
begin; update my_table set new_id = id where id between 0 and 100000; commit;
begin; update my_table set new_id = id where id between 100001 and 200000; commit;
begin; update my_table set new_id = id where id between 200001 and 300000; commit;
begin; update my_table set new_id = id where id between 300001 and 400000; commit;
...
create unique index my_table_pk_idx on my_table(new_id);
begin;
alter table my_table drop constraint my_table_pk;
alter table my_table alter column new_id set default nextval('my_table_id_seq'::regclass);
update my_table set new_id = id where new_id is null;
alter table my_table add constraint my_table_pk primary key using index my_table_pk_idx;
alter table my_table drop column id;
alter table my_table rename column new_id to id;
commit;
Radek 的解决方案看起来很棒。如果我有这方面的声誉,我会添加评论,但我只想提一下,如果您这样做,您可能还想扩大主键的序列。
ALTER SEQUENCE my_table_id_seq AS bigint;
如果您只是扩大列类型,如果序列仍然是整数大小,当您达到 20 亿条记录时,您仍然会遇到问题。
我认为 James 指出的关于添加需要 table 扫描的主键的问题可以通过 NOT VALID/VALIDATE 舞蹈来解决。你可以做
而不是alter table my_table add constraint my_table_pk primary key using index my_table_pk_idx;
ALTER TABLE my_table ADD UNIQUE USING INDEX my_table_pk_idx;
ALTER TABLE my_table ADD CONSTRAINT my_table_id_not_null CHECK (id IS NOT NULL) NOT VALID;
ALTER TABLE my_table VALIDATE CONSTRAINT my_table_id_not_null;
我认为还值得一提的是
create unique index my_table_pk_idx on my_table(new_id);
将执行完全 table 扫描,并在 my_table 上使用独占锁。最好做
CREATE UNIQUE INDEX CONCURRENTLY ON my_table(new_id);
我尝试了@radek-postołowicz 解决方案,但它对我来说失败了,因为我需要将 new_id 列设置为非空,并且长时间锁定 table .
我的解决方案:
- Select记录旧的table,插入到新的table
my_table_new
中,id为bigint。 运行 这是一个独立的交易。 - 在另一个事务中:对可能同时创建的记录再次执行步骤 1),删除
my_table
并将my_table_new
重命名为my_table
.
此解决方案的缺点是它 auto-scaled 我的 AWS RDS 的存储,并且无法缩减。