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
現在,讓我們為我們的測試創建兩個大小為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
這個大對象被再次拆分為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!
原文:http://www.ludovicocaldara.net/dba/pgsql-lo-space-usage-part-1/