微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

PostgreSQL中的The Oversized-Attribute Storage Technique(TOAST:超大属性存储技术)

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以适应。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐