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/