PostgreSQL的大对象以及空间使用 (1)
2020-10-10 09:34 abce 阅读(1544) 评论(1) 编辑 收藏 举报PostgreSQL对大列使用了一种很好的,非标准的TOAST机制,可以将其与Oracle中的扩展数据类型进行比较(顺便说一下,TOAST行可能更大)。
不过,传统的大对象,仍然被许多客户使用。
如果你不熟悉PostgreSQL中的大对象,请阅读此处(https://www.postgresql.org/docs/9.6/largeobjects.html)。对于TOAST,请阅读此处(https://www.postgresql.org/docs/9.6/storage-toast.html)。
在应用表中,大对象的列被定义为指向pg_largeobject表内数据块(chunks)的oid。
因为大对象是独立于引用它的表列创建的,所以当你从表中删除指向大对象的行时,大对象本身不会被删除。
此外,pg_largeobject被设计用于存储数据库中存在的所有大对象。这使得该表的管理维护对于数据库管理至关重要。(我们将在下一篇文章中看到它)
大对象是如何组织空间的?
我们将通过示例来展示。让我们从pg_largeobject为空的一个数据库开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | lob_test=# select count (*) from pg_largeobject; count ------- 0 (1 row) lob_test=# vacuum full pg_largeobject; VACUUM lob_test=# select pg_total_relation_size( 'pg_largeobject' ); pg_total_relation_size ------------------------ 8192 (1 row) |
只有一个block。我们再来看看磁盘上对应的数据文件:
1 2 3 4 5 6 7 8 | lob_test=# SELECT pg_relation_filepath( 'pg_largeobject' ); pg_relation_filepath ---------------------- base/16471/16487 (1 row) # ls -l base/16471/16487 -rw ------- 1 postgres postgres 0 Jul 26 16:58 base/16471/16487 |
现在,让我们为我们的测试创建两个大小为1MB的文件,一个用零填充,另一个随机填充:
1 2 3 4 5 | $ dd if=/dev/zero of =/tmp/zeroes bs=1024 count =1024 $ dd if=/dev/urandom of =/tmp/randoms bs=1024 count =1024 $ ls -l /tmp/zeroes /tmp/randoms -rw-r --r-- 1 postgres postgres 1048576 Jul 26 16:56 /tmp/randoms -rw-r --r-- 1 postgres postgres 1048576 Jul 26 16:23 /tmp/zeroes |
让我们导入用0填充的文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 | lob_test=# \lo_import '/tmp/zeroes' ; lo_import 16491 lob_test=# select count (*) from pg_largeobject_metadata; count ------- 1 (1 row) lob_test=# select count (*) from pg_largeobject; count ------- 512 (1 row) |
大对象被切分成大小为每个2048bytes的chunk,因此一共有512个。那物理大小呢?
1 2 3 4 5 6 7 8 9 | lob_test=# select pg_relation_size( 'pg_largeobject' ); pg_total_relation_size ------------------------ 40960 (1 row) bash-4.1$ ls -l 16487* -rw ------- 1 postgres postgres 40960 Jul 26 17:18 16487 |
只有40k。这就意味着chunk被压缩了(类似TOAST的page)。PostgreSQL使用了pglz_compress函数,其算法在源代码src/common/pg_lzcompress.c中做了很好的解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | lob_test=# \lo_import '/tmp/randoms' ; lo_import 16492 lob_test=# select count (*) from pg_largeobject where loid=16492; count ------- 512 (1 row) lob_test=# select pg_relation_size( 'pg_largeobject' ); pg_relation_size ------------------ 1441792 (1 row) $ ls -l 16487 -rw ------- 1 postgres postgres 1441792 Jul 26 17:24 16487 |
这个大对象被再次拆分为512个chunk,每个都有2048个字节,PostgreSQL再次尝试压缩它们。但是,因为一个随机字符串不能被压缩,所以段仍然(平均)是2048字节大。
现在,一个数据库块的大小是8192字节。如果我们减去block header的大小,就没有足够的空间容纳4个2048字节的chunk。每个块将只包含3个未压缩的chunk。(这里block和chunk别混淆)
因此,512个chunk将分布在171个block上(CEIL(512/3.0)),得到:
1 2 3 4 5 | lob_test=# select ceil(1024*1024/2048/3.0)*8192; ? column ? ---------- 1400832 (1 row) |
1400832 bytes!
原文:http://www.ludovicocaldara.net/dba/pgsql-lo-space-usage-part-1/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2019-10-10 PostgreSQL中的partition-wise aggregation
2019-10-10 PostgreSQL中的partition-wise join
2019-10-10 Partition-wise join