PostgreSQL的大对象以及空间使用 (1)

PostgreSQL对大列使用了一种很好的,非标准的TOAST机制,能够将其与Oracle中的扩展数据类型进行比较(顺便说一下,TOAST行可能更大)。不过,传统的大对象,仍然被许多客户使用。html

若是你不熟悉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。sql

 

由于大对象是独立于引用它的表列建立的,因此当你从表中删除指向大对象的行时,大对象自己不会被删除。数据库

此外,pg_largeobject被设计用于存储数据库中存在的全部大对象。这使得该表的管理维护对于数据库管理相当重要。(咱们将在下一篇文章中看到它)bash

大对象是如何组织空间的?

咱们将经过示例来展现。让咱们从pg_largeobject为空的一个数据库开始:dom

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。咱们再来看看磁盘上对应的数据文件:函数

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

证据1:文件是空的。这意味着在表中有一些数据以前不会物理地建立第一个block(相似于Oracle中的延迟段建立,除非该文件已经存在)。post

如今,让咱们为咱们的测试建立两个大小为1MB的文件,一个用零填充,另外一个随机填充:测试

$ 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填充的文件:spa

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个。那物理大小呢?

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中作了很好的解释。

当咱们导入随机填充的文件时会发生什么?

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

段增长了超过1Mb!准确地说,1441792-40960 = 1400832字节。为何?

这个大对象被再次拆分为512个chunk,每一个都有2048个字节,PostgreSQL再次尝试压缩它们。可是,由于一个随机字符串不能被压缩,因此段仍然(平均)是2048字节大。

如今,一个数据库块的大小是8192字节。若是咱们减去block header的大小,就没有足够的空间容纳4个2048字节的chunk。每一个块将只包含3个未压缩的chunk。(这里block和chunk别混淆)

所以,512个chunk将分布在171个block上(CEIL(512/3.0)),获得:

lob_test=# select ceil(1024*1024/2048/3.0)*8192;
 ?column?
----------
  1400832
(1 row)

1400832 bytes!

根据能够应用于大对象的压缩率,咱们能够指望在pg_largeobject表中使用更多或更少的空间。

 

 

原文:http://www.ludovicocaldara.net/dba/pgsql-lo-space-usage-part-1/

相关文章
相关标签/搜索