pun*_*ish 58 postgresql storage database-design 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)
);
Run Code Online (Sandbox Code Playgroud)
以上每行最多可添加50个字节.我的经验是,我需要另外40%到50%的系统开销,甚至没有任何用户创建的索引.所以,每行约75个字节.我将在表中有许多行,可能超过1450亿行,因此该表将推动13-14太字节.我可以使用什么技巧来压缩这个表?我的可能想法如下......
将real值转换为integer.如果它们可以存储为smallint,则每个字段节省2个字节.
将列b .. m转换为数组.我不需要搜索这些列,但我确实需要能够一次返回一列的值.所以,如果我需要列g,我可以做类似的事情
SELECT a, arr[5] FROM t;
Run Code Online (Sandbox Code Playgroud)
我会用数组选项节省空间吗?会有速度惩罚吗?
还有其他想法吗?
Erw*_*ter 177
实际上,你可以做点什么,但这需要更深入的了解.关键字是对齐填充.每种数据类型都有特定的对齐要求.
您可以通过有利地排序来最小化列之间填充的空间损失.以下(极端)示例将浪费大量物理磁盘空间:
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)
Run Code Online (Sandbox Code Playgroud)
要保存每行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
Run Code Online (Sandbox Code Playgroud)
根据经验,如果先放入8字节列,那么4字节,2字节和1字节列最后就不会出错.
boolean,uuid以及其他一些类型不需要对齐填充.text,varchar等"varlena"(可变长度)类型名义上需要"INT"对齐(在大多数机器4个字节).但事实上,磁盘格式中没有对齐填充(与RAM不同).我在许多测试中验证过.最后,我在源代码的注释中找到了解释:
还要注意,当存储"打包"varlenas时,我们允许违反名义对齐;
通常情况下,每行最多可以节省几个字节,最好玩"列俄罗斯方块".在大多数情况下,这些都不是必需的.但是数十亿行可以轻松实现几千兆字节.
您可以使用该函数测试实际的列/行大小pg_column_size().
某些类型在RAM中占用的空间比在磁盘上占用的空间大(压缩或"打包"格式).当测试相同的值(或值行与表行)时,您可以获得更大的常量(RAM格式)结果,而不是表格列pg_column_size().
最后,某些类型可以被压缩或"烘烤"(存储在线外)或两者.
项目指针每行4个字节 - 不受上述考虑.
并且至少有24个字节(23 +填充)用于元组头.数据库页面布局手册:
有一个固定大小的头(在大多数机器上占用23个字节),后跟一个可选的空位图,一个可选的对象ID字段和用户数据.
对于标头和用户数据之间的填充,您需要知道MAXALIGN您的服务器 - 通常是64位操作系统上的8个字节(或32位操作系统上的4个字节).如果您不确定,请查看pg_controldata.
在Postgres二进制目录中运行以下命令以获得明确的答案:
./pg_controldata /path/to/my/dbcluster
Run Code Online (Sandbox Code Playgroud)
实际用户数据(行的列)从指示的偏移开始,该偏移
t_hoff必须始终是MAXALIGN平台距离的倍数.
因此,您通常通过将数据打包为8个字节的倍数来获得最佳存储空间.
在您发布的示例中没有任何好处.它已经紧紧包装好了.最后一个填充后的2个字节,最后的int24个字节.您可以在最后将填充合并到6个字节,这不会改变任何内容.
数据页大小通常为8 KB.在这个级别也有一些开销/膨胀:余数不足以容纳另一个元组,更重要的是死行或FILLFACTOR设置保留的百分比.
磁盘大小还有几个其他因素需要考虑:
对于您正在评估的数组,您将为数组类型添加24字节的开销.此外,阵列的元素像往常一样占据空间.没有什么可以获得的.
leo*_*loy 11
在数组中存储多个数字字段时,我看不到任何可以获得的东西(以及丢失的东西).
每种数字类型的大小都清楚地记录下来,您应该使用与您所需的范围分辨率兼容的最小尺寸类型; 这就是你所能做的一切.
我不认为(但我不确定)是否对行中的列有一些字节对齐要求,在这种情况下,列的重新排序可能会改变所使用的空间 - 但我不这么认为.
顺便说一句,每行有一个修复开销,大约23个字节.
从这个伟大的文档: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
Run Code Online (Sandbox Code Playgroud)
这是关于 Erwin 的列重新排序建议的一个很酷的工具: https: //github.com/NikolayS/postgres_dba
它有确切的命令——p1:
然后它会自动向您显示所有表上的列重新排序的真正潜力: