【发布时间】:2023-03-12 13:48:01
【问题描述】:
我有一个包含 7 列的表,其中 5 列为空。我将在 int、text、date、boolean 和 money 数据类型上有一个空列。该表将包含数百万行,其中包含许多空值。恐怕空值会占空间。
另外,你知道 Postgres 是否索引空值吗?我想阻止它索引空值。
【问题讨论】:
标签: postgresql database-design indexing null
我有一个包含 7 列的表,其中 5 列为空。我将在 int、text、date、boolean 和 money 数据类型上有一个空列。该表将包含数百万行,其中包含许多空值。恐怕空值会占空间。
另外,你知道 Postgres 是否索引空值吗?我想阻止它索引空值。
【问题讨论】:
标签: postgresql database-design indexing null
基本上,NULL 值在 NULL 位图中占据 1 位。但事情没那么简单。
空位图(每行)仅在该行中至少有一列包含NULL 值时才分配。这可能会在具有 9 列或更多列的表中导致看似矛盾的效果:将第一个 NULL 值分配给列可能会比向其写入值占用更多的磁盘空间。相反,从行中删除最后一个 NULL 值也会删除 NULL 位图。
在物理上,初始空位图在HeapTupleHeader(23 个字节)和实际列数据或行OID(如果您仍然应该使用它)之间占据 1 个字节 - 其中 总是以 MAXALIGN 的倍数开始(通常是 8 字节)。这会留下 1 字节 的填充空间,供初始空位图使用。
实际上,对于 8 列或更少列的表,NULL 存储是完全免费的(包括已删除但尚未清除的列)。
之后,为接下来的MAXALIGN * 8 列(通常为 64)分配另一个MAXALIGN 字节(通常为 8)。等等。
更多详情in the manual 并在这些相关问题下:
一旦了解了数据类型的对齐填充,就可以进一步优化存储:
但您可以节省大量空间的情况很少见。通常这不值得。
@Daniel 已经涵盖了对索引大小的影响。
注意删除的列(虽然现在不可见)会保留在系统目录中,直到重新创建表。那些僵尸可以强制分配(放大的)NULL 位图。见:
【讨论】:
MAXALIGN 的下一个倍数。
t_cid ... (overlays with t_xvac)there 意味着这两个项目共享相同的物理场。源代码中的注释也暗示了很多:doxygen.postgresql.org/htup__details_8h_source.html 第 72 行。
NULL 值是否到达索引至少取决于索引的类型。
基本上,对于btree 和gist 索引类型,这将是YES,对于hash,这将是NO,看起来YES 或NO gin 索引类型取决于 PostgreSQL 版本。
在 pg_catalog.pg_am 表中曾经有一个布尔列 amindexnulls 携带该信息,但在 9.1 中已不存在。可能是因为索引在 PG 改进中变得更加复杂。
在您的数据的特定情况下,最好的了解方法是使用 pg_relation_size('index_name') 函数测量索引的大小差异,在完全 NULL 和完全 NOT NULL 的内容之间,使用您的确切 PG 版本,确切的数据类型,确切的索引类型和定义。并且要知道,未来任何这些参数的变化都可能会改变结果。
但无论如何,如果您“只是”想避免索引 NULL,则始终可以创建部分索引:
CREATE INDEX partial_idx(col) ON table WHERE (col is not null)
这将占用更少的空间,但这是否有助于查询的性能取决于这些查询。
【讨论】:
WHERE 子句以确保可以使用索引(可能AND-ed 有更多条件)。 Details in the manual.
我相信每个人都会在位图中为行使用一个位。见这里:http://www.postgresql.org/docs/9.0/static/storage-page-layout.html#HEAPTUPLEHEADERDATA-TABLE
【讨论】: