PostgreSQL 在线修改数据类型 - online ddl 方法之一

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: 标签PostgreSQL , online ddl , trigger , ddl 事务背景有张表的主键id是serial,但现在不够了,需要升级成bigserial,有什么优雅的方法吗?我看下来好像会锁表很久(因为数据量挺大)如果直接alter table,由于数据类型从4字节改成了8字节,而tuple结构是在METADATA里面的,不是每行都有,所以DEFORM需要依赖METADATA,目前来说,这种操作需要rewrite table。

标签

PostgreSQL , online ddl , trigger , ddl 事务


背景

有张表的主键id是serial,但现在不够了,需要升级成bigserial,有什么优雅的方法吗?我看下来好像会锁表很久(因为数据量挺大)

如果直接alter table,由于数据类型从4字节改成了8字节,而tuple结构是在METADATA里面的,不是每行都有,所以DEFORM需要依赖METADATA,目前来说,这种操作需要rewrite table。

那么如何做到改bigserial,整个过程不堵塞dml呢?

有一张父表 bigtable,两个字段 id int primary key, x bigint,以及两张子表 child1 和 child2,数据量在一亿以上,通过脚本,可以改成 bigint 。整个过程不会锁住表,能正常CRUD。

alter sequence bigtable_id_seq as bigint;  
  
alter table bigtable add column new_id bigint;  
create unique index concurrently on bigtable (new_id);  
create unique index concurrently on child1 (new_id);  
create unique index concurrently on child2 (new_id);  
  
create or replace function id_handler() returns trigger as $$  
begin  
  new.new_id = new.id;  -- id 是int4类型 , newid是int8类型   
  return new;  
end;  
$$ language plpgsql;  
create trigger id_trigger before insert on bigtable for each row execute procedure id_handler();  
create trigger id_trigger before insert on child1 for each row execute procedure id_handler();  
create trigger id_trigger before insert on child2 for each row execute procedure id_handler();  
  
-- 更新历史数据  
update bigtable set new_id = id where 分段; -- 分解成多条  
-- update bigtable set new_id = id where id between 1 and 10000 and new_id<>id;  
-- update bigtable set new_id = id where id between 10001 and 20000 and new_id<>id;  
-- ...  
  
-- 历史数据更新完成后,切换ID字段  
  
begin;  
alter table bigtable  
  alter column id drop default,  
  alter column new_id set default nextval('bigtable_id_seq'::regclass);  
  
create or replace function id_handler() returns trigger as $$  
begin  
  new.id = new.new_id;   -- 业务上不会直接去写ID的值。  
  return new;  
end;  
$$ language plpgsql;  
end;  
  
update pg_catalog.pg_attribute set attnotnull = true where attrelid in (select oid from pg_catalog.pg_class where relname in ('bigtable', 'child1', 'child2')) and attname = 'new_id';  
  
alter table bigtable  
  drop constraint bigtable_pkey,  
  add constraint bigtable_pkey primary key using index bigtable_new_id_idx;  
alter table child1  
  drop constraint child1_pkey,  
  add constraint child1_pkey primary key using index child1_new_id_idx;  
alter table child2  
  drop constraint child2_pkey,  
  add constraint child2_pkey primary key using index child2_new_id_idx;  
  
begin;  
alter table bigtable rename id to old_id;  
alter table bigtable rename new_id to id;  
alter table bigtable alter column old_id drop not null;  
drop trigger id_trigger on bigtable;  
drop trigger id_trigger on child1;  
drop trigger id_trigger on child2;  
commit;  
  
alter sequence bigtable_id_seq owned by bigtable.id;  
  
alter table bigtable drop column old_id;  

之前唯一卡住的是要把新的列设置成主键时,会自动设置 set not null,这一步会锁表,然后检查是否有 null 值;但通过之前的步骤,其实没有 null 值了,所以我就手工修改了 attributes 这张表。

update pg_catalog.pg_attribute set attnotnull = true where attrelid in (select oid from pg_catalog.pg_class where relname in ('bigtable', 'child1', 'child2')) and attname = 'new_id';  

明天我前同事会在线上用 17亿数据测试一下效果,如果也能顺利完成的话,那就可以考虑做成一个通用的脚本了

注意:

直接修改pg_catalog.pg_attribute元数据,注意一下prepared statement,看看PS缓存是否会更新,直接改元数据的方式的情况下,可能约束检查不一定会实时传播给其他会话,比如业务上直接写NULL值。

因为前面已经设置了 new_id 的默认值,本身是不会出现 null 值的。除非用户在SQL中可能直接写null,否则这个值是不会为NULL的。

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
4天前
|
XML JSON 关系型数据库
PostgreSQL支持多种数据类型
PostgreSQL支持多种数据类型
7 1
|
23天前
|
SQL 关系型数据库 Linux
在CentOS 6上安装和使用PostgreSQL的方法
在CentOS 6上安装和使用PostgreSQL的方法
14 2
|
23天前
|
Ubuntu 关系型数据库 数据库
在Ubuntu 18.04上安装和使用PostgreSQL的方法
在Ubuntu 18.04上安装和使用PostgreSQL的方法
29 1
|
23天前
|
Ubuntu 关系型数据库 Linux
在Ubuntu 14.04上安装和使用PostgreSQL的方法
在Ubuntu 14.04上安装和使用PostgreSQL的方法
12 1
|
7天前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
63 0
|
23天前
|
关系型数据库 Linux 数据库
在CentOS 7上安装和使用PostgreSQL的方法
在CentOS 7上安装和使用PostgreSQL的方法
34 0
|
4月前
|
SQL 关系型数据库 MySQL
postgresql |数据库 |数据库的常用备份和恢复方法总结
postgresql |数据库 |数据库的常用备份和恢复方法总结
218 0
|
12月前
|
SQL NoSQL 关系型数据库
PostgreSQL 准确且快速的数据对比方法
作为一款强大而广受欢迎的开源关系型数据库管理系统,PostgreSQL 在数据库领域拥有显著的市场份额。其出色的可扩展性、稳定性使其成为众多企业和项目的首选数据库。而在很多场景下(开发 | 生产环境同步、备份恢复验证、数据迁移、数据合并等),不同环境中的数据库数据可能导致数据的不一致,因此,进行数据库之间的数据对比变得至关重要。
310 0
|
存储 关系型数据库 PostgreSQL
PostgreSQL表扫描方法解析
PostgreSQL表扫描方法解析
99 0
|
存储 NoSQL 关系型数据库
PostgreSQL 12的可拔插存储引擎--表访问方法以及bloackholes案例
PostgreSQL 12的可拔插存储引擎--表访问方法以及bloackholes案例
162 0

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 下一篇
    DDNS