在PostgreSQL中计算和节省空间

时间:2010-06-03 13:44:57

标签: postgresql database-design storage bigdata

我有一张像pg一样的表:

CREATE TABLE t (
    a BIGSERIAL NOT NULL,               -- 8 b
    b SMALLINT,                         -- 2 b
    c SMALLINT,                         -- 2 b
    d REAL,                             -- 4 b
    e REAL,                             -- 4 b
    f REAL,                             -- 4 b
    g INTEGER,                          -- 4 b
    h REAL,                             -- 4 b
    i REAL,                             -- 4 b
    j SMALLINT,                         -- 2 b
    k INTEGER,                          -- 4 b
    l INTEGER,                          -- 4 b
    m REAL,                             -- 4 b
    CONSTRAINT a_pkey PRIMARY KEY (a)
);

以上每行最多可添加50个字节。我的经验是,我需要另外40%到50%的系统开销,甚至没有任何用户创建的索引。所以,每行约75个字节。我将在表中有许多行,可能超过1450亿行,因此该表将推动13-14太字节。我可以使用什么技巧来压缩这个表?我可能的想法如下......

real值转换为integer。如果它们可以存储为smallint,那么每个字段可节省2个字节。

将列b .. m转换为数组。我不需要搜索这些列,但我确实需要能够一次返回一列的值。所以,如果我需要列g,我可以做类似

的事情
SELECT a, arr[5] FROM t;

我可以使用数组选项节省空间吗?会有速度惩罚吗?

还有其他想法吗?

4 个答案:

答案 0 :(得分:167)

“列俄罗斯方块”

实际上,您可以执行 某些内容 ,但这需要更深入的了解。关键字为 对齐填充 Every data type has specific alignment requirements

您可以通过优先排序来最小化填充 列之间 的空间。以下(极端)示例将浪费大量物理磁盘空间:

CREATE TABLE t (
    e int2    -- 6 bytes of padding after int2
  , a int8
  , f int2    -- 6 bytes of padding after int2
  , b int8
  , g int2    -- 6 bytes of padding after int2
  , c int8
  , h int2    -- 6 bytes of padding after int2
  , d int8)

要保存每行 24字节,请改为使用:

CREATE TABLE t (
    a int8
  , b int8
  , c int8
  , d int8
  , e int2
  , f int2
  , g int2
  , h int2)   -- 4 int2 occupy 8 byte (MAXALIGN), no padding at the end

SQL Fiddle.

根据经验,如果先放入8字节列,那么4字节,2字节和1字节列最后就不会出错。

booleanuuid和其他一些类型不需要对齐填充。 textvarchar和其他“varlena”(可变长度)类型名义上需要“int”对齐(大多数机器上为4个字节)。但事实上,磁盘格式中没有对齐填充(与RAM不同)。我在许多测试中验证过。最后,我在note in the source code:

中找到了解释
  

另请注意,我们允许在存储“打包”varlenas时违反名义对齐;

通常情况下,每行最多可以节省几个字节“column tetris”。在大多数情况下,这些都不是必需的。但是数十亿行可以轻松实现几千兆字节。

您可以使用函数pg_column_size()测试实际的列/行大小 某些类型在RAM中占用的空间比在磁盘上占用的空间大(压缩或“打包”格式)。当使用pg_column_size()测试相同的值(或值行与表行)时,您可以获得更大的常量(RAM格式)结果,而不是表格列。

最后,某些类型可能是compressed or "toasted"(存储在行外)或两者都有。

每个元组的开销(行)

项目指针每行4个字节 - 不受上述考虑 并且至少有24个字节(23 +填充)用于元组头。 The manual on Database Page Layout:

  

有一个固定大小的标题(在大多数机器上占用23个字节),   后跟可选的空位图,可选的对象ID字段和   用户数据。

对于标头和用户数据之间的填充,您需要知道服务器上的MAXALIGN - 通常是64位操作系统上的8个字节(或32位操作系统上的4个字节)。如果您不确定,请查看pg_controldata

Postgres二进制目录中运行以下内容以获得明确答案:

./pg_controldata /path/to/my/dbcluster

The manual:

  

实际用户数据(行的列)从偏移量开始   由t_hoff表示,其必须始终是MAXALIGN的倍数   平台的距离。

因此,您通常可以通过将数据打包为8个字节的倍数来获得最佳存储空间。

您发布的示例中没有任何内容可以获得。它已经紧紧包装好了。最后int2后的2个字节填充,最后4个字节。您可以在最后将填充合并到6个字节,这不会改变任何内容。

每个数据页的开销

数据页面大小通常为8 KB。这个级别也有一些开销/膨胀:余数不足以容纳另一个元组,更重要的是死行或FILLFACTOR setting保留的百分比。

要考虑磁盘大小还有其他几个因素:

数组类型?

对于您正在评估的数组类型,您将为该类型添加 24字节的开销。此外,阵列元素像往常一样占据空间。什么都没有。

答案 1 :(得分:10)

在数组中存储多个数字字段时,我看不到任何好处(以及丢失的东西)。

size of each numerical type已清楚记录,您应该使用与您所需的范围分辨率兼容的最小尺寸类型;这就是你所能做的一切。

我不认为(但我不确定)是否对行中的列有一些字节对齐要求,在这种情况下,列的重新排序可能会改变使用的空间 - 但我不认为如此。

顺便说一下,每行都有一个修复开销,约为23 bytes

答案 2 :(得分:2)

摘自这份出色的文档:https://www.2ndquadrant.com/en/blog/on-rocks-and-sand/

对于已经拥有的表,或者正在开发中的表,名为my_table的表,此查询将从左到右给出最佳顺序。

SELECT a.attname, t.typname, t.typalign, t.typlen
FROM pg_class c
JOIN pg_attribute a ON (a.attrelid = c.oid)
JOIN pg_type t ON (t.oid = a.atttypid)
WHERE c.relname = 'my_table'
 AND a.attnum >= 0
ORDER BY t.typlen DESC

答案 3 :(得分:-2)

在这样的工作流中使用 No-SQL 数据库,因为它们像 json 一样,没有像行那样的东西,你宁愿将每个键的数据存储在那里,每个键的结构都是相互独立的。

一些例子是 MongoDB、Cassandra。