PostgreSQL的大對象以及空間使用 (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為空的一個數據庫開始:

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中的延遲段創建,除非該文件已經存在)。

現在,讓我們為我們的測試創建兩個大小為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填充的文件:

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/

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM