作者:瀚高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

避免压缩和行外存储。
只有那些不需要 TOAST 策略就能存放的数据类型允许选择(例如 int 类型),而对于 text 这类要求存储长度超过页大小的类型,是不允许采用此策略的。

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技术相关推荐

  1. postgresql修炼之道_PostgreSQL的TOAST技术

    本文参考: PostgreSQL TOAST 技术理解 <PostgreSQL修炼之道> 一.TOAST是什么? TOAST是"The Oversized-Attribute S ...

  2. toast 技术之一

    os: centos 7.4 db: postgresql 9.6 TOAST的全称是: 超尺寸属性存储技术(The Oversized-Attribute Storage Technique). P ...

  3. PostgreSQL 解决 “大 value”问题的 存储技术 -- TOAST(The Oversized Attributes Storage Technique)

    文章目录 前言 TOAST 基本策略 及 相关存储策略生效方式 TOAST 机制 的实现 TOAST 写链路的实现 TOAST 读链路的实现 结语 参考 前言 postgresql 作为关系型数据库 ...

  4. POSTGRESQL TOAST 数据扩展存储技术原理与优势

    POSTGRESQL 的TOAST 功能是POSTGRESQL 本身提供对于可变长大字段的管理的方式. 讲此方面的的文章也是比较多的. 这里想提及的是,从TOAST 功能中对数据库系统设计的一种新的认 ...

  5. “王者对战”之 MySQL 8 vs PostgreSQL 10

    本文是对两大开源关系型数据库MySQL.PostgreSQL做了详细的对比,欢迎大家在评论区发表自己的见解. 在这些版本之前,人们普遍认为,Postgres 在功能集表现更出色,也因其"学院 ...

  6. pg数据库和mysql8_MySQL8与PG10:新版本下的较量谁更胜一筹?

    既然MySQL 8和PostgreSQL 10已经发布了,现在是时候回顾一下这两大开源关系型数据库是如何彼此竞争的. 在这些版本之前,人们普遍认为,Postgres在功能集表现更出色,也因其" ...

  7. PostgreSQL 14 版本发布,快来看看有哪些新特性!

    文章目录 性能增强 数据类型和 SQL 管理功能 复制和恢复 安全增强 更多特性 大家好!我是只谈技术不剪发的 Tony 老师. PostgreSQL 全球开发组于 2021-05-20 发布了 Po ...

  8. MySQL8与PG10:新版本下的较量谁更胜一筹?

    既然MySQL 8和PostgreSQL 10已经发布了,现在是时候回顾一下这两大开源关系型数据库是如何彼此竞争的. 在这些版本之前,人们普遍认为,Postgres在功能集表现更出色,也因其" ...

  9. 对PostgreSQL的 ctid 的初步认识

    开始 ctid 和 物理存储有关,指的是 一条记录位于哪个数据块的哪个位移上面. postgres=# select ctid, * from gaotab;ctid | id | name | de ...

最新文章

  1. 谷歌AI专家爆料:90%的人都不知道,写不出好代码,是输在了这点上!
  2. 高并发服务优化篇:从RPC预热转发看服务端性能调优
  3. python怎么读excel文件-用python读写excel文件
  4. CCNA Cisco 端口配置(上)
  5. SparseArray代替HashMap
  6. leetcode117. 填充每个节点的下一个右侧节点指针 II(层序遍历08)
  7. python 傅里叶_基于python的图像傅里叶处理
  8. 网站开发综合技术 HTML
  9. Linux 配置vim编辑器
  10. Alibaba分层领域模型规约
  11. python服务器搭建nginx_python服务器环境搭建Flask,uwsgi和nginx
  12. maven 排除某个类_java-如何从Maven依赖项中排除某些程序包(在JAR中)?
  13. caffe特征可视化---python实现
  14. 【UE4】多视角相机捕获图像如何同屏拼接在一起
  15. 80后小学计算机课上的游戏,80后最值得回味的经典课间游戏
  16. 关于日记app的思考
  17. matlab中用simulink仿真六足机器人
  18. Google浏览器自动翻译失灵
  19. mac 版VirtualBox 安装win10方法 全屏
  20. C语言设计学生宿舍管理系统

热门文章

  1. TYVJ1658 奶牛同盟
  2. 2021 平面设计趋势:对混乱的审美反应
  3. java产生随机数的方法_JAVA: java产生随机数的几种方式
  4. iPhone 4S陷信号门:中移动频段被苹果阉割
  5. C语言100题练习计划 13——快速排序果然名副其实
  6. 符文收集(回溯法)C/C++
  7. 颜色识别器APP隐私政策NEW
  8. potPlayer-windows安装包,皮肤,配置及使用
  9. 2019做网站如何赚钱?哪种网站赚钱
  10. 分享 11 个常用的 Nginx 性能优化参数工作