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/

相關文章
相關標籤/搜索