Postgres 之 TOAST技术
作者:瀚高PG实验室 (Highgo PG Lab)
TOAST简介
首先,Toast是一个名字缩写,全写是The OverSized Attribute Storage Technique(超尺寸字段存储技术)顾名思义,是说超长字段在Postgres的一个存储方式;
Postgres采用的存储默认是每个页面存储固定8Kb大小的数据,并且元组不允许跨页面存储,所以并不能直接存储大字段数据;
Toast就是为此应运而生,它会将大字段值压缩或者分散为多个物理行来存储;
对于用户来说完全不用关注这一技术实现,完全是透明的。
TOAST的存储方式
Postgres的部分类型数据支持toast,因为有些字段类型是不会产生大字段数据的,完全没必要用到Toast技术(比如date,time,boolean等);
支持Toast的数据类型应当时变长的(variable-length);
当表中字段任何一个有Toast,那这个表都会有这一个相关联的Toast表,OID被存储在pg_class.reltoastrelid里面;
超出的的数值将会被分割成chunks,并最多toast_max_chunk_size 个byte(缺省是2Kb)
当存储的行数据超过toast_tuple_threshold值(通常是2kB),就会触发toast存储;
toast将会压缩或者移动字段值直到超出部分比toast_tuple_targer值小(这个值通常也是2KB)。
Toast4种策略
策略 |
说明 |
PLAIN |
避免压缩和行外存储。 |
EXTENDED |
允许压缩和行外存储。 |
EXTERNA |
允许行外存储,但不许压缩。 |
MAIN |
允许压缩,但不许行外存储。 |
更改表的存储方式为TOAST
ALTER TABLE table_name ALTER COLUMN column_name SET STORAGE {PLAIN | EXTENDED | MAIN | EXTERNAL};
示例:
testdb=# select relname,relfilenode,reltoastrelid from pg_class where relname='toast_t';
relname | relfilenode | reltoastrelid
---------+-------------+---------------
toast_t | 65584 | 65587
(1 row)
testdb=# create table toast_t1(dd character varying);
CREATE TABLE
testdb=#
testdb=# \d+ toast_t1
Table "public.toast_t1"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------------------+-----------+----------+--------------+-------------
dd | character varying | | extended | |
testdb=# alter table toast_t1 alter column dd set storage main;
ALTER TABLE
testdb=# \d+ toast_t1
Table "public.toast_t1"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-------------------+-----------+---------+--------------+-------------
dd | character varying | | main | |
TOAST额外的三个字段
字段名 |
属性 |
chunk_id |
标识TOAST表的OID字段 |
chunk_seq |
chunk的序列号,与chunk_id的组合唯一索引可以加速访问 |
chunk_data |
存储TOAST表的实际数据 |
testdb=# select relname,relfilenode,reltoastrelid from pg_class where relname='toast_t1';
relname | relfilenode | reltoastrelid
----------+-------------+---------------
toast_t1 | 65596 | 65599
(1 row)
通过以上语句,我们查到 blog 表的 oid 为16441,其对应 TOAST 表的 oid 为16444(关于 oid 和 pg_class 的概念,请参考PG官方文档),那么其对应 TOAST 表名则为: pg_toast.pg_toast_65596(注意这里是 toast_t1表的 oid ),我们看下其定义:
testdb=# \d+ pg_toast.pg_toast_65596;
TOAST table "pg_toast.pg_toast_65596"
Column | Type | Storage
------------+---------+---------
chunk_id | oid | plain
chunk_seq | integer | plain
chunk_data | bytea | plain
TOAST表的计算
计算一个表的大小时要注意统计Toast的大小,因为对超长字段存储时,在基础表上可能只存了20%,另外的数据都存到了Toast里面去了,计算大小时要结合起来看,索引也是一样,对于表里有main或者extended类型的会创建Toast表,两者的关联是通过pg_class里的OID去关联的。
1.TOAST表关联查询
Example a:
[postgres@localhost ~]$ psql
psql (9.2.3)
Type "help" for help.
postgres=# create table toast_t(id int);
CREATE TABLE
postgres=# select relname,reltoastrelid from pg_class where relname = 'toast_t';
relname | reltoastrelid
----------+---------------
toast_t | 0
(1 row)
postgres=# \d+ toast_t
Table "public.toast_t"
Column | Type | Modifiers | Storage | Stats target | Description
-
--------+---------+-----------+---------+--------------+-------------
i
id | integer | | plain | |
Has OIDs: no上面的字段没有toast表,因为字段int是定长的。
Example b:
postgres=# create table toast_t(id int,vname varchar(48),remark text);
postgres=# select relname,reltoastrelid from pg_class where relname = 'toast_t';
relname | reltoastrelid
----------+---------------
toast_t | 65587
(1 row)
postgres=# select relname from pg_class where oid = 65587;
relname
----------------
pg_toast_65584
(1 row)
2.TOAST表计算大小
testdb=# \d+ toast_t
Table "public.toast_t"
Column | Type | Modifiers | Storage | Stats target | Description
--------+-----------------------+-----------+----------+--------------+-------------
id | integer | | plain | |
vname | character varying(48) | | extended | |
remark | text | | extended | |
postgres=# select oid,relname,reltoastrelid from pg_class where relname = 'toast_t';
oid | relname | reltoastrelid
-------+---------+---------------
65584 | toast_t | 65587
postgres=# insert into toast_t select generate_series(1,2),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful',500);
INSERT 0 2000
postgres=# insert into toast_t select generate_series(1,2),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful,Remark here!!',2000);
I
INSERT 0 2
postgres=# select pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from toast_t limit 2;
pg_column_size | pg_column_size | pg_column_size
----------------+----------------+----------------
4 | 29 | 154
4 | 29 | 154
(2 rows)
--查看基础表和Toast的大小
testdb=# select pg_relation_size(65584 ); 或 select pg_size_pretty(pg_relation_size(65576));
pg_relation_size
------------------
450560
(1 row)
postgres=# select pg_relation_size(65587);
pg_relation_size
------------------
0
(1 row)
--文本数据量增多,这时可以看到toast表字段大小在2kb左右时有大小了
postgres=# insert into toast_t select generate_series(3,4),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful,Remark here!!',4000);
INSERT 0 2
postgres=# select pg_relation_size(65587);
pg_relation_size
------------------
0
(1 row)
postgres=# insert into toast_t select generate_series(5,6),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful,Remark here!!',5500);
INSERT 0 2
postgres=# select pg_relation_size(65587);
pg_relation_size
------------------
8192
(1 row)
postgres=# select pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from toast_t;
pg_column_size | pg_column_size | pg_column_size
----------------+----------------+----------------
4 | 29 | 154
4 | 29 | 154
4 | 29 | 851
4 | 29 | 851
4 | 29 | 851
4 | 29 | 851
4 | 29 | 1651
4 | 29 | 1651
4 | 29 | 2247
4 | 29 | 2247
(10 rows)
postgres=# insert into toast_t select generate_series(1,2),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful,Remark here!!',10000);
INSERT 0 2
postgres=# select pg_relation_size(65584);
pg_relation_size
------------------
8192
(1 row)
postgres=# select pg_relation_size(65587);
pg_relation_size
------------------
16384
(1 row)
postgres=# insert into toast_t select generate_series(7,8),repeat('come here'||'^_^',2),repeat('^_^ It is wonderful,Remark here!!',20000);
INSERT 0 2
postgres=# select id,pg_column_size(id),pg_column_size(vname),pg_column_size(remark) from toast_t;
id | pg_column_size | pg_column_size | pg_column_size
----+----------------+----------------+----------------
1 | 4 | 29 | 154
2 | 4 | 29 | 154
1 | 4 | 29 | 851
2 | 4 | 29 | 851
1 | 4 | 29 | 851
2 | 4 | 29 | 851
3 | 4 | 29 | 1651
4 | 4 | 29 | 1651
5 | 4 | 29 | 2247
6 | 4 | 29 | 2247
1 | 4 | 29 | 4050
2 | 4 | 29 | 4050
7 | 4 | 29 | 8056
8 | 4 | 29 | 8056
(14 rows)
postgres=# select pg_relation_size(65584);
pg_relation_size
------------------
8192
(1 row)
postgres=# select pg_relation_size(65587);
pg_relation_size
------------------
32768
(1 row)
可以看到后插入的数据随着字段内容的增多,toast段一直在变大。基础表的大小没有变化。
这个和Oracle存储的大字段内容比较像,Oracle存储Blob类的数据时也是指定另外的segment来存储,而不是在原表中存储,当然可以设置enable storage in row来指定表中存储
Toast的优点
1.可以存储超长超大字段,避免之前不能直接存储的限制
2.物理上与普通表是分离的,检索查询时不检索到该字段会极大地加快速度
3.更新普通表时,该表的Toast数据没有被更新时,不用去更新Toast表
Toast的缺点
1.对大字段的索引创建是一个问题,有可能会失败,其实通常也不建议在大字段上创建,全文检索倒是一个解决方案。
2.大字段的更新会有点慢,其它DB也存在。
Postgres 之 TOAST技术相关推荐
- postgresql修炼之道_PostgreSQL的TOAST技术
本文参考: PostgreSQL TOAST 技术理解 <PostgreSQL修炼之道> 一.TOAST是什么? TOAST是"The Oversized-Attribute S ...
- toast 技术之一
os: centos 7.4 db: postgresql 9.6 TOAST的全称是: 超尺寸属性存储技术(The Oversized-Attribute Storage Technique). P ...
- PostgreSQL 解决 “大 value”问题的 存储技术 -- TOAST(The Oversized Attributes Storage Technique)
文章目录 前言 TOAST 基本策略 及 相关存储策略生效方式 TOAST 机制 的实现 TOAST 写链路的实现 TOAST 读链路的实现 结语 参考 前言 postgresql 作为关系型数据库 ...
- POSTGRESQL TOAST 数据扩展存储技术原理与优势
POSTGRESQL 的TOAST 功能是POSTGRESQL 本身提供对于可变长大字段的管理的方式. 讲此方面的的文章也是比较多的. 这里想提及的是,从TOAST 功能中对数据库系统设计的一种新的认 ...
- “王者对战”之 MySQL 8 vs PostgreSQL 10
本文是对两大开源关系型数据库MySQL.PostgreSQL做了详细的对比,欢迎大家在评论区发表自己的见解. 在这些版本之前,人们普遍认为,Postgres 在功能集表现更出色,也因其"学院 ...
- pg数据库和mysql8_MySQL8与PG10:新版本下的较量谁更胜一筹?
既然MySQL 8和PostgreSQL 10已经发布了,现在是时候回顾一下这两大开源关系型数据库是如何彼此竞争的. 在这些版本之前,人们普遍认为,Postgres在功能集表现更出色,也因其" ...
- PostgreSQL 14 版本发布,快来看看有哪些新特性!
文章目录 性能增强 数据类型和 SQL 管理功能 复制和恢复 安全增强 更多特性 大家好!我是只谈技术不剪发的 Tony 老师. PostgreSQL 全球开发组于 2021-05-20 发布了 Po ...
- MySQL8与PG10:新版本下的较量谁更胜一筹?
既然MySQL 8和PostgreSQL 10已经发布了,现在是时候回顾一下这两大开源关系型数据库是如何彼此竞争的. 在这些版本之前,人们普遍认为,Postgres在功能集表现更出色,也因其" ...
- 对PostgreSQL的 ctid 的初步认识
开始 ctid 和 物理存储有关,指的是 一条记录位于哪个数据块的哪个位移上面. postgres=# select ctid, * from gaotab;ctid | id | name | de ...
最新文章
- 谷歌AI专家爆料:90%的人都不知道,写不出好代码,是输在了这点上!
- 高并发服务优化篇:从RPC预热转发看服务端性能调优
- python怎么读excel文件-用python读写excel文件
- CCNA Cisco 端口配置(上)
- SparseArray代替HashMap
- leetcode117. 填充每个节点的下一个右侧节点指针 II(层序遍历08)
- python 傅里叶_基于python的图像傅里叶处理
- 网站开发综合技术 HTML
- Linux 配置vim编辑器
- Alibaba分层领域模型规约
- python服务器搭建nginx_python服务器环境搭建Flask,uwsgi和nginx
- maven 排除某个类_java-如何从Maven依赖项中排除某些程序包(在JAR中)?
- caffe特征可视化---python实现
- 【UE4】多视角相机捕获图像如何同屏拼接在一起
- 80后小学计算机课上的游戏,80后最值得回味的经典课间游戏
- 关于日记app的思考
- matlab中用simulink仿真六足机器人
- Google浏览器自动翻译失灵
- mac 版VirtualBox 安装win10方法 全屏
- C语言设计学生宿舍管理系统