大表中完全空的列如何影响性能?

ebi*_*ebi 13 postgresql performance database-design storage disk-space postgresql-performance

我在 Postgres 数据库中有 4 亿行,表有 18 列:

id serial NOT NULL,
a integer,
b integer,
c integer,
d smallint,
e timestamp without time zone,
f smallint,
g timestamp without time zone,
h integer,
i timestamp without time zone,
j integer,
k character varying(32),
l integer,
m smallint,
n smallint,
o character varying(36),
p character varying(100),
q character varying(100)
Run Code Online (Sandbox Code Playgroud)

ekn都是 NULL,它们根本不存储任何值,此时完全没用。它们是原始设计的一部分,但从未被移除。

编辑 - 大多数其他列都是非 NULL。

问题:

  1. 如何计算这对存储的影响?它是否等于列的大小 * 行数?

  2. 删除这些空列会显着提高该表的性能吗?页面缓存能够容纳更多行吗?

Erw*_*ter 17

由于列ekn可以为 NULL,我假设“100% 空”表示 NULL。

NULL 存储很便宜。每个 NULL 在空位图中“消耗”一位用于存储,否则几乎不影响性能。有效的存储要求取决于每行的空位图是否已经存在并且是否还有空间容纳 3 个位。

在当前实现中,具有最多8 个用户列的表在空位图的元组标头之后使用一个备用字节。再多一点,另一个MAXALIGN(通常是 8 个)字节被分配给另外64 个列(现在总共72 个)。等等。因此,对于大多数表(包括您的具有18列的表),空位图每行有效地消耗 8 个字节。

空位图是在每一行完全分配或根本不分配,并且仅当至少有一个实际 NULL 值时。如果定义了所有列NOT NULL,则永远不会有一个列。

在没有其他更重要的考虑因素的情况下,将始终(或大部分)为 NULL 的列移动到行的末尾。对性能有一点帮助。

回答问题 1。

有效的存储要求是:

  • 每行0 个字节,其中其他列也为 NULL。
  • 每行8 个字节(通常),其中没有其他列为 NULL,用于分配空位图

回答问题 2。

放弃不会给你带来太多好处。页面缓存将相同或略少取决于 1。处理查询、备份、源代码等将得到简化。

进一步阅读:

你没有要求的

经过一轮“列俄罗斯方块”后,我建议使用此表格布局 - 再次排除其他更重要的考虑因素:

  id serial NOT NULL
, a  integer
, g  timestamp
, i  timestamp
, b  integer
, c  integer
, h  integer
, j  integer
, l  integer
, d  smallint
, f  smallint
, m  smallint
, o  varchar(36)
, p  varchar(100)
, q  varchar(100)
, k  varchar(32)      --  always NULL
, n  smallint         --  always NULL
, e  timestamp        --  always NULL
Run Code Online (Sandbox Code Playgroud)

与原始布局相比,每行至少节省 16 个字节,可能大约 20+,具体取决于您的varchar列。“列俄罗斯方块”的说明在这里:

如何对列重新排序

最简单的方法是创建一个新表,将数据复制到它。然后你会得到一个没有死列(和死行)的原始新表。您也可以对流程中行的物理顺序进行排序(集群)。

也可以进行适当的更改,但是没有方便的工具(我知道)通常可以重新排序列的顺序。依赖对象,如视图、函数(依赖)、FK 约束、索引等是这里的限制因素。这些可能会阻止您删除列,您必须删除并重新创建依赖对象。

当然,对表的并发访问与更改它的 DDL 命令发生冲突。在这种情况下,您需要排他表锁。

删除完全空的列并将它们添加到行的末尾既简单又便宜。切换表中间填充列的顺序并不那么简单或便宜。我会为此创建一个新表。

这个相关的答案有食谱和更多链接: