Postgresql使用固定的页面大小(通常为8kB),并且不允许元组跨越多个页面。因此,不可能直接存储非常大的字段值。为了克服这种限制,将大字段值压缩和/或分解成多个物理行。这对用户来说是透明的,对大多数后端代码只有很小的影响。该技术被称为TOAST。TOAST架构还用于改进内存中大数据值的处理。
只有某些数据类型支持TOAST - 不需要对无法生成大字段值的数据类型施加开销。为了支持TOAST,数据类型必须具有可变长度(varlena)表示,其中,通常,任何存储值的第一个四字节字包含以字节为单位的值的总长度(包括其自身)。TOAST不限制数据类型其余部分的表示。被TOAST的值的特殊的表示形式,是通过修改或者重新解释此初始长度字。因此,支持可以被TOAST的数据类型的C级别的函数必须注意它们如何处理可能的被TOAST的输入值:输入实际上可能不包含四字节长度的word和内容,直到它被TOAST解释。(这通常通过在对输入值执行任何 *** 作之前调用PG_DetoAST_DATUM来完成,但在某些情况下可以采用更有效的方法)
TOAST使用varlena length word的两个bit (大端机器的高位,小端机器的低位), 从而将可以被TOAST *** 作的数据类型的任何值的逻辑大小限制为1GB(2的30次方-1 bytes)。
当两个bit都为零时,该值是一个普通的非TOAST的数据类型的值,长度字的其余位以字节为单位给出总数据大小(包括长度字自身)。当设置最高位或最低位时,该值仅具有单字节头而不是正常的四字节头,并且该字节的其余位以字节为单位给出总数据大小(包括长度字节自身)。此备选方案支持空间高效存储短于127字节的值,同时仍允许数据类型在需要时增长到1GB。具有单字节头的值不在任何特定边界上对齐,而具有四字节头的值在至少四字节边界上对齐;与短值相比,省略对齐填充提供了额外的空间节省。作为特殊情况,如果单字节头的剩余位全部为零(对于自包含长度而言是不可能的),则该值是指向外部数据的指针,具有如下面所描述的几种可能的替代方案。这种TOAST指针的类型和大小由存储在数据的第二个字节中的代码确定。最后,当最高位或最低位清零但相邻位置位时,数据的内容已被压缩,必须在使用前解压缩。在这种情况下,四字节长度字的剩余位给出压缩数据的总大小,而不是原始数据。请注意,对于外部数据也可以进行压缩,但是可变长头不会告诉它是否已经发生 - 而是TOAST指针的内容来告诉。
如上所述,有多种类型的TOAST指针datums。最老的和最常见的类型是指向存储在TOAST表中的外部数据的指针,该表与包含TOAST指针数据本身的表分开但与之相关联。当要存储在磁盘上的元组太大而无法按原样存储时,这些磁盘上的指针基准由TOAST管理代码(在access / heap / tuptoaster.c中)创建。更多细节见第68.2.1节。或者,TOAST指针datum可以包含指向内存中其他位置的out-of-line的指针。这些datums必然是短暂的,并且永远不会出现在磁盘上,但它们对于避免复制和冗余处理大数据值非常有用。
用于in-line或out-of-line压缩数据的压缩技术是LZ系列压缩技术中相当简单且非常快速的成员。有关详细信息,请参阅src/common/pg_lzcompress.c。
Out-of-line,on-disk TOAST storage
如果表的任意列是TOAST-able,则该表将具有关联的TOAST表,其OID存储在表的pg_class.reltoastrelID条目中。
Out-of-line值(在压缩后使用)划分为最多TOAST_MAX_CHUNK_SIZE个字节的块(默认情况下,选择此值使得四个chunk行适合一个页面,使其大约为2000个字节)。每个chunk都作为TOAST表中的单独行存储。每个TOAST表都有列chunk_ID(标识特定TOASTed值的OID),chunk_seq(其值中的块的序列号)和chunk_data(块的实际数据)。chunk_ID和chunk_seq上的唯一索引提供了对值的快速检索。因此,表示out-of-line基于磁盘TOASTed值的指针datum需要存储要查看的TOAST表的OID以及特定值的OID(其chunk_ID)。为方便起见,指针datum还存储逻辑datum大小(原始未压缩数据长度)和物理存储大小(如果应用压缩则不同)。因此,允许可变长的头字节,基于磁盘的TOAST指针数据的总大小为18字节,而不管所表示的值的实际大小。
仅当要存储在表中的行值宽于TOAST_TUPLE_THRESHolD字节(通常为2kB)时,才会触发TOAST管理代码。TOAST代码将压缩和/或移动字段值out-of-line,直到行值短于TOAST_TUPLE_TARGET字节(通常也是2kB,可调)或者不能获得更多增益。在UPDATE *** 作期间,未更改字段的值通常保持原样; 因此,如果没有任何out-of-line值发生更改,则具有out-of-line值的行的更新不会产生任何TOAST成本。
TOAST管理代码识别用于在磁盘上存储TOAST-able列的四种不同策略:
1.PLAIN:禁止压缩或out-of-line存储;对于可变长类型,禁止使用单字节头部。对于非TOAST数据类型列,这是唯一可行的策略。
2.EXTENDED:支持压缩或out-of-line存储。这是大多数支持TOAST数据类型的默认值。首先尝试压缩,然后在行仍然太大的情况下进行out-of-line存储。
3.EXTERNAL:允许out-of-line存储但不允许压缩。使用EXTERNAL将使宽文本和bytea列上的子字符串 *** 作更快(以增加的存储空间为代价),因为这些 *** 作被优化为在未压缩时仅获取外部值的所需部分。
4.MAIN:允许压缩但不允许out-of-line存储。(实际上,仍然会为这些列执行out-of-line存储,但只有在没有其他方法使行足够小以适合页面时才作为最后的手段)
每个TOAST-able数据类型为该数据类型的列指定默认策略,但是可以使用ALTER table ... SET STORAGE更改给定表列的策略。
可以使用ALTER table ... SET(toast_tuple_target = N)为每个表调整TOAST_TUPLE_TARGET
与更简单的方法(例如允许行值跨越页面)相比,该方案具有许多优点。假设查询通常通过与相对较小的键值进行比较来限定,执行程序的大部分工作将使用main row entry完成。TOASTed属性的大值只会在结果集发送到客户端时被拉出(如果选中的话)。因此,与没有任何out-of-line存储的情况相比,主表更小并且其更多行适合共享缓冲区高速缓存。排序集也缩小,排序通常完全在内存中完成。一个小小的测试显示,包含典型HTML页面及其URL的表格存储在大约一半的原始数据大小(包括TOAST表)中,并且主表仅包含大约10%的整个数据(URL和一些小的HTML)页)。与un-TOAST比较表相比,没有运行时间差异,其中所有HTML页面都被削减到7kB以适应。
总结以上是内存溢出为你收集整理的PostgreSQL中的The Oversized-Attribute Storage Technique(TOAST:超大属性存储技术)全部内容,希望文章能够帮你解决PostgreSQL中的The Oversized-Attribute Storage Technique(TOAST:超大属性存储技术)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)