想象一下,我在 Postgres 13 中有一张这样的表:
CREATE TABLE public.people (
id integer PRIMARY KEY,
full_name character varying(255),
bio text
);
然后我插入一行,其中包含足够的字符,以便将 bio 写入 TOAST 表(4000 个随机字节,应该压缩到 > 2Kb):
# insert into people values (1, 'joe toast', (SELECT array_to_string(ARRAY(SELECT chr((65 + round(random() * 25)) :: integer) FROM generate_series(1,4000)), '')));
INSERT 0 1
然后插入一行,其中包含足够的字符用于 bio fit 内联(3000 个重复字节,应该压缩到 < 2Kb):
# insert into people values (2, 'joe compressed', (SELECT array_to_string(ARRAY(SELECT chr(65) FROM generate_series(1,3000)), '')));
INSERT 0 1
最后在 bio 中插入一行只有几个字符的行,这样它将内联存储(10 个重复字节):
# insert into people values (3, 'joe inline', 'aaaaaaaaaa');
INSERT 0 1
我有什么方法可以检测每个元组中 bio 的存储策略吗?我可以报告内联行或 TOAST 中的行的百分比(“22% 的元组存储内联生物,78% 在 TOAST 中”)?
一个相关的问题:我是否知道磁盘上按内联、内联压缩和 TOAST 存储分解的元组的字节数?
上下文:我正在使用一个总计超过 10 亿行的分区表,我想知道特定列的内联存储频率与 TOAST 存储的频率。
研究
我可以获得每个 bio 的磁盘大小,在一种情况下,它显然是内联压缩的大小:
# select id, full_name, pg_column_size(bio) from people order by id;
id | full_name | pg_column_size
----+----------------+----------------
1 | joe toast | 4000
2 | joe compressed | 44
3 | joe inline | 11
(3 rows)
将该大小与未压缩数据的大小进行比较可以告诉我们一些关于压缩的信息,但是它可以告诉我们关于 TOAST 状态的任何信息吗?
# select id, full_name, pg_column_size(bio), length(bio) from people order by id;
id | full_name | pg_column_size | length
----+----------------+----------------+--------
1 | joe toast | 4000 | 4000
2 | joe compressed | 44 | 3000
3 | joe inline | 11 | 10
我可以手动检查 TOAST 表中有一些行:
# select relname from pg_class where oid = (select reltoastrelid from pg_class where relname='people');
relname
----------------
pg_toast_20138
# select chunk_id, sum(length(chunk_data)) from pg_toast.pg_toast_20138 group by chunk_id;
chunk_id | sum
----------+------
20149 | 4000
在一般情况下,以下情况是否正确?
# select id, full_name, pg_column_size(bio), length(bio),
case
when pg_column_size(bio) < length(bio) then 'inline-compressed'
when pg_column_size(bio) = length(bio) then 'toast'
else
'inline'
end as storage_strategy
from people order by id;
id | full_name | pg_column_size | length | storage_strategy
----+----------------+----------------+--------+-------------------
1 | joe toast | 4000 | 4000 | toast
2 | joe compressed | 44 | 3000 | inline-compressed
3 | joe inline | 11 | 10 | inline
关于方法
out_of_line
表示数据存储在 TOASTbytes_on_disk
并且uncompressed_bytes
可能包含一些元数据长度(1 或 4 个字节),需要某天对其进行完善。inner join people
,如果您想查看不可见的行(例如已删除但尚未清空),请使用left join people
执行
首先打开
pageinspect
检查并创建函数以从列元数据中获取信息:现在您可以选择一些列(
[3]
表示第 3 列),获取二进制数据并解析标题:内部 Postgres
此信息由 Postgres 内部存储的内容确定。varlena(可变长度字段)元数据(代码、文档、演示文稿)有 3 个选项:
在接受答案的评论中,我询问是否也可以压缩 TOAST 数据。在 Stanislav 的指导下,我添加了另一行,它足够大,需要 TOAST,但也可以压缩:
现在接受的答案中的查询显示 TOAST 数据在节省空间时也被压缩存储: