创建表test,y字段插入null.

test=# create table test(x bigint,y bigint,z text);
CREATE TABLE
test=# insert into test values(11,null,'abcdefg');
INSERT 0 1
test=# select * from test;
x | y | z
----+---+---------
11 | | abcdefg
(1 row)

这条记录存储在数据页里面是:

(gdb) p	*lpp
$17 = {lp_off = 8152, lp_flags = 1, lp_len = 40}

占了40个字节,TupleHeader是24个字节,40-24=16。

我们再插入一条数据看看

test=# insert into test values(22,100,'xyz');
INSERT 0 1
test=# select * from test;
x | y | z
----+-----+---------
11 | | abcdefg
22 | 100 | abcdefg
(2 rows)

再看看新插入的记录:

(gdb) p	*lpp
$19 = {lp_off = 8104, lp_flags = 1, lp_len = 48}

这2条数据对比

第一条记录:

insert into test values(11,null,'abcdefg');

字段x是bigint,8字节

字段y是bigint,8字节

字段z是text,字符串strlen('abcdefg')=7,8字节对齐。

8152+40=8192

第二条记录:

insert into test values(22,100,'abcdefg');

8+8+8=24

24+TupleHeader=48字节

8104+48=8152

我们来看看第一条是如何存储NULL的:

代码:src/include/access/tupmacs.h

#define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))

这就是计算字段是否为NULL的宏

PG通过t_bits来标记是否为null.

struct HeapTupleHeaderData
{
union
{
HeapTupleFields t_heap;
DatumTupleFields t_datum;
} t_choice; ItemPointerData t_ctid; /* current TID of this or newer tuple (or a
* speculative insertion token) */ /* Fields below here must match MinimalTupleData! */ uint16 t_infomask2; /* number of attributes + various flags */ uint16 t_infomask; /* various flag bits, see below */ uint8 t_hoff; /* sizeof header incl. bitmap, padding */ /* ^ - 23 bytes - ^ */ bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs */ /* MORE DATA FOLLOWS AT END OF STRUCT */
};

t_bits是一个字节,那么就有8位。所有上面的宏需要根据ATT来移3位。ATT>>3

这是算这个字段是在第几个数组下标

例如我这里查询的是第一个字段1>>3 = 0 就是在第一个数组下标

ATT & 0x07  求字段顺序  第一条是 0 & 0x07 = 0 ,如果是第八个字段也是0,范围就是0-7

test=# select * from test;
x | y | z
----+-----+---------
11 | | abcdefg
22 | 100 | abcdefg
(2 rows)

第一条数据t_bits是这样的

0 0 0 0 0 1 0 1

根据上面的宏计算结果:

第一个字段:

(BITS)[(ATT) >> 3] = BITS[0]

(ATT) & 0x07 = 0 & 0x07 = 0

1 << 0 = 1

结果就是

0 0 0 0 0 1 0 1

0 0 0 0 0 0 0 1

!(BITS[0] & 1) = 0

代表数据不算为NULL.

第二个字段:

(BITS)[(ATT) >> 3] = BITS[0]

(ATT) & 0x07 = 1 & 0x07 = 1

1 << 1 = 2

结果就是

0 0 0 0 0 1 0 1

0 0 0 0 0 0 1 0

!(BITS[0] & 2) = 1

代表第二个字段为NULL

我们再插入一条数据

test=# insert into test values(null,null,'abcdefg');
INSERT 0 1
test=# select * from test;
x | y | z
----+-----+---------
11 | | abcdefg
22 | 100 | abcdefg
| | abcdefg
(3 rows)

我们主要是看新增加的这条数据

(gdb) p	*lpp
$42 = {lp_off = 8072, lp_flags = 1, lp_len = 32}
(gdb)

新插入的数据只占了32个字节 TupleHeader+8

t_bits = 0 0 0 0 0 1 0 0

利用上面的宏也很好的算出前面2个字段为NULL

我们来看看超过8个字段的情况

test=# create table test_more_column(
test(# col1 bigint,
test(# col2 bigint,
test(# col3 bigint,
test(# col4 bigint,
test(# col5 bigint,
test(# col6 bigint,
test(# col7 bigint,
test(# col8 bigint,
test(# col9 bigint,
test(# col10 bigint
test(# );
CREATE TABLE
test=# insert into test_more_column values(1,2,null,4,5,null,7,8,null,10);
INSERT 0 1
test=# select * from test_more_column ;
col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 | col9 | col10
------+------+------+------+------+------+------+------+------+-------
1 | 2 | | 4 | 5 | | 7 | 8 | | 10
(1 row) test=#

首先看看item

(gdb) p	*lpp
$1 = {lp_off = 8104, lp_flags = 1, lp_len = 88}
(gdb)

总共88个字节

(gdb) p *tuple->t_data
$3 = {t_choice = {t_heap = {t_xmin = 4414, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, t_datum = {datum_len_ = 4414, datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid =
{bi_hi = 0, bi_lo = 0}, ip_posid = 1}, t_infomask2 = 10, t_infomask = 2305, t_hoff = 32 ' ', t_bits = 0x7f8c9af9ef5f "\333\002"}

首先看看头偏移量就改变了不是前面的24个字节。是因为字段超过了8 需要用2个bit来标记NULL,而PG又是8字节对齐所以是24+8=32

88-32=56 总共88字节减去头32  数据占56字节

7 * 8 = 56 上面总共有7个字段存储了值,每个占8字节就是56字节

(gdb) p	bp
$8 = (bits8 *) 0x7f8c9af9ef5f "\333\002"
(gdb) p sizeof(bp)
$9 = 8
(gdb)

数组的值

(gdb) p	bp[0]
$10 = 219 '\333'
(gdb) p bp[1]
$11 = 2 '\002'

bp[0] = 1 1 0 1 1 0 1 1  = 1 +2 +8 +16 +64 +128 = 219

bp[1] = 0 0 0 0 0 0 1 0  = 2

bp[3] = 0 0 0 0 0 0 0 0 = 0

......

bp[7] = 0 0 0 0 0 0 0 0  = 0

根据上面的宏att_isnull 就能很好的判断出那个字段是NULL。这样就非常的节省了数据存储空间。

Postgres是如何管理空值的的更多相关文章

  1. pgpool-II主备流复制的架设

    1.环境 OS: CentOS release 6.4 (Final) DB: postgresql 9.3.6 pgpool服务器: pgpool 172.16.0.240 数据库主服务器:mast ...

  2. [转帖] “王者对战”之 MySQL 8 vs PostgreSQL 10

    原贴地址:https://www.oschina.net/translate/showdown-mysql-8-vs-postgresql-10?lang=chs&page=2# 英文原版地址 ...

  3. Mysql 和 Postgresql(PGSQL) 对比

    Mysql 和 Postgresql(PGSQL) 对比 转载自:http://www.oschina.net/question/96003_13994 PostgreSQL与MySQL比较 MySQ ...

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

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

  5. pgpool-II 高可用搭建

    pgpool-II主备流复制的架设1.环境 OS: CentOS release 6.4 (Final)DB: postgresql 9.3.6pgpool服务器: pgpool 172.16.0.2 ...

  6. 如何快速搭建自己的ERP系统,4步源码快速安装odoo教程

    上一篇内容:了解什么是Odoo,为二次开发做准备 1.下载odoo源码 Github地址:https://github.com/odoo/odoo Gitee地址:https://gitee.com/ ...

  7. Centos7下安装BlockScout

    简介 BlockScout是一个Elixir应用程序,允许用户搜索以太坊网络(包括所有叉子和侧链)上的交易,查看账户和余额以及验证智能合约.BlockScout为用户提供了一个全面,易于使用的界面,以 ...

  8. 第20课-数据库开发及ado.net 可空值类型,资料管理器,多条件查询,Case

    第20课-数据库开发及ado.net 可空值类型,资料管理器,多条件查询,Case SqlHelper using System; using System.Collections.Generic; ...

  9. postgres高可用学习篇二:通过pgbouncer连接池工具来管理postgres连接

    安装pgbouncer yum install libevent -y yum install libevent-devel -y wget http://www.pgbouncer.org/down ...

随机推荐

  1. Vue源码后记-vFor列表渲染(1)

    钩子函数比较简单,没有什么意思,这一节搞点大事情 => 源码中v-for的渲染过程. vue的内置指令包含了v-html.v-if.v-once.v-bind.v-on.v-show等,先从一个 ...

  2. Strtus2框架笔记

    Struts2以WebWork优秀的设计思想为核心,吸收了 Struts框架的部分优点,提供了一个更加整洁的MVC设计模式实现的Web 应用程序框架. Struts2引入了几个新的框架特性:从逻辑中分 ...

  3. Linux系列教程(十二)——Linux软件包管理之yum在线管理

    上一篇博客我们介绍了rpm包管理之rpm命令管理,我们发现在使用rpm命令手动安装rpm包的时候,会发现安装遇到到的依赖让你痛不欲生,安装一个rpm时会要先先安装某个依赖的rpm,而安装这个依赖的rp ...

  4. Android 开发笔记___DatePicker__日期选择器

    虽然EditText提供了inputTtype="date",但用户往往不太喜欢自己输入时间. Android为这个提供了DatePicker,但有很多缺点,不是弹窗模式,而是直接 ...

  5. Robotframework自动化系统:筛选结果数量统计

    Robotframework自动化系统:筛选结果数量统计 上一个节点已经可以随机选中某一个下拉框的值,我们在使用evaluate随机数的时候需要计算下拉选项总数,这时候我们是手工计算输入的:这时候如果 ...

  6. whonix官网部分翻译

    Whonix:一个高安全的方式来Surfing the Internet Whonix是一个桌面操作系统,被设计用于高级安全和隐私.It realistically addresses attacks ...

  7. codefoces384A-Mafia心得

    题目描述:One day n friends gathered together to play "Mafia". During each round of the game so ...

  8. SEO内链操作的技巧大家又知道多少

    关于一个优质的网站而言,一套优质的内部优化链接体系是必不可少的,简略来说杰出的内链体系能让网站页面之前彼此传递权重,协助搜索引擎快速辨认网站内容的中心,可是SEO内链操作的技巧我们又知道多少?下面齐鲁 ...

  9. javascript正则多次调用test 结果交替出现

    现在需要一个正则验证小数点后保留一到三位数,小数点前只能两位或一位整数的这么一个数. 正则如: var reg = /^\d{,}\.\d{,}$/g; 验证如下: 因为我们用1.23符合规则的数据去 ...

  10. 关于svg

    动画:css3动画,canvas(js动画),svg(html动画). svg基本元素 version: 表示 <svg> 的版本,目前只有 1.0,1.1 两种 xmlns:http:/ ...