postgreSQL中的TOAST技术

news/2024/11/30 0:46:08/

摘要:介绍postgreSQL中的TOAST技术

​ TOAST(The Oversize-Attribute Storage Technique)技术是PG提供的一种存储大数据的机制。

​ 要理解TOAST,我们要先理解页(BLOCK)的概念。在PG中,页是数据在文件存储中的基本单位,其大小是固定的且只能在编译期指定,之后无法修改,默认的大小为8KB。同时,PG不允许一行数据跨页存储。那么对于超长的行数据,PG就会启动TOAST,将大的字段压缩或切片成多个物理行存到另一张系统表中(TOAST表),这种存储方式叫行外存储

​ 在PostgreSQL中只有具有变长行为的数据类型(代码内部常称为varlena类型)才支持TOAST,比如TEXT数据类型。在向支持TOAST的属性中存储超过BLCKSZ/4字节(通常是2K)的数据时,TOAST机制才会被触发。在变长数据类型中,数值的前两位表示数值的存储方式:

  • 如果是00,该数值是普通的未TOAST的数值。
  • 如果是01,表示该数据被压缩过,剩下30位表示压缩后的数据大小,在这4字节之后还会附加4字节,表示未压缩的数据大小。
  • 如果是1x,数据头部仅用一字节。当存储的是短字符串(小于128字节),剩下7位表示字符串长度。当该字节为10000000时,表明这是线外存储的数据,紧随其后使用1字节记录指针大小,数据区域记录TOAST指针。

注意:线外存储时,也可能进行压缩,这种情况通过TOAST指针中的va_rawsize和va_extsize来进行比较。

​ 线外存储的数据会保存在称为TOAST表的普通表中。如果一个表中有一个属性是可TOAST的,那么该表将会有一个可关联的TOAST表,其OID存储在表的基本信息(也就是pg_class中的元组)的reltoastrelid属性中。如果没有关联的TOAST表,reltoastrelid属性为0。

​ TOAST机制有四种不同的存储策略(表中的Storage属性):

  • PLAN:避免压缩或者线外存储。不支持toast的数据类型
  • EXTENDED:允许压缩和线外存储,大多数可TOAST的数据类型的缺省值。
  • EXTERNAL:允许线外存储但是不允许压缩。可以使text和bytea字段上的字串操作更快。
  • MAIN:允许压缩,但是不允许线外存储。实际上这样的数据类型仍然可以进行线外存储。

存储策略可以使用ALTER TABLE SET STORAGE语句修改。

db=> create table t1 (c1 int, c2 text, c3 bytea);
CREATE TABLE
db=> \d+ t1Table "public.t1"Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------c1     | integer |           |          |         | plain    |             |              | c2     | text    |           |          |         | extended |             |              | c3     | bytea   |           |          |         | extended |             |              | 
Access method: heap
db=> select relname, reltoastrelid from pg_class where relname = 't1';relname | reltoastrelid 
---------+---------------t1      |         32785
(1 row)db=> select relname, relnamespace from pg_class where oid  = '32785';relname     | relnamespace 
----------------+--------------pg_toast_32782 |           99
(1 row)
-- pg_toast_32777的命名空间
db=> select nspname from pg_namespace where oid = 99;
-[ RECORD 1 ]-----
nspname | pg_toast--TOAST表的定义
db=> \d+ pg_toast.pg_toast_32777
TOAST table "pg_toast.pg_toast_32777"Column   |  Type   | Storage 
------------+---------+---------chunk_id   | oid     | plainchunk_seq  | integer | plainchunk_data | bytea   | plain
Owning table: "public.t1"
Indexes:"pg_toast_32777_index" PRIMARY KEY, btree (chunk_id, chunk_seq)
Access method: heap

TOAST表有三个字段:

  • chunk_id :用来表示特定 TOAST 值的 OID ,可以理解为具有同样 chunk_id 值的所有行组成原表的 TOAST 字段的一行数据。
  • chunk_seq: 用来表示该行数据在整个数据中的位置。
  • chunk_data : 该Chunk实际的数据。

对应的TOAST表的数据结构:

 */
typedef struct varatt_external
{int32		va_rawsize;		/* Original data size (includes header) */uint32		va_extinfo;		/* External saved size (without header) and* compression method */Oid			va_valueid;		/* Unique ID of value within TOAST table */Oid			va_toastrelid;	/* RelID of TOAST table containing it */
} varatt_external;

探究TOAST机制

​ c2只有10个字符,所以没有压缩,也没有行外存储。然后我们使用如下 SQL 语句增加 c2的长度,每次增长1倍,同时观察 c2的长度。

db=> insert into t1 values(1, 'title', '0123456789');                                                       
INSERT 0 1 
db=> select * from t1;c1 |  c2   |           c3           
----+-------+------------------------1 | title | \x30313233343536373839
(1 row)db=> select * from pg_toast.pg_toast_32782;chunk_id | chunk_seq | chunk_data 
----------+-----------+------------
(0 rows)

​ 反复执行如上过程,直到 pg_toast_32782表中有数据。直到 content 的长度为327680时(已远远超过页大小 8K),对应 TOAST 表中才有了数据,且长度都是略小于2K,这是因为 extended 策略下,先启用了压缩,然后才使用行外存储。

db=> update t1 set c2=c2||c2 where c1=1;
UPDATE 1
.....db=> select c1, length(c2) from t1;c1 |  length  
----+----------1 | 83886080
(1 row)db=> select * from pg_toast.pg_toast_32782;
-[ RECORD 1 ]-----------------------------------------------------------------------------------------------
chunk_id   | 32795
chunk_seq  | 0
chunk_data | .....
-[ RECORD 2 ]-----------------------------------------------------------------------------------------------
chunk_id   | 32795
chunk_seq  | 1
chunk_data | .....
-[ RECORD 3 ]-----------------------------------------------------------------------------------------------
chunk_id   | 32795
chunk_seq  | 2
chunk_data | .....db=> select chunk_id,chunk_seq,length(chunk_data) from pg_toast.pg_toast_32782;chunk_id | chunk_seq | length 
----------+-----------+--------32795 |         0 |   199632795 |         1 |   199632795 |         2 |   199632795 |         3 |   199632795 |         4 |   199632795 |         5 |   199632795 |         6 |   199632795 |         7 |   199632795 |         8 |   199632795 |         9 |   199632795 |        10 |   199632795 |        11 |   199632795 |        12 |   1996

参考:https://zhuanlan.zhihu.com/p/142281841


http://www.ppmy.cn/news/1212362.html

相关文章

【数据结构初阶】顺序表SeqList

描述 顺序表我们可以把它想象成在一个表格里面填数据,并对数据做调整; 那我们的第一个问题是:怎么样在创建出足够的空间呢? 我们可以去堆上申请,用一个指针指向一块空间,如果申请的空间不够,我…

制作麒麟V10-server-sp2镜像

1.挂载iso 文件到目录 mount -o loop /xxx.iso /mnt 这样mnt 目录下会有iso 解压相关的文件 2.修改源文件内容 vim /etc/yum.repos.d/ kylin_x86_64.repo 将里面的所有的源enabled 都改成 0 并添加一个新的源 [ks10-local] name Kylin Linux Advanced Server 10 - Local base…

Python与ArcGIS系列(四)在地图文档中加入图层

目录 0 简述1 将图层添加到地图文档中2 将图层插入到地图文档0 简述 本篇介绍如何利用arcpy实现将图层添加到地图文档中,以及将图层插入到地图文档指定的位置。 1 将图层添加到地图文档中 arcpy的mapping模块提供的AddLayer()函数可以实现将图层添加到地图文档中。功能本质上…

C++11特性

1.统一的列表初始化 C11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自 定义的类型,使用初始化列表时,可添加等号(),也可不添加。 struct Point {int _x;int _y; }; int main() {int x1 1;…

链表经典OJ题(链表回文结构,链表带环,链表的深拷贝)

目录 前言 1.反转一个单链表。 2. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。 3.链表的回文结构。 4.链表带环问题(*****) 4.1是否带环 4.2 入环的节点 5.随机链表的复制(链表的深拷贝) 前言…

人工智能基础_机器学习023_理解套索回归_认识L1正则---人工智能工作笔记0063

然后上一节我们说了L1,L2正则是为了提高,模型的泛化能力, 提高泛化能力,实际上就是把模型的公式的w,权重值,变小对吧. 然后我们这里首先看第一个L1正则,是怎么做到把w权重变小的 可以看到最上面是线性回归的损失函数,然后 L1可以看到,这个正则,就是在损失函数的基础上给损失…

Java必刷入门递归题×5(内附详细递归解析图)

目录 1.求N的阶乘 2.求12...N的和 3.顺序打印数字的每一位 4.求数字的每一位之和 5.求斐波拉契数列 1.求N的阶乘 (1)解析题目意思 比如求5的阶乘,符号表示就是5!;所以5!5*4*3*2*1我们下面使用简单的…

Matplotlib绘图一网打尽【持续更新ing】

2 绘制扇形图 绘制一个展示男女乘客比例的扇形图 得出男女的具体数字 sex_per df["Sex"].value_counts() sex_per # 把画图的包导入进来 import matplotlib.pyplot as plt# 这种绘图方式主要用于有多个子图以及复杂的图形布局的时候。fig,ax plt.subplots()# pl…