Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用INT4/INT类型替换INT8/BIGINT类型能够节省多少磁盘空间?

使用INT4/INT类型替换INT8/BIGINT类型能够节省多少磁盘空间?

作者头像
yzsDBA
发布于 2022-03-29 03:33:31
发布于 2022-03-29 03:33:31
89500
代码可运行
举报
运行总次数:0
代码可运行

使用INT4/INT类型替换INT8/BIGINT类型能够节省多少磁盘空间?

最近有人在IRC,Slack和Reddit上讨论使用int4/integer替代int8/bigint能够少4个字节。事实并非如此,来解释下。

进行下测试,我的环境64位,Ryzen处理器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ select version();
                                              version                                               
────────────────────────────────────────────────────────────────────────────────────────────────────
 PostgreSQL 15devel on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.2.0-7ubuntu2) 11.2.0, 64-bit
(1 row)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ create table test8 (id int8);
CREATE TABLE
 
$ create table test4 (id int4);
CREATE TABLE
 
$ insert into test8 select generate_series(1,1000000) i;
INSERT 0 1000000
 
$ insert into test4 select generate_series(1,1000000) i;
INSERT 0 1000000
 
$ \dt+ test*
                                  List of relations
 Schema │ Name  │ Type  │ Owner  │ Persistence │ Access method │ Size  │ Description
────────┼───────┼───────┼────────┼─────────────┼───────────────┼───────┼─────────────
 public │ test4 │ table │ depesz │ permanent   │ heap          │ 35 MBpublic │ test8 │ table │ depesz │ permanent   │ heap          │ 35 MB 
(2 rows)

创建单列表,插入1百万行记录。结果表大小一样大。

接着在32位环境上测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
postgres=# select version();
                                                         version                                                         
-------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 13.5 (Debian 13.5-0+deb11u1) on i686-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 32-bit
(1 row)
 
...
 
postgres=# \dt+ test*
                           List of relations
 Schema | Name  | Type  |  Owner   | Persistence | Size  | Description 
--------+-------+-------+----------+-------------+-------+-------------
 public | test4 | table | postgres | permanent   | 31 MB | 
 public | test8 | table | postgres | permanent   | 35 MB | 
(2 rows)

有趣的是,这里表大小减少了4M。为什么?是什么让它以这种方式工作?

答案是性能。由于性能原因PG将数据对齐到架构相关的大小,也就是说在64位系统上对齐8字节。究竟什么是对齐?这意味着分配的最小单位是8字节。如果技术上可行,PG不会将单个值拆分位多个8字节的块。这意味着,如果你表中只有4字节的列,无论如何都会使用8字节。如果有2个int4的列,他们将8字节对齐,仅使用这一个8字节。但是如果有int4,下一个列超过4字节,PG不会将另一个列拆分为“前一个8字节的4个字节的一部分,下一个中的一部分”,一切都将转到下一个8字节块中。

我们通过简单测试来看,仍在64位机器上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ create table test as select i::int4 as i1, i::int4 as i2 from generate_series(1,1000000) i;
SELECT 1000000

$ \dt+ test
                                 List of relations
 Schema │ Name │ Type  │ Owner  │ Persistence │ Access method │ Size  │ Description
────────┼──────┼───────┼────────┼─────────────┼───────────────┼───────┼─────────────
 public │ test │ table │ depesz │ permanent   │ heap          │ 35 MB 
(1 row)

在这里你可以看到,当我用两个 int4 列创建表时,它使用的空间与单个 int8 相同。现在让我们看看当我创建一个包含 int4 + int8 列的表时会发生什么。如果我的数学是正确的,它应该使用大约43MB:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ create table test48 as select i::int4 as i1, i::int8 as i2 from generate_series(1,1000000) i;
SELECT 1000000


$ \dt+ test48
                                  List of relations
 Schema │  Name  │ Type  │ Owner  │ Persistence │ Access method │ Size  │ Description
────────┼────────┼───────┼────────┼─────────────┼───────────────┼───────┼─────────────
 public │ test48 │ table │ depesz │ permanent   │ heap          │ 42 MB 
(1 row)

它稍微少一点,但正如所料,它显然接近我的预期。您还可以看到,对于更短类型也是如此。INT2(2字节)或者BOOL(1字节)仍占用整个8字节,除非他们可以容纳上一列/下一列的8字节块:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ create table test2 as select (i % 32000)::int2 as i2 from generate_series(1,1000000) i;
SELECT 1000000

$ \dt+ test2
                                  List of relations
 Schema │ Name  │ Type  │ Owner  │ Persistence │ Access method │ Size  │ Description
────────┼───────┼───────┼────────┼─────────────┼───────────────┼───────┼─────────────
 public │ test2 │ table │ depesz │ permanent   │ heap          │ 35 MB 
(1 row)

$ create table testb as select 'true'::bool as b from generate_series(1,1000000) i;
SELECT 1000000

$ \dt+ testb
                                  List of relations
 Schema │ Name  │ Type  │ Owner  │ Persistence │ Access method │ Size  │ Description
────────┼───────┼───────┼────────┼─────────────┼───────────────┼───────┼─────────────
 public │ testb │ table │ depesz │ permanent   │ heap          │ 35 MB 
(1 row)

他们都是35MB。为什么呢?还是性能。我不知道底层细节,但根据我的理解,处理器以与架构相关的块大小处理数据。64位处理器在64位上工作,意味着如果对int4(8字节块的一部分)执行某些操作,则必须添加操作以将其他32位归零。

差异太小不易测试,并会因负载的随机波动而相形见绌,但它确实存在。在重负载的机器上可能会有所区别。

因此可以通过切换到更小的数据类型来使用更少的磁盘空间,但您必须非常小心表中列的顺序。当设计表时,这可能非常容易但一旦部署应用程序就不再合理,现在只是更改架构以适应新功能。

还有一个因素要考虑。注意,表中放入了100万行和8MB的数据,表大小35MB,剩下的是什么?除了通常可见的列之外,PG中每行都有一些额外的系统列,而且他们大小非零,可以看到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ SELECT
    a.attname,
    t.typname,
    t.typlen,
    a.attnum
FROM
    pg_attribute a
    JOIN pg_type t ON a.atttypid = t.oid
WHERE
    a.attrelid = 'test8'::regclass
ORDER BY
    attnum;
 attname  │ typname │ typlen │ attnum
──────────┼─────────┼────────┼────────
 tableoid │ oid     │      4-6
 cmax     │ cid     │      4-5
 xmax     │ xid     │      4-4
 cmin     │ cid     │      4-3
 xmin     │ xid     │      4-2
 ctid     │ tid     │      6-1
 id       │ int8    │      81
(7 rows)

对于每一行,都有tableoid、cmax、xmax、cmin和ctid(tableoid和ctid在数据文件中并不存在),然后再表格中有“真实列”:

https://paste.depesz.com/s/77i

可以看到这些信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ select tableoid, cmax, xmax, cmin, xmin, ctid, id from test8 limit 1;
 tableoid │ cmax │ xmax │ cmin │ xmin │ ctid  │ id
──────────┼──────┼──────┼──────┼──────┼───────┼────
   3076960001773  (0,1)1
(1 row)

可以在手册中找到有关列的含义描述。从另一方面说,如果在表中添加了一个4字节的列,那么在它旁边添加另一个4字节的列(磁盘空间方面)基本上是不会增加的。

看一个真实的表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
=> select 'col_' || a.attnum, a.atttypid::regtype, a.attlen from pg_attribute a where a.attrelid = 'accounts'::regclass and attnum > 0;
 ?column? │          atttypid           │ attlen 
══════════╪═════════════════════════════╪════════
 col_1    │ bigint                      │      8
 col_2    │ text                        │     -1
 col_3    │ timestamp without time zone │      8
 col_4    │ timestamp without time zone │      8
 col_5    │ text                        │     -1
 col_6    │ timestamp without time zone │      8
 col_7    │ bigint                      │      8
 col_8    │ text                        │     -1
 col_9    │ bigint                      │      8
 col_10   │ bigint                      │      8
 col_11   │ bigint                      │      8
 col_12   │ bigint                      │      8
 col_13   │ text                        │     -1
 col_14   │ text                        │     -1
 col_15   │ text                        │     -1
 col_16   │ bigint                      │      8
 col_17   │ bigint                      │      8
 col_18   │ boolean                     │      1
 col_19   │ text                        │     -1
 col_20   │ text                        │     -1
 col_21   │ text                        │     -1
 col_22   │ text                        │     -1
 col_23   │ text                        │     -1
 col_24   │ text                        │     -1
 col_25   │ boolean                     │      1
 col_26   │ boolean                     │      1
 col_27   │ text                        │     -1
 col_28   │ text                        │     -1
 col_29   │ text                        │     -1
 col_30   │ text                        │     -1
 col_31   │ text                        │     -1
 col_32   │ bigint                      │      8
 col_33   │ bigint                      │      8
 col_34   │ text                        │     -1
 col_35   │ bigint                      │      8
 col_36   │ text                        │     -1
 col_37   │ text                        │     -1
 col_38   │ text                        │     -1
 col_39   │ text                        │     -1
 col_40   │ bigint                      │      8
 col_41   │ text                        │     -1
 col_42   │ bigint                      │      8
 col_43   │ bigint                      │      8
(43 rows)

attnum > 0表示过滤掉系统列,-1的attlen表示数据长度可变,具体取决于实际有多少数据。

由于都是texts,估算每行大小比较复杂,但是假设他们每个只占8个字节块,鉴于列的排列方式,只有第25和26列可以放入单个8字节块中。所以,总行大小(无系统列)42 * 8 bytes = 336 bytes

现在,将每个Int8更改为int4,可以将第9-12列合并为2个8字节块,将第16-17列合并一个,对于32,33和42,43也是如此。总计296字节。这意味着我们将每行节省40字节。注意,假设23个文本列都不会适应超过8字节,猜猜它的可能性。

可以简单的看下行的实际宽度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
=> explain select * from accounts;
                            QUERY PLAN                             
═══════════════════════════════════════════════════════════════════
 Seq Scan on accounts  (cost=0.00..3979.23 rows=100323 width=1113)
(1 row)

每行超过1kb,在此节省40字节或多或少等于舍入误差。所以在某些情况下,使用int4/int2是有益的。可以通过使用较小的数据类型来节省一些磁盘空间,但是差异并没有那么大。需要仔细规划。

下面脚本有助于PG列找到更好的对齐方式:

https://github.com/NikolayS/postgres_dba/blob/master/sql/p1_alignment_padding.sql

原文

https://www.depesz.com/2022/02/13/how-much-disk-space-you-can-save-by-using-int4-int-instead-of-int8-bigint/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 yanzongshuaiDBA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Postgresql模糊匹配案例(包括中文前后模糊)
test 04 使用对应的pattern ops走索引在zh_CN也是列时也走索引
mingjie
2022/05/12
2.6K0
Postgresql垃圾回收原理分析
间隔删除数据,使用ctid(页面号,lp号)作为条件,发现数据并没有真正的从页面中删除
mingjie
2022/05/12
8860
Postgresql垃圾回收原理分析
PostgreSQL查看表结构语句
在PG里面查看表结构,不如mysql那样show create table xx 这样方便。 
保持热爱奔赴山海
2022/11/14
4.5K0
PostgreSQL 磁盘空间的保护伞 PG_repack VS 表膨胀
PG 最近的使用中,发现这个数据库确确实实是一个无底洞,东西太多了,但学习一样东西都是通过主干和分支的方式来学习,后续的学习其实有的时候是靠自觉和运气。
AustinDatabases
2019/08/30
1.9K0
PostgreSQL 磁盘空间的保护伞 PG_repack  VS 表膨胀
聊聊PostgreSQL表膨胀
PostgreSQL Basic PG中的MVCC(多版本并发)设计目的是读不阻塞写。PG中的所有的insert和update操作都是创建新的一行数据;update和delete都不是立即删除旧版本无用的数据。tuple是否可见是由snapshot决定。 PG中追踪每个表的Block可见性是通过表的vm文件。Table或者Index的可用空间管理是通过表或者索引的fsm文件管理,它是一个2级的binary tree,最底层存储了每个page可用空间,最上层聚合最低层的信息。 PG目前支持多种
用户4700054
2023/02/26
1.9K0
聊聊PostgreSQL表膨胀
PostgreSQL基础(五):PostgreSQL基本操作和数据类型介绍
在PGSQL中,写SQL语句时,单引号用来标识实际的值。双引号用来标识一个关键字,比如表名,字段名。
Lansonli
2024/09/17
7070
Greenplum性能优化之路 --(三)ANALYZE
基于规则的优化器,就是优化器在优化查询计划的时候,是根据预先设置好的规则进行的,这些规则无法灵活改变。举个例子,索引优先于扫描,这是一个规则,优化器在遇到所有可以利用索引的地方,都不会选择扫描。这在多数情况下是正确的,但也不完全如此:
lambgong
2020/08/25
4.4K0
Greenplum性能优化之路 --(三)ANALYZE
vacuum full空间不释放的原因及过程模拟
vacuum full本质上是创建了一张新的表,会创建该表的一个新拷贝,并且在操作完成之前都不会释放旧的拷贝。因此在进行vacuum full操作的时候是会加上一个ACCESS EXCLUSIVE级别的锁,所以一般只有当我们需要从表中回收大量磁盘空间的,即膨胀率很高的表才会去做vacuum full的操作。
AiDBA宝典
2023/04/27
1.8K0
vacuum full空间不释放的原因及过程模拟
PG 13新特性汇总
PostgreSQL 10 版本开始支持逻辑复制,在12版本之前逻辑复制仅支持普通表,不支持分区表,如果需要对分区表进行逻辑复制,需单独对所有分区进行逻辑复制。
AiDBA宝典
2023/08/09
1.5K0
PG 13新特性汇总
Postgresql源码(31)Btree索引相关系统表和整体结构
《Postgresql源码(30)Postgresql索引基础B-linked-tree》
mingjie
2022/07/14
6930
《Postgresql 内幕探索》读书笔记 - 第一章:集簇、表空间、元组
PostgreSQL天然集群,多个集群可以组成集簇,有点类似军队的连、团、旅这样的组织规则。对于我们日常学习使用的单节点则是单个集簇单个集群,自己就是集群。
阿东
2023/06/27
7800
《Postgresql 内幕探索》读书笔记 - 第一章:集簇、表空间、元组
大数据标签查询优化实战之pg_roaringbitmap
pg_roaringbitmap是一个基于roaringbitmap而实现的压缩位图存储数据插件,支持roaring bitmap的存取、集合操作,聚合等运算。
腾讯云数据库 TencentDB
2022/01/18
2.2K1
大数据标签查询优化实战之pg_roaringbitmap
PostgreSQL13新特性解读-Btree索引去重Deduplication
PostgreSQL13.0于2020年9月24日正式release,13版本的PG带来很多优秀特性:比如索引的并行vacuum,增量排序,btree索引deduplication,异构分区表逻辑订阅等。在这里面最闪亮的特性非deduplication莫属。
数据库架构之美
2020/11/09
1.6K0
PostgreSQL13新特性解读-Btree索引去重Deduplication
技术译文 | MySQL 添加主键可以节省磁盘空间吗?
本文和封面来源:https://www.percona.com/,爱可生开源社区翻译。
爱可生开源社区
2024/03/07
3430
技术译文 | MySQL 添加主键可以节省磁盘空间吗?
PgSQL - 内核插件 - pg_dirtyread
表中删除了记录,并且没有进行vacuum,此时可以通过pg_dirtyread扩展读取死记录。
yzsDBA
2024/04/12
3040
PgSQL - 内核插件 - pg_dirtyread
PostgreSQL技术大讲堂 - 第28讲:索引内部结构
PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUG PG技术大讲堂。
用户5892232
2023/09/08
3610
PostgreSQL技术大讲堂 - 第28讲:索引内部结构
从Oracle到PostgreSQL:Storage Index 特性 vs BRIN索引
导读:本文介绍 PostgreSQL 中的BRIN索引。为什么引人注意专门单独讲述这个性能?因为这就是活脱脱的 Oracle Exadata 中的 Storage Index 和 Oracle Database 12.1.0.2 中的新功能 Zone Maps。
数据和云01
2019/06/19
5500
在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)
这里假设,你已经在 k8s 上部署好了基于 Citus 扩展的分布式 PostgreSQL 集群。
为少
2022/03/31
2.8K0
在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)
MOP 系列|MOP 三种主流数据库索引简介
MOP 不用多说了,我这里指的就是 MySQL、Oracle、PostgreSQL 三种目前最主流的数据库,MOP 系列打算更新 MOP 三种数据库的索引知识、高可用架构及常用 SQL 语句等等,今天打算介绍一下这三种数据库的索引基础知识。
JiekeXu之路
2024/04/30
2650
MOP 系列|MOP 三种主流数据库索引简介
PostgreSQL常用语句
注意:select current_setting(‘server_version_num’);返回类型为text,如果需要可以转换为interger
Java架构师必看
2021/06/10
8730
推荐阅读
相关推荐
Postgresql模糊匹配案例(包括中文前后模糊)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验