【问题标题】:Serial column takes up disproportional amount of space in PostgreSQL串行列在 PostgreSQL 中占用了不成比例的空间量
【发布时间】:2021-10-23 05:37:33
【问题描述】:

我想在 PostgreSQL 表中创建一个不是主键的自动递增 id 列。该表目前刚刚超过 2 亿行,包含 14 列。

SELECT pg_size_pretty(pg_total_relation_size('mytable'));

上面的查询显示mytable 在磁盘上占用了57 GB。在检查 df -h(在 Ubuntu 20.04 上)后,我目前在磁盘上剩余 30 GB 可用空间

我不明白为什么,在尝试创建 SERIAL 列后,我完全耗尽了磁盘空间 - 查询最终永远不会完成。我运行以下命令:

ALTER TABLE mytable ADD COLUMN id SERIAL;

然后看看我的磁盘空间是如何逐渐耗尽的,直到没有任何剩余并且查询失败。我不是数据库专家,但这没有意义。为什么一个简单的序列化列会占用表本身一半以上的空间,尤其是当它不是主键因此没有索引时?是否有已知的解决方法来创建这种自动递增的 id 列?

【问题讨论】:

  • 与您的空间问题完全无关,但是:使用串行is discouraged 支持符合标准的identity
  • 谢谢,这很有帮助,但是我应该如何实现标识列来达到我的目标?
  • 您的空间不足,因为SERIAL 基本上是一个执行此操作的宏Serial。结果是表的重写。要提出解决方案,我们需要知道您希望通过新列实现什么目标?

标签: postgresql indexing primary-key auto-increment


【解决方案1】:

添加serial 列是添加具有非常量DEFAULT 值的integer 列。这将导致 PostgreSQL 重写表,因为必须将新列值添加到所有现有行。因此 PostgreSQL 会写入表的新副本,并在完成后丢弃旧的副本。这将暂时需要原始表的两倍以上磁盘空间,这解释了为什么您会用完磁盘空间。

您可以将操作分成几个步骤:

ALTER TABLE mytable ADD id bigint;
CREATE SEQUENCE mytable_id_seq OWNED BY mytable.id;
ALTER TABLE mytable ALTER id SET DEFAULT nextval('mytable_id_seq');

这不会重写表,并且不会改变现有的行。这些列的id 的值为NULL。

您可能希望将现有行更新为NOT NULL,但要小心:如果一次全部更新,您也会耗尽磁盘空间,因为在 PostgreSQL 中,UPDATE 会写入一个全新的表中的行版本。您必须批量更新行并在这些更新之间运行VACUUM

总而言之,这是相当烦人和复杂的。所以帮自己一个忙,增加磁盘空间。这是最简单也是最好的解决方案。

【讨论】:

    【解决方案2】:

    作为概念证明:

    create table id_test(pk_fld integer primary key generated always as identity);
    --FYI, in Postgres 14+ the overriding system value won't be needed.
    --That is a hack around a bug in 13-
    insert into id_test overriding system value values (default), (default);
    select * from id_test;
     pk_fld 
    --------
          1
          2
    alter table id_test add column id_fld integer ;
    update id_test set id_fld = 0;
    alter table id_test alter COLUMN id_fld set not null;
    alter table id_test alter COLUMN id_fld add generated always as identity;
    update id_test set id_fld = default;
    select * from id_test;
    pk_fld | id_fld 
    --------+--------
          1 |      1
          2 |      2
    

    基本上,这会将流程分解为多个步骤。显然,这只是一个玩具桌,并不代表您的设置。我会在作为您实际表的子集的测试表上尝试它,以查看磁盘空间消耗会发生什么。在更新后使用VACUUM 将行返回到数据库不会有什么坏处。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-11
      • 2015-03-21
      • 2014-12-13
      • 2020-10-11
      • 2023-03-12
      • 2020-10-29
      • 1970-01-01
      相关资源
      最近更新 更多