【问题标题】:postgreSQL concurrently change column type from int to bigintpostgreSQL 同时将列类型从 int 更改为 bigint
【发布时间】:2016-02-03 22:50:16
【问题描述】:

我有一个相当大的表(大约 10 亿行),我需要将 id 类型从 SERIAL 更新为 BIGSERIAL;猜猜为什么?:)。

基本上这可以用这个命令来完成:

execute "ALTER TABLE my_table ALTER COLUMN id SET DATA TYPE bigint"

尽管如此,这将永远锁定我的桌子并关闭我的网络服务。

是否有一种非常简单的方法可以同时执行此操作(无论需要多长时间)?

【问题讨论】:

    标签: database postgresql concurrency migration


    【解决方案1】:

    Radek 的解决方案看起来很棒。如果我对此享有盛誉,我会添加评论,但我只想提一下,如果您这样做,您可能还希望扩大主键的序列。

    ALTER SEQUENCE my_table_id_seq AS bigint;
    

    如果你只是扩大列类型,如果序列仍然是整数大小,当你达到 20 亿条记录时,你仍然会遇到问题。

    我认为 James 指出的关于添加需要表扫描的主键的问题可以通过 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);
    

    将在 my_table 上使用排他锁进行全表扫描。这样做更好

    CREATE UNIQUE INDEX CONCURRENTLY ON my_table(new_id);
    

    【讨论】:

      【解决方案2】:

      如果您没有指向您的 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;
      

      【讨论】:

      • 谢谢,这个解决方案非常优雅。在我看来,仍然存在问题。当我们在填充 new_id 列时将新行插入到我们的表中时,不会设置 new_id 值并且唯一索引创建可能会失败。我们可以在插入期间添加一个触发器设置 new_id 直到我们在其上添加 nexval 默认值吗?
      • 唯一索引会忽略null的值,所以不需要触发。
      • 如果alter table my_table add column new_id bigint;耗时长(超过1小时还没完成),阻塞了其他读取操作怎么办?
      • 老实说:我不知道:)。也许问一个单独的问题,如何加快添加列本身的速度?
      • 当你说“如果你没有指向你的 id 的外键”,这是什么意思?您的意思是,如果任何其他表的列是该表的外键,则此解决方案将不起作用?
      猜你喜欢
      • 2021-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-19
      • 2020-06-10
      • 2017-04-16
      • 2017-04-23
      • 1970-01-01
      相关资源
      最近更新 更多