【PGCCC】Postgresql Toast 原理
前言
上篇博客讲述了 postgresql 如何存储变长数据,它的应用主要是在 toast 。Toast 在存储大型数据时,会将它存储在单独的表中(称为 toast 表)。因为 postgresql 的 tuple(行数据)是存在在 Page 中的,Page 的大小默认为 8KB。postgresql 不允许 tuple 跨页存储,所以当一行数据的某个列数据过大时,比如 text 类型的数据,超过了单页的大小,那么 postgresql 会将它压缩,切分,并且存储在另外的位置。这种技术就是称为 Toast。
Toast 表
如果我们创建了一张表,有了变长数据的列,那么就会有一个对应的 toast 表,专门存储过大的数据。下面展示了一个例子
test=# \d mytableTable "public.mytable"Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------id | integer | | not null | name | text | | |
Indexes:"mytable_pkey" PRIMARY KEY, btree (id)
可以看见 mytable 有个变长数据类型的列 name。然后我们来看看表对应的 oid。
test=# select oid from pg_class where relname = 'mytable';oid
--------127295
(1 row)
可以看到 mytable 表的 oid 为 127295,那么可以推断出它的 toast 表名称为 pg_toast_127295,其对应的 oid 仍然可以通过上述语句查出来(在此省略了),或者
test=# select reltoastrelid from pg_class where relname = 'mytable';reltoastrelid
---------------127298
(1 row)
我们继续来看看 pg_toast_127295 表有哪些列
test=# select attname, typname from pg_attribute inner join pg_type ON pg_type.oid = pg_attribute.atttypid where pg_attribute.attrelid = 127298;attname | typname
------------+---------chunk_data | byteachunk_seq | int4chunk_id | oidtableoid | oidctid | tidxmin | xidxmax | xidcmin | cidcmax | cid
(9 rows)
除去下面的通用属性,它只定义了三列

切分数据

假设现在向 mytable 表插入一条大型数据,长度为3MB,里面存储了一张图片,采用了base64格式。
INSERT INTO mytable (name) values ('j7161gnb1u23 ...... 972bh6==');
postgresql 在处理这条请求时,发现 name 是 text 类型,并且这次插入的数据过大。那么首先它会被压缩,假设被压缩成 1MB,压缩后仍然不满足大小,然后按照指定的大小(默认为 2048 byte)切分成 512 份。每一份切片对应 toast 表的一行数据,它们的 chunk_id 都是相同的,因为属于同一个数据,只是 chunk_seq 不同,对应着切片位置。chunk_data 列就是存储着切片的数据。
数据结构
下面的图展示了普通表的 heap 数据和 toast 表的数据联系。

toast 表的数据格式在上面已经讲解过了,这里详细介绍了普通表的数据格式,它存储了 toast 表的对应数据位置。首先它是 varattrib_1b_e 数据类型,
typedef struct
{uint8 va_header; uint8 va_tag; /* 类型 */char va_data[FLEXIBLE_ARRAY_MEMBER];
} varattrib_1b_e;
它的 va_tag 类型为 VARTAG_ONDISK ,后面的 va_data数据格式如下:
typedef struct varatt_external
{int32 va_rawsize; /* Original data size (includes header) */int32 va_extsize; /* External saved size (doesn't) */Oid va_valueid; /* Unique ID of value within TOAST table */Oid va_toastrelid; /* RelID of TOAST table containing it */
} varatt_external;
src/backend/access/common/toast_internals.c文件中的toast_save_datum函数实现了如何将数据存储到 toast 表,下面的程序经过简化处理了。
/*rel: 普通表value: 该列数据oldexternal: 用于更新用的
*/
Datum toast_save_datum(Relation rel, Datum value, struct varlena *oldexternal, int options) {struct varatt_external toast_pointer;Datum t_values[3];t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);t_values[2] = PointerGetDatum(&chunk_data);int32 chunk_seq = 0; // 切片索引// 开始切分数据,data_todo 是剩余数据的长度while (data_todo > 0){int i;// 计算切片长度chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);// 记录 chunk_seq 列值t_values[1] = Int32GetDatum(chunk_seq++);// 记录切片数据, data_p 记录了写入的数据位置SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);memcpy(VARDATA(&chunk_data), data_p, chunk_size);// 插入 toast 表toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);heap_insert(toastrel, toasttup, mycid, options, NULL);// toast 表设置了 chunk_id 和 chunk_seq 的符合唯一索引for (i = 0; i < num_indexes; i++){if (toastidxs[i]->rd_index->indisready)index_insert(toastidxs[i], t_values, t_isnull,&(toasttup->t_self),toastrel,toastidxs[i]->rd_index->indisunique ?UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,NULL);}data_todo -= chunk_size;data_p += chunk_size;}}
切片策略
上面展示了 postgresql 对变长数据的默认切片策略,其实它还提供了别的策略。总共支持四种,如下所示:
PLAIN,数据不能被压缩,也不能存储到 toast 表
EXTENDED,默认策略,可以被压缩,也可以存储到 toast 表
EXTERNAL,不能被压缩,但可以存储到 toast 表
MAIN,可以被压缩,也可以存储到 toast 表,只不过它的优先级比EXTENDED低
技术比较
postgresql 并没有使用跨页存储的方案,而是将大型数据单独放到其余地方存储。这样在条件过滤时,会比较好,因为它不需要读取这些大的数据,而且只有当该列被选中时,才会在返回数据时去读取。这种场景下,减少了磁盘 IO 的读取,提升了性能。
同样它也有对应的缺点,那就是写入大型的数据时,会比较慢。因为它需要切片,然后插入到 toast 表中,还要更新 toast 表的索引。如果采用跨页存储,那么还可以利用磁盘顺序写的高性能。在读取整行数据时候,还需要先去寻找 toast 表的索引,然后再去读取 toast 表的数据,相比较跨页存储,仍然无法使用磁盘顺序读的高性能。
作者:zhmin
链接:https://zhmin.github.io/posts/postgresql-toast/
#PG证书#PG考试#postgresql培训#postgresql考试#postgresql认证
相关文章:
【PGCCC】Postgresql Toast 原理
前言 上篇博客讲述了 postgresql 如何存储变长数据,它的应用主要是在 toast 。Toast 在存储大型数据时,会将它存储在单独的表中(称为 toast 表)。因为 postgresql 的 tuple(行数据)是存在在 Page 中的&…...
vue3使用element-plus,树组件el-tree增加引导线
vue3使用element-plus,树组件el-tree增加引导线 vue3项目element-plus,树组件el-tree增加引导线 element-plus组件库的el-tree样式 因为element的样式不满足当前的的需求,UI图,所以对el-tree进行增加了引导线 修改样式如下&am…...
AlphaFold3中文使用说明
目录 1. 在线网站用例1. 使用json输入预测蛋白结构 2. 本地命令行2.1 运行示例2.2 AF3输入输入格式JSON兼容性JSON最外层(Top-level)结构序列多序列比对MSA结构模板键 用户提供CCDs 2.3 AF3输出 AlphaFold3(AF3)可以通过在线网站或…...
使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
前言 假设您正在现有项目中集成这些包,而该项目的构建工具为 Webpack 或 Vite。同时,您对 Three.js 和 React 有一定的了解。如果您发现有任何错误或有更好的方法,请随时留言。 安装 npm install three types/three react-three/fiber rea…...
Koa进阶:掌握中间件和参数校验的艺术
目录 一、首先下载依赖 二、在index.js中引入koa-parameter,一般挂载这个中间件时会放在注册请求体的后面 三、使用实例 四、如果跟我们所需求的参数不同,返回结果直接会返回422 koa-parameter一般是用来校验请求传过来的参数是否是自己所需要的的 G…...
开源共建 | 长安链开发常见问题及规避
长安链开源社区鼓励社区成员参与社区共建,参与形式包括不限于代码贡献、文章撰写、社区答疑等。腾讯云区块链王燕飞在参与长安链测试工作过程中,深入细致地总结了长安链实际开发应用中的常见问题及其有效的规避方法,相关内容多次解答社区成员…...
【网络】深入理解 HTTPS:确保数据传输安全的核心协议
目录 引言一、HTTPS的基本概念1.1 什么是 HTTPS?1.2 HTTPS 的工作原理1.3 图解:HTTPS 通信过程1.4 HTTPS 与 HTTP 的区别1.5 为什么 HTTPS 更加重要? 二、SSL/TLS协议的核心2.1 SSL/TLS 协议的作用2.2 SSL/TLS 的工作流程2.2.1 握手阶段2.2.2…...
C/C++中使用MYSQL
首先要保证下载好mysql的库和头文件,头文件在/usr/include/mysql/目录下,库在/usr/lib64/mysql/目录下: 一般情况下,在我们安装mysql的时候,这些都提前配置好了,如果没有就重装一下mysql。如果重装mysql还是…...
【GD32】(一) 开发方式简介及标准库开发入门
文章目录 0 前言1 开发方式选择2 标准库模板的创建3 遇到的问题和解决方法 0 前言 因为项目关系,需要使用GD32。之前对此早有耳闻,知道这个是一个STM32的替代品,据说甚至可以直接烧录STM32的程序(一般是同型号)&#x…...
轻松上手:使用Docker部署Java服务
文章目录 1. 什么是Docker?2. 为什么使用Docker部署Java服务?3. 如何使用Docker部署Java服务?步骤1:创建Dockerfile步骤2:构建Docker镜像步骤3:运行Docker容器 4. 注意事项5. 结语推荐阅读文章 在当今的云计…...
wormml_vgg19
创建环境 mamba install libopencv hdf5 -c conda-forge conda create -n st python3.6.2手动导入包 mamba install blas1.0mkl -c conda-forge mamba install hdf51.8.20hac2f561_1 -c conda-forge mamba install libopencv3.4.2h20b85fd_0 -c conda-forge mamba install l…...
Rust学习(二):rust基础语法Ⅰ
Rust学习(二)——rust基础语法Ⅰ: 1、关键字: 了解编程语言的同学都清楚,关键字在一门编程语言中的意义,所谓关键字就是语言的创造者及后续开发者们,以及定义好的具有特殊含义和作用的单词&am…...
【WebRTC】视频发送链路中类的简单分析(下)
目录 1.任务队列节流发送器(TaskQueuePacedSender)1.1 节流控制器添加RTP数据包(PacingController::EnqueuePacket())1.2 监测是否要处理Packet(PacingController::MaybeProcessPackets()) 2.数据包路由&am…...
HTML(超文本标记语言)
HTML(超文本标记语言 - HyperText Markup Language)是一种用于创建网页的标准标记语言。 HTML 最初是由蒂姆・伯纳斯 - 李(Tim Berners - Lee)在 1990 年左右开发的。当时的目的是为了让世界各地的科学家能够方便地共享和交流信息…...
CatBoost中目标变量统计
CatBoost中的目标变量统计(Target Statistics)是其处理分类特征(Categorical Features)的核心技术之一。目标变量统计是一种特殊的编码方法,通过利用目标值信息生成数值特征,从而替代传统的独热编码或其他处…...
WSL与Ubuntu系统--使用Linux
WSL与Ubuntu系统--使用Linux 前言基础教学视频卸载链接网络配置方法1方法2 正式安装步骤步骤1 基本命令修改网络配置Ubuntu系统的导出与导入文件操作给Ubuntu创造界面--也就是在装一个有界面的UbuntuHyper-v与windows主机文件共享 前言 需要链接梯子,并且梯子十分稳…...
操作系统离散存储练习题
1. (简答题)分页存储管理系统具有快表,内存访问时间为2ns,检索快表时间为0.5ns,快表命中率为80%,求有效访问时间 -分析:首先访问缓存(快表),如果没有找到访问内存(页表&…...
性能高于Transformer模型1.7-2倍,彩云科技发布基于DCFormer架构通用大模型云锦天章
2017年,谷歌发布《Attention Is All You Need》论文,首次提出Transformer架构,掀开了人工智能自然语言处理(NLP)领域发展的全新篇章。Transformer架构作为神经网络学习中最重要的架构,成为后来席卷全球的一…...
PHP反序列化_3-漏洞利用
1. 信息收集与分析 确定目标应用程序:首先需要找到存在反序列化漏洞的 PHP 应用程序。这可能是一个网站、Web 服务、内部系统等。可以通过网络扫描、漏洞报告、安全评估等方式来发现潜在的目标。分析应用程序逻辑:了解目标应用程序的功能和业务逻辑&…...
2.初始sui move
vscode安装move插件 查看sui 客户端版本号 sui client --version 创建新项目 sui move new <项目名> sui move new hello_world 项目目录结构: hello_world ├── Move.toml ├── sources │ └── hello_world.move └── tests└── hello_world…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
