支援版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0

65.2. TOAST #

本節概述TOAST(超大型屬性儲存技術)。

PostgreSQL 使用固定頁面大小(通常為 8 kB),且不允許 tuple 跨越多個頁面。 因此,無法直接儲存非常大的欄位值。 為了克服此限制,大型欄位值會被壓縮和/或分解成多個實體列。 這對使用者來說是透明的,對大多數後端程式碼的影響很小。 該技術被親切地稱為TOAST(或自切片麵包以來最好的東西)。TOAST基礎架構也用於改善記憶體中大型資料值的處理。

只有某些資料類型支援TOAST— 沒有必要將額外負擔加在無法產生大型欄位值的資料類型上。 為了支援TOAST,資料類型必須具有可變長度(varlena)表示形式,其中,通常,任何儲存值的首四個位元組字組都包含該值以位元組為單位的總長度(包括其自身)。TOAST不會約束資料類型其餘部分的表示形式。 統稱為 TOASTed 值的特殊表示形式,透過修改或重新解釋此初始長度字組來工作。 因此,支援TOAST的 C 語言等級函式必須小心處理潛在的TOASTed 輸入值:在 detoasted 之前,輸入實際上可能不包含四位元組長度字組和內容。 (這通常透過在使用輸入值之前調用 PG_DETOAST_DATUM 來完成,但在某些情況下,可以採用更有效的方法。 詳情請參閱第 36.13.1 節。)

TOAST會佔用 varlena 長度字組的兩個位元(大端機上的高位,小端機上的低位),從而將TOAST的任何值的邏輯大小限制為 1 GB (230 - 1 位元組)。 當兩位都為零時,該值是資料類型的普通未TOASTed 值,並且長度字組的其餘位元以位元組為單位給出總資料大小(包括長度字組)。 當設定最高位或最低位時,該值只有一個單一位元組標頭,而不是普通的四位元組標頭,並且該位元組的其餘位元以位元組為單位給出總資料大小(包括長度位元組)。 這種替代方案支援空間效率高的短於 127 個位元組的值的儲存,同時仍然允許資料類型根據需要增長到 1 GB。 具有單一位元組標頭的值不會對齊任何特定邊界,而具有四位元組標頭的值至少對齊四位元組邊界; 這種省略對齊填充提供了額外的空間節省,相對於短值而言非常重要。 作為一種特殊情況,如果單一位元組標頭的其餘位元都為零(對於自包含長度而言是不可能的),則該值是指向離線資料的指標,如下所述有幾種可能的替代方案。 此類 TOAST 指標的類型和大小由儲存在資料的第二個位元組中的程式碼決定。 最後,當最高位或最低位清除但相鄰位設定時,資料的內容已被壓縮,必須在使用前解壓縮。 在這種情況下,四位元組長度字組的其餘位元給出壓縮資料的總大小,而不是原始資料。 請注意,壓縮也可用於離線資料,但 varlena 標頭不會告訴它是否已發生 —TOAST指標的內容會告訴您,而不是。

可以透過在 CREATE TABLEALTER TABLE 中設定 COMPRESSION 欄選項,為每個欄位選擇用於線上或離線壓縮資料的壓縮技術。 沒有明確設定的欄位的預設值是在插入資料時查閱 default_toast_compression 參數。

如前所述,有多種類型的TOAST指標資料。 最古老也是最常見的類型是指向儲存在 TOAST中的離線資料的指標,該表與包含TOAST指標資料本身的表分開但相關聯。 這些 磁碟上的指標資料由TOAST管理程式碼(在 access/common/toast_internals.c 中)在要儲存在磁碟上的 tuple 太大而無法按原樣儲存時建立。 更多詳細資訊請參閱第 65.2.1 節。 或者,一個TOAST指標資料可以包含指向記憶體中其他地方出現的離線資料的指標。 此類資料必然是短暫的,永遠不會出現在磁碟上,但它們對於避免複製和冗餘處理大型資料值非常有用。 更多詳細資訊請參閱第 65.2.2 節

65.2.1. 離線、磁碟上的 TOAST 儲存 #

如果一個表中的任何欄位是TOAST-able,該表將有一個關聯的TOAST表,其 OID 儲存在表的 pg_class.reltoastrelid 條目中。 磁碟上的TOASTed 的值會被保存在TOAST表格中,詳細說明如下。

非行內(Out-of-line)的值會被分割(如果使用的話,會在壓縮後)成最多 TOAST_MAX_CHUNK_SIZE 位元組的區塊(預設情況下,此值的選擇是為了讓四個區塊列可以放入一個頁面,大約是 2000 位元組)。每個區塊會以獨立的列儲存在TOAST擁有表格所屬的表格中。每個TOAST表格都有欄位 chunk_id (一個 OID,用來識別特定的TOASTed 值)、chunk_seq (區塊在其值中的序號),以及 chunk_data (區塊的實際資料)。chunk_idchunk_seq 上的唯一索引提供了快速檢索這些值的能力。因此,一個表示行外(out-of-line)磁碟上的TOASTed 值的指標資料需要儲存TOAST表格的 OID,用以查詢特定的值 (其 chunk_id)。為了方便起見,指標資料也會儲存邏輯資料大小(原始未壓縮資料長度)、實際儲存大小(如果應用了壓縮則會不同),以及使用的壓縮方法(如果有的話)。算上 varlena 標頭位元組,磁碟上TOAST指標資料的總大小因此為 18 位元組,無論表示的實際值大小為何。

只有當要儲存在表格中的列值寬度大於 TOAST_TUPLE_THRESHOLD 位元組(通常為 2 kB)時,才會觸發TOAST管理程式碼。此TOAST程式碼將會壓縮和/或將欄位值移出行外(out-of-line),直到列值小於 TOAST_TUPLE_TARGET 位元組(通常也是 2 kB,可調整)或無法再獲得更多收益為止。在 UPDATE 操作期間,未更改欄位的值通常會保持不變;因此,如果沒有行外值更改,則更新具有行外值的列不會產生TOAST成本。

只有當要儲存在表格中的列值寬度大於 TOAST_TUPLE_THRESHOLD 位元組(通常為 2 kB)時,才會觸發TOAST管理程式碼識別出四種不同的策略,用於在磁碟上儲存TOAST-able 的欄位。

  • PLAIN 會阻止壓縮或行外儲存。這是非 -TOAST-able 資料類型欄位的唯一可能策略。

  • EXTENDED 允許壓縮和行外儲存。這是大多數TOAST-able 資料類型的預設值。將首先嘗試壓縮,如果列仍然太大,則進行行外儲存。

  • EXTERNAL 允許行外儲存但不允許壓縮。使用 EXTERNAL 將使寬 textbytea 欄位的子字串操作更快(以增加儲存空間為代價),因為這些操作經過優化,僅在未壓縮時才獲取行外值的所需部分。

  • MAIN 允許壓縮但不允許行外儲存。(實際上,對於此類欄位仍然會執行行外儲存,但僅作為最後的手段,當沒有其他方法使列足夠小以適合頁面時。)

每個TOAST-able 資料類型都為該資料類型的欄位指定了預設策略,但是可以使用 ALTER TABLE ... SET STORAGE 更改給定表格欄位的策略。

可以使用 ALTER TABLE ... SET (toast_tuple_target = N) 調整每個表格的 TOAST_TUPLE_TARGET

與允許列值跨越頁面的更直接方法相比,此方案具有許多優點。假設查詢通常通過與相對較小的鍵值進行比較來限定,則執行器的多數工作將使用主要列條目完成。只有在將結果集發送到客戶端時,才會提取TOASTed 屬性的較大值(如果選擇了任何值)。因此,主表格比沒有任何行外儲存的情況下小得多,並且更多的列可以放入共享緩衝區快取中。排序集合也會縮小,並且排序將更常完全在記憶體中完成。一個小測試表明,包含典型 HTML 頁面及其 URL 的表格儲存在大約原始資料大小的一半中,包括TOAST表格,並且主表格僅包含大約 10% 的完整資料(URL 和一些小型 HTML 頁面)。與未TOASTed 的比較表格相比,沒有執行時間上的差異,在該表格中,所有 HTML 頁面都縮小到 7 kB 以適合。

65.2.2. 行外、記憶體內 TOAST 儲存 #

TOAST指標可以指向不在磁碟上,而是在目前伺服器程序記憶體中其他位置的資料。此類指標顯然無法長期存在,但它們仍然有用。目前有兩個子案例:指向間接資料的指標和指向擴展資料的指標。

間接TOAST指標僅指向儲存在記憶體中某處的非間接 varlena 值。最初創建此案例僅僅是為了驗證概念,但目前在邏輯解碼期間使用它,以避免可能需要建立超過 1 GB 的物理元組(因為將所有行外欄位值提取到元組中可能會這樣做)。此案例的用途有限,因為指標資料的建立者完全負責確保引用的資料在指標可能存在的時間內存在,並且沒有基礎結構可以協助此操作。

擴展TOAST指標對於磁碟上表示形式不特別適合計算用途的複雜資料類型很有用。例如,PostgreSQL 陣列的標準 varlena 表示形式包括維度資訊、任何空元素時的空值點陣圖,然後按順序排列所有元素的值。當元素類型本身是變長時,找到第 N 個元素的唯一方法是掃描所有先前的元素。由於其緊湊性,此表示形式適用於磁碟儲存,但是對於陣列的計算,最好使用 擴展解構表示形式,其中已識別出所有元素起始位置。此TOAST指標機制透過允許傳遞參考 Datum 指向標準 varlena 值(磁碟上的表示形式)或TOAST指向記憶體中某個擴展表示法的指標。此擴展表示法的細節取決於資料類型,但必須具有標準標頭,並符合src/include/utils/expandeddatum.h中給定的其他 API 要求。處理資料類型的 C 語言層級函式可以選擇處理任一種表示法。不知道擴展表示法,但僅將 PG_DETOAST_DATUM 應用於其輸入的函式,將自動接收傳統的 varlena 表示法;因此,可以逐步引入對擴展表示法的支援,一次一個函式。

TOAST指向擴展值的指標又進一步分為讀寫唯讀指標。無論哪種方式,指向的表示法都相同,但接收讀寫指標的函式可以就地修改引用的值,而接收唯讀指標的函式則不得修改;如果想要建立修改後的版本,必須先建立副本。這種區別以及一些相關的慣例,使得在查詢執行期間可以避免不必要的擴展值複製。

對於所有類型的記憶體內TOAST指標,TOAST管理程式碼確保不會意外地將此類指標資料儲存到磁碟上。記憶體內TOAST指標會自動擴展為正常的內聯 varlena 值,然後可能會轉換為磁碟上的TOAST指標,如果包含的 tuple 體積太大。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。