将 BIG 表上的列数据类型从 numeric(18, 2) 更改为 numeric(22, 6)

Moh*_*eem 7 postgresql datatypes alter-table postgresql-performance

我们有一个非常大的POSTGRES表,包含超过80 亿行,并且以非常高的速度增长(每天 3000 万行)。

我们的表是按日期分区的。每个分区包含6 个月的数据,除了第一个分区包含近18 个月的数据。

Postgres 版本:PostgreSQL 12.11

数据库规格: db.r6g.16xlarge (vCPU:64,内存:512,EBSBandwidth(Mbps):19000)

表架构:

create table table_1
(
    id       bigint default nextval('table_1_id_seq'::regclass) not null,
    user_id  integer                                            not null,
    amount   numeric(18, 2)                                     not null,
    date     timestamp                                          not null,
    column_1 varchar                                            not null,
    column_2 varchar,
    .
    .
    primary key (id, date)
) partition by RANGE (date);
Run Code Online (Sandbox Code Playgroud)

该表还包含多个索引(包括部分索引

create index index_1
    on table_1 (column_1);

create index index_2
    on table_1 (date, column_2, column_3);

create index index_3
    on table_1 (column_4);

create index index_4
    on table_1 (user_id, column_3);

create index index_5
    on table_1 (user_id asc, date desc)
    where (column_3 = ANY (ARRAY [1, 2, 3, 6, 7, 8, 10]));

create index index_6
    on table_1 (user_id, column_2, column_3, column_5);
Run Code Online (Sandbox Code Playgroud)

最初,我们创建了数据类型为 numeric(18, 2) 的amount列。现在我们需要支持高达 6 的更高精度。因此我们需要将类型更改为numeric(22, 6)

现在运行如下所示的Alter 命令需要花费大量时间(数小时):

注意:我们在表上运行此命令,没有删除任何索引

ALTER TABLE table_1 ALTER COLUMN amount type numeric(22, 6);
Run Code Online (Sandbox Code Playgroud)

我们正在探索的几种不同策略:

  1. 更新运行时删除所有索引触发器外键,并在最后重新创建它们。

  2. 创建新表

    A。创建一个具有完全相同配置的新分区表,

    b. 将数据从旧表迁移到新表。

    C。在新表上创建所有索引

    d. 删除旧表。

注意:我们的主要目标是最大限度地减少停机时间。如果 更大的数据库实例可以提供帮助,我们也愿意对此进行探索。

我们想探索是否有其他策略来完成这项任务,或者在我们当前的策略中,我们可以采取哪些额外不同的措施来最大限度地减少停机时间

dez*_*zso 8

我要做的如下:

  • 添加具有所需数据类型的新列
  • 更改应用程序逻辑,以便它同时写入旧列和新列 - 这样新行将在新列中填充数据
  • 将值从旧列复制到新列。逐步执行此操作,以便系统仍然可以正常运行(可能比平时慢一些)。您很有可能希望VACUUM ANALYZE在更新一定数量的行后执行 a 操作。
  • 更新所有行后,您可以执行一对ALTER TABLE操作来将同一事务中的两列重命名(新的为amount,旧的为其他)。这通常是一个非常快的操作。预先检查应用程序是否可以在所有读取路径上很好地处理数据类型更改。
  • 在应用程序端删除对旧列的写入后,最终删除旧列

根据我的经验,这是您必须以尽可能短的停机时间进行计划的方式。更改的总时间将比简单的数据类型更改要长得多。另一方面,如果出现一些问题,您可以恢复到旧列直到最后。显然,事先在测试环境中测试该过程。

注1:正如评论中提到的,您有可能无法在交换新列的同时更改应用程序代码。在这种情况下,您将需要ALTER TABLE在事务中使用三个语句:

amount -> amount_old
amount_new -> amount
amount_old -> amount_new (looks confusing first, but the application will be happy)
Run Code Online (Sandbox Code Playgroud)

然后更改应用程序代码以使用更高的精度amount和 的原始精度amount_new

注意2:如果您碰巧使用较旧的Postgres版本(最多10个),请添加新列而不使用默认值,否则整个表将被重写。在更高版本中,这种情况仅在不稳定的默认值下发生- 检查您的用例是否与这两种情况匹配。

  • 关于*“`ALTER TABLE`用于同时重命名两列”*:它只能在同一命令中重命名*单个*列。使用包含在单个事务中的两个命令。请参阅:/sf/answers/1629245201/ (2认同)