Postgres是如何管理空值的
创建表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是如何管理空值的的更多相关文章
- pgpool-II主备流复制的架设
1.环境 OS: CentOS release 6.4 (Final) DB: postgresql 9.3.6 pgpool服务器: pgpool 172.16.0.240 数据库主服务器:mast ...
- [转帖] “王者对战”之 MySQL 8 vs PostgreSQL 10
原贴地址:https://www.oschina.net/translate/showdown-mysql-8-vs-postgresql-10?lang=chs&page=2# 英文原版地址 ...
- Mysql 和 Postgresql(PGSQL) 对比
Mysql 和 Postgresql(PGSQL) 对比 转载自:http://www.oschina.net/question/96003_13994 PostgreSQL与MySQL比较 MySQ ...
- “王者对战”之 MySQL 8 vs PostgreSQL 10
既然 MySQL 8 和 PostgreSQL 10 已经发布了,现在是时候回顾一下这两大开源关系型数据库是如何彼此竞争的. 在这些版本之前,人们普遍认为,Postgres 在功能集表现更出色,也因其 ...
- pgpool-II 高可用搭建
pgpool-II主备流复制的架设1.环境 OS: CentOS release 6.4 (Final)DB: postgresql 9.3.6pgpool服务器: pgpool 172.16.0.2 ...
- 如何快速搭建自己的ERP系统,4步源码快速安装odoo教程
上一篇内容:了解什么是Odoo,为二次开发做准备 1.下载odoo源码 Github地址:https://github.com/odoo/odoo Gitee地址:https://gitee.com/ ...
- Centos7下安装BlockScout
简介 BlockScout是一个Elixir应用程序,允许用户搜索以太坊网络(包括所有叉子和侧链)上的交易,查看账户和余额以及验证智能合约.BlockScout为用户提供了一个全面,易于使用的界面,以 ...
- 第20课-数据库开发及ado.net 可空值类型,资料管理器,多条件查询,Case
第20课-数据库开发及ado.net 可空值类型,资料管理器,多条件查询,Case SqlHelper using System; using System.Collections.Generic; ...
- postgres高可用学习篇二:通过pgbouncer连接池工具来管理postgres连接
安装pgbouncer yum install libevent -y yum install libevent-devel -y wget http://www.pgbouncer.org/down ...
随机推荐
- 记Javascript的编写方式的全新学习
前言 这次有幸参与前端的工作,对于前端开发学习了不少新知识,在此记录一下相比之前,完全不同的Javascript编写方式. 原来的编写方式 之前也是写过Javascript,就是常见的.js 文件写函 ...
- 快速学会使用Fiddler抓包 截包伪造提交包
1.Fiddler介绍 Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑,移动设备和互联网之间的http通讯,设置断点,查看所有的"进出"Fiddler的 ...
- vue 使用axios 跨域请求数据的问题
axios默认是没有jsonp 跨域请求的方法的.一般来说流行的做法是将跨域放在后台来解决,也就是后台开发人员添加跨域头信息. 例如java中的 header,response.setHeader(& ...
- 动态代理:JDK动态代理和CGLIB代理的区别
代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...
- 30.Linux-RTC驱动分析及使用
linux中的rtc驱动位于drivers/rtc下,里面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以它的RTC驱动为rtc-s3c.c 1.进入./drivers/rtc/r ...
- JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)
一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...
- 比特币区块结构Merkle树及简单支付验证分析
在比特币网络中,不是每个节点都有能力储存完整的区块链数据,受限于存储空间的的限制,很多节点是以SPV(Simplified Payment Verification简单支付验证)钱包接入比特币网络,通 ...
- bzoj1003
考虑dp[i]表示前i天的最小总成本. 枚举上一次在第j天之后对路线进行了修改,那么就由dp[j]转移至dp[i],转移的代价是把第[j+1,i]天所有被占用的点全删掉后的最短路(不连通当然就是INF ...
- nodejs a和b文件相互引用
//取自于node中文网 http://nodejs.cn/api/modules.html 当循环调用 require() 时,一个模块可能在未完成执行时被返回. 例如以下情况: a.js: con ...
- [转载] Java并发编程:Lock
转载自http://www.cnblogs.com/dolphin0520/p/3923167.html 以下是本文目录大纲: 一.synchronized的缺陷 二.java.util.concur ...