为什么我的数据库比插入的数据大 12 倍?

A.c*_*A.c 3 postgresql datatypes storage database-size

我有一个关于我的数据库大小的简短问题。我需要在数据库中插入数据。在插入之前,需要进行一些计算。

关键是:从 50 mb 纯数据(~700,000 行),这导致 600 mb db 大小。这是12倍!我确定我在这里做错了什么。你能帮我缩小我的数据库的大小吗?数据库大小的来源是 web postgres 管理界面。

这是插入:

CREATE TYPE CUSTOMER_TYPE AS ENUM
('enum1', 'enum2', 'enum3', '...', 'enum15');       ## max lenght of enum names ~15

CREATE TABLE CUSTOMER(
   CUSTOMER_ONE    TEXT PRIMARY KEY NOT NULL,       ## max 35 char String
   ATTRIBUTE_ONE   TEXT UNIQUE,                     ## max 35 char String
   ATTRIBUTE_TWO   TEXT UNIQUE,                     ## max 51 char String
   ATTRIBUTE_THREE TEXT UNIQUE,                     ## max 52 char String
   ATTRIBUTE_FOUR  TEXT UNIQUE,                     ## max 64 char String
   ATTRIBUTE_FIFE  TEXT UNIQUE,                     ## 1-80 char String
   CUSTOMER_TYPE   PRIVATEKEYTYPE                   ## see enum
);
Run Code Online (Sandbox Code Playgroud)

我真的不需要那个枚举,因为我也可以插入它。枚举对数据库大小有影响吗?

有没有办法减小尺寸?是否有可能达到因子 x4(而不是 x12)?如果没有,如有必要,我可以删除一些列。

也许还有其他用于字符数据的 Postgres 数据类型?

在这里反馈后,我更新后的表格现在看起来像这样:

CREATE TABLE CUSTOMER(
   CUSTOMER_ONE    TEXT PRIMARY KEY NOT NULL,       ## max 35 char String
   ATTRIBUTE_ONE   TEXT UNIQUE,                     ## max 35 char String
   ATTRIBUTE_TWO   TEXT,                            ## max 51 char String
   ATTRIBUTE_THREE TEXT,                            ## max 52 char String
   ATTRIBUTE_FOUR  TEXT,                            ## max 64 char String
   ATTRIBUTE_FIFE  TEXT,                            ## 1-80 char String
   CUSTOMER_TYPE   PRIVATEKEYTYPE                   ## see enum
);
Run Code Online (Sandbox Code Playgroud)

之前:12x

现在:7x :)

还有更多可能的优化吗?(除了删除列?)也许其他数据类型使用更少的空间?

Erw*_*ter 6

text是纯文本的最佳类型。单独的文本值在内部占用大致相同(当然也取决于编码细节:UTF-8?)。在您的情况下,每个字段 1 个字节的小开销。看:

enum列对总大小的贡献很小。每行 4 个字节(real内部数量)。枚举名称的长度实际上无关紧要,作为数据类型存储一次name,因此最多为NAMEDATALEN(通常为 63 个字符)。类型声明的开销最小。因此,ENUM列实际占用较少比在文本文件中的DB内部空间。
Postgres 存储有各种类型的开销,它必须是这样的。其中一些是一次性成本,另一些是与行数或关系等的比例。 有目录表(系统数据)、索引、关系、表统计、簿记数据等。

对于 50 MB 的原始文本数据来说,12 倍的大小似乎超过了平均值。但这真的取决于细节。许多小行有很多开销,很少有大行没有那么多,甚至可以压缩和更小。a_horse 已经指出了一个主要原因:6 个索引 - 其中一些显然也是不必要的。

表中的每一行都有一个占 23 个字节的元组头(加上对齐填充,通常为 24 个字节),列和元组之间可能有对齐填充,4 个字节用于项目标识符,每个数据页有一些开销(通常为 8kb 页面大小)。在您的情况下,每行大约 35 个字节。

每个索引元组都差不多(标头只有 8 个字节,但索引膨胀更多)。6 个索引,每个表行至少约 150 字节。加上每个文本列都保存两次,1x 表,1x 索引。并且 btree 索引以FILLFACTOR90 的a 开头,因此所有内容都为 ~ + 10 %。

您的平均行大小约为 75 字节(50MB / 700000 - 假设“行”对应于行)。您的表和索引大约占用原始文本大小的 6 倍。剩下的就是上面提到的其他开销。

这一切都假设没有表膨胀(还)。一旦您使用数据库(更新、删除、事务回滚等),就会有死行等,可能会乘以总空间。这在很大程度上取决于写入模式。

您可以通过删除不必要的索引来节省很多。

如果将enum列移动到表的顶部,每行几个字节:较少的对齐填充,平均每行 1.5 个字节,总共大约 1 MB,几乎不值得。看: