?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
本節(jié)提供一個(gè)TOAST的概述。(超大字段存儲(chǔ)技術(shù))
因?yàn)?span id="377j5v51b" class="PRODUCTNAME">PostgreSQL的頁(yè)面大小是固定的(通常是 8Kb), 并且不允許行跨越多個(gè)頁(yè)面,因此不可能直接存儲(chǔ)非常大的字段值。為了突破這個(gè)限制, 大的字段值被壓縮和/或打碎成多個(gè)物理行。這些事情對(duì)用戶(hù)都是透明的, 只是在后端代碼上有一些小的影響。這個(gè)技術(shù)稱(chēng)為TOAST。 ("切片面包之后最好的東西")
只有一部分?jǐn)?shù)據(jù)類(lèi)型支持TOAST(沒(méi)必要在那些不可能生成大的字段值
的數(shù)據(jù)類(lèi)型強(qiáng)制這種額外開(kāi)銷(xiāo))。要支持TOAST,數(shù)據(jù)類(lèi)型必須有變長(zhǎng)
(varlena)表現(xiàn)形式,這個(gè)時(shí)候,任何存儲(chǔ)的數(shù)值的頭 32 位都是
存儲(chǔ)著以字節(jié)記的數(shù)值的總長(zhǎng)度(包括長(zhǎng)度本身)。TOAST
并不約束剩下的表現(xiàn)形式。所有支持TOAST 的數(shù)據(jù)類(lèi)型之
C 級(jí)別的函數(shù)都必須仔細(xì)處理TOAST 的輸入值。
也就是通常是在對(duì)一個(gè)輸入值做任何事情之前調(diào)用PG_DETOAST_DATUM
;
但是在某些情況下也存在更高效的方法。
TOAST 占用變長(zhǎng)的長(zhǎng)度字的兩位(在大型機(jī)器上高位序,在小型機(jī)器上低位序), 因此限制可TOAST數(shù)據(jù)類(lèi)型任何值的邏輯大小為1 GB(230 - 1 字節(jié))。 當(dāng)兩位都是零時(shí),該值是一個(gè)普通的非TOAST數(shù)據(jù)類(lèi)型的值,長(zhǎng)度字的剩下位給總數(shù)據(jù)大小以字節(jié)計(jì)。(包括長(zhǎng)度字) 當(dāng)設(shè)置最高或最低位,該值僅有一個(gè)字節(jié)頭替代通常的4字節(jié)頭,而剩余的位給總數(shù)據(jù)大小以字節(jié)計(jì)。(包括長(zhǎng)度字) 作為一個(gè)特殊的情況下,如果剩余位都是零(其將不可能包含自身的長(zhǎng)度),該值為一個(gè)指向存儲(chǔ)在TOAST表的行外數(shù)據(jù)。 (一個(gè)TOAST指針的大小是給定的在第二個(gè)字節(jié)的數(shù)據(jù)。)單字節(jié)頭的值沒(méi)有對(duì)齊任何特定的邊界。最后當(dāng)清除最高或最低位時(shí), 但是設(shè)置了臨近的位,壓縮了數(shù)據(jù)內(nèi)容,在使用前必須解壓縮。在這種情況下長(zhǎng)度字剩余位給壓縮數(shù)據(jù)的總大小,而不是原數(shù)據(jù)的。 請(qǐng)注意壓縮也可能是行外數(shù)據(jù),但是變長(zhǎng)的頭不會(huì)告訴這是否發(fā)生—TOAST指針的內(nèi)容告訴這些,替代。
如果一個(gè)表中有任何一個(gè)字段是可以TOAST的,那么該表將有一 個(gè)關(guān)聯(lián)的TOAST表,其 OID 存儲(chǔ)在表的 pg_class.reltoastrelid記錄里, 行外TOAST過(guò)的數(shù)值保存在TOAST表里, 下面有更詳細(xì)的描述。
這里使用的壓縮技術(shù)是非常簡(jiǎn)單并且非??焖俚?LZ 族壓縮技術(shù)。 參閱src/backend/utils/adt/pg_lzcompress.c獲取細(xì)節(jié)。
將外數(shù)據(jù)分割成(如果壓縮過(guò),在壓縮之后)最多TOAST_MAX_CHUNK_SIZE (缺省選擇這個(gè)值, 2000字節(jié) ,使4塊行將適合一內(nèi)存頁(yè),約2000個(gè)字節(jié))字節(jié),每個(gè)塊都作為獨(dú)立的行 在TOAST表里為所屬表存儲(chǔ)。每個(gè)TOAST表都有chunk_id 字段(一個(gè)表示特定TOAST值的 OID)、chunk_seq (一個(gè)序列號(hào),存儲(chǔ)該塊在數(shù)值中的位置)、chunk_data(該塊實(shí)際的數(shù)據(jù))。 在chunk_id和chunk_seq上有一個(gè)唯一索引, 提供對(duì)數(shù)值的快速檢索。因此,一個(gè)表示行外TOAST值的指針數(shù)據(jù)需要 存儲(chǔ)要查閱的TOAST的 OID 和特定數(shù)值的 OID(它的chunk_id)。 為了方便,指針數(shù)據(jù)還存儲(chǔ)邏輯數(shù)據(jù)的尺寸(原始的未壓縮的數(shù)據(jù)長(zhǎng)度)以及實(shí)際存儲(chǔ)的尺寸 (如果使用了壓縮,則兩者不同)。加上頭部的長(zhǎng)度字,一個(gè)TOAST指針數(shù)據(jù)的總 尺寸是 20 字節(jié),不管它代表的數(shù)值的實(shí)際長(zhǎng)度是多大。
TOAST代碼只有在準(zhǔn)備向某表中存儲(chǔ)超過(guò)TOAST_TUPLE_THRESHOLD 字節(jié)(通常是 2KB)的行的時(shí)候才會(huì)觸發(fā)。TOAST代碼將壓縮和/或行外存儲(chǔ)字段值, 直到數(shù)值比TOAST_TUPLE_TARGET字節(jié)(通常是2KB)短,或者無(wú)法得到更好的結(jié)果 的時(shí)候才停止。在一個(gè) UPDATE 操作過(guò)程中,未改變的字段值通常原樣保存; 所以,如果 UPDATE 一個(gè)帶有行外數(shù)據(jù)的行時(shí),如果行外數(shù)據(jù)值沒(méi)有變化, 那么將不會(huì)有TOAST開(kāi)銷(xiāo)存在。
TOAST代碼識(shí)別四種不同的存儲(chǔ)可TOAST字段的策略:
PLAIN避免壓縮或者行外的存儲(chǔ);此外,它禁止使用單字節(jié)的頭變長(zhǎng)類(lèi)型。 這只是對(duì)那些不能 TOAST 的數(shù)據(jù)類(lèi)型才有可能。
EXTENDED允許壓縮和行外存儲(chǔ)。這是大多數(shù)可 以TOAST的數(shù)據(jù)類(lèi)型的缺省。首先將企圖進(jìn)行壓縮, 如果行仍然太大,那么則進(jìn)行行外存儲(chǔ)。
EXTERNAL允許行外存儲(chǔ),但是不許壓縮。 使用EXTERNAL,將令那些在text和bytea字段上的子字符串操作更快 (代價(jià)是增加了存儲(chǔ)空間),因?yàn)檫@些操作是經(jīng)過(guò)優(yōu)化的:如果行外數(shù)據(jù)沒(méi)有壓縮, 那么它們只會(huì)去抓取需要的部分。
MAIN允許壓縮,但不允許行外存儲(chǔ)。 實(shí)際上,在這樣的字段上仍然會(huì)進(jìn)行行外存儲(chǔ),但只是作 為沒(méi)有辦法把數(shù)據(jù)行變得更小的情況下使之足以容納一個(gè)頁(yè)面的最后的手段。
每個(gè)可TOAST的數(shù)據(jù)類(lèi)型都為該數(shù)據(jù)類(lèi)型的字段指定一個(gè)缺省策略, 但是特定表的字段的存儲(chǔ)策略可以用ALTER TABLE SET STORAGE修改。
這個(gè)方法比那些更直接的方法,比如允許行值直接跨越多個(gè)頁(yè)面, 有更多優(yōu)點(diǎn)。假設(shè)查詢(xún)通常是用相對(duì)比較短的鍵值進(jìn)行匹配的, 那么大多數(shù)執(zhí)行器的工作都將使用主行記錄完成。TOAST的屬性的大值, 只是在把結(jié)果集發(fā)送給客戶(hù)端的時(shí)候才抽出來(lái)(如果選擇了它的話(huà))。因此, 主表要小得多,并且它的大部分行都存儲(chǔ)在共享緩沖區(qū)里,因此就可以不需要任何行外存儲(chǔ)。 排序集也縮小了,并且排序?qū)⒏嗟卦趦?nèi)存里完成。一個(gè)小測(cè)試表明,一個(gè)用于 保存 HTML 頁(yè)面以及它們的 URL 的表,包括TOAST表在內(nèi), 存儲(chǔ)將近一半大小的裸數(shù)據(jù),而主表只包含全部數(shù)據(jù)的 10%(URL 和一些小的 HTML 頁(yè)面)。 與在一個(gè)非TOAST的對(duì)比表里面存儲(chǔ)(把全部 HTML 頁(yè)面裁剪成 7KB 以匹配頁(yè)面大小), 沒(méi)有任何運(yùn)行時(shí)的區(qū)別。