Postgresql - TOAST ( The Oversized-Attribute Storage Technique )


TOAST
PostgreSQL使用固定的页面大小(通常为8 KB),并且不允许元组跨越多个页面。因此,不可能直接存储非常大的字段值。为了克服这个限制,大字段值被压缩和/或分解成多个物理行。这对用户来说是透明的,对后端代码的影响很小。这项技术被亲切地称为TOAST。基础结构还用于改善内存中的大数据值的处理。
只有某些数据类型支持TOAST——没有必要将开销强加在不能产生大字段值的数据类型上。为了支持TOAST,数据类型必须具有可变长度(Valela)表示,其中,任何存储值的第一个四字节字通常包含字节(包括其本身)的值的总长度。TOAST不限制数据类型的其余部分的表示。这些特殊的表示统称为烘烤值,通过修改或重新解释这个初始长度字来工作。
因此,支持可TOAST的数据类型的C-level功能必须小心如何处理潜在的TOAST输入值:输入可能实际上不包括由四字节长度的单词和内容组成,直到它被删除为止。(这通常是通过调用PG_DETOAST_DATUM 来完成的,但是在某些情况下,更有效的方法是可能的。)
TOAST篡夺了两个可变长度的bit,从而限制了TOAST-able 数据类型的任何值的逻辑大小为1 GB。当设置最高阶或最低阶位时,该值只有一个字节头,而不是正常的四字节头,并且该字节的剩余位以字节表示总基准大小(包括长度字节)。该替代方案支持比127字节短的值的空间有效存储,同时仍然允许数据类型在需要时增长到1 GB。单字节标头的值不在任何特定边界上对齐,而具有四字节标题的值在至少四字节边界上对齐;这种省略对齐填充提供了与短值相比显著的额外空间节省。作为一种特殊情况,如果单字节报头的剩余位全部为零(这对于自包含长度来说是不可能的),则该值是指向out-of-line数据的指针,如下面描述的几种可能的备选方案。这种吐司指针的类型和大小由存储在数据的第二字节中的代码决定。最后,当最高阶或最低阶位是明确的,但相邻位被设置时,数据的内容已经被压缩,并且必须在使用之前被解压缩。在这种情况下,四字节长度字的剩余位给出压缩数据的总大小,而不是原始数据。注意,对于out-of-line数据,压缩也是可能的,但可变长度的header不知道它是否已经发生了——而TOAST指针的内容则说明了这一点。
如上所述,有多种类型的TOAST指针数据。最古老和最常见的类型是指向存储在表中的out-of-line数据的指针,它与包含TOAST指针数据本身的表分开,但与表相关。当磁盘上存储的元组太大而不能按原样存储时,这些磁盘指针数据由TOAST管理代码(access/heap/tuptoaster.c)创建。TOAST指针数据可以包含指向内存中其他地方出现的out-of-line数据的指针。这样的数据必须是短命的,并且永远不会出现在磁盘上,但是它们对于避免复制和冗余处理大数据值非常有用。
用于in-line或out-line压缩数据的压缩技术是LZ压缩技术的一个相当简单和非常快的成员。

如果表的任何列都是TOAST-able。则表将有一个相关的TOAST表,其OID存储在表的pg_class.reltoastrelid条目中。
out-of-line值(如果使用后压缩)被划分成最多TOAST_MAX_CHUNK_SIZE bytes(默认情况下,该值被选择为四个块行将适合于一页,使其约为2000字节)。每个块被存储为属于拥有表的TOAST表中的单独行。每个TOAST表都有列chunk_id(一个识别特定TOAST值的OID)、chunk_seq(其值中的块的序列号)和chunk_data(块的实际数据)。chunk_id and chunk_seq 的唯一索引提供了对值的快速检索。一个表示磁盘TOAST值的out-of-line数据的指针数据需要存储TOAST表中的OID,以查找特定值(其 chunk_id)的OID。为了方便起见,指针数据还存储逻辑数据大小(原始未压缩数据长度)和物理存储大小(如果应用压缩不同)。允许可变长度的头字节,disk上的TOAST指针数据的总大小因此是18字节,而不管所代表的值的实际大小。

只有当要存储在表中的行值大于TOAST_TUPLE_THRESHOLD阈值字节(通常为2 KB)时才触发TOAST管理代码。TOAST代码将压缩和/或移动字段值,直到行值小于TOAST_TUPLE_TARGET目标字节(通常也为2 KB),或者没有更多的增益。在更新操作期间,不变字段的值通常保持为as-is;因此,如果没有in-line值变化,则具有超出行值的行的更新不会产生TOAST成本。

TOAST管理代码识别四种不同的策略,用于在disk中存储可TOAST的列。
ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
PLAIN:防止压缩或out-of-line存储
EXTENDED :允许压缩和out-of-line存储。这是大多数TOAST-able 数据类型的默认值。压缩将首先尝试,如果行仍然太大,然后out-of-line存储。
EXTERNAL :允许out-line存储但不压缩。使用外部将使在宽文本和bytea列上的子串操作更快,因为这些操作被优化以只在不压缩时获取所需的部分行外值。
MAIN :允许压缩但不in-line存储。
相比于更简单的方法,如允许行值跨越页面,该方案具有许多优点。假定查询通常通过比较相对较小的键值来限定,则执行器的大部分工作将使用主行条目来完成。在将结果集发送给客户端时,TOAST属性的大值只会被拉出。因此,主表要小得多,并且它的行数比没有任何out-of-line存储的情况更适合于共享缓冲区高速缓存。排序集也收缩,排序通常会在内存中完成。


TOAST指针可以指向不是在磁盘上的数据,而是指向当前服务器进程内存中的其他位置。这样的指针显然不能长期存在,但它们仍然是有用的。目前有两个子实例:指向间接数据的指针和指向扩展数据的指针。间接TOAST指针简单地指向存储在某个内存中的非间接可变长度值。这种情况最初只是作为概念的证明而创建的,但目前在逻辑解码期间使用,以避免可能必须创建超过1 GB的物理元组(因为将所有的外部字段值拉入元组可能会发生)。由于指针数据的创建者完全有责任,只要指针存在,引用的数据就可以生存,并且没有基础设施来帮助这一点,这种情况是有限的。
扩展的TOAST指针对于复杂的数据类型来说是有用的,这些数据类型的磁盘表示并不特别适合计算的目的。PostgreSQL阵列的标准可变长度表示包括维数信息,如果有空元素,则是空的位图,然后按顺序排列所有元素的值。当元素类型本身是可变长度时,找到第N个元素的唯一方法是扫描所有前面的元素。这种表示适合于磁盘存储,因为它的紧凑性,但是对于数组的计算,它有一个更好的“扩展”或“解构”的表示,其中所有元素的起始位置已经被识别。TOAST指针机制通过允许通过引用数据指向标准可变长度值(ON盘表示)或指向内存中某个扩展表示的TOAST指针来支持这一需求。这个扩展表示的细节取决于数据类型,尽管它必须有一个标准的报头,并满足src/include/utils/expandeddatum.h中的其他API要求。与数据类型一起工作的c-level函数可以选择处理任何表示。不知道扩展表示的函数,但简单地将PG_DETOAST_DATUM应用到它们的输入,将自动接收传统的可变长度表示;因此,对于扩展表示的支持可以逐级引入,每次一个函数。
扩展值的TOAST指针进一步分解为读写和只读指针。指向指向的表示方式是相同的,但是接收读写指针的函数允许修改所引用的值,而接收只读指针的函数则不能;它必须首先创建一个拷贝,如果它想要修改一个值的版本。这种区分和一些相关的约定使得避免在查询执行期间扩展值的不必要复制成为可能。
对于所有类型的内存TOAST指针,TOAST管理代码确保没有这样的指针数据意外地被存储在磁盘上。在内存中,TOAST指针在存储之前自动扩展到普通的in-line可变长度值,然后可能转换成磁盘上的TOAST指针,如果包含的元组否则太大。

你可能感兴趣的:(Postgresql,Postgresql,原理,数据文件,TOAST)