代码改变世界

PostgreSQL记录中的null值是如何存储的

2020-10-27 09:10  abce  阅读(1521)  评论(0编辑  收藏  举报

当向表中插入数据时,可以指定列的值为null。例如,有一张表t(i int,j int,k int),我们可以插入值(8,1,6),或将包含null值的(3,null,7)插入到表中。本文将探讨PostgrSQL中是如何存储null的一些技术细节。

PostgreSQL如何存储null值的?

PostgreSQL中的一个元组被分成元组头部分和数据部分。上面提到了向表t插入两条数据的例子。这里,一幅图片被用来抛出这个问题:在元组中null值是如何展示的?

 

如图中所示,一个int类型的数据占据4个字节的空间,但是遇到null值时,数据库该如何表示在3和7之间有个null值呢?

PostgreSQL存储null值的方法

使用pageinspact工具来观察null是如何存储的。执行下面的测试:

postgres=# create table t(i int, j int, k int);
CREATE TABLE
postgres=# insert into t values(8,1,6);
INSERT 0 1
postgres=# insert into t values(3,NULL,7);
INSERT 0 1
postgres=# insert into t values(4,9,2);
INSERT 0 1
postgres=# select lp,t_infomask,t_bits,t_data  from  heap_page_items(get_raw_page('t', 0));
 lp | t_infomask |  t_bits  |           t_data           
----+------------+----------+----------------------------
  1 |       2048 |          | \x080000000100000006000000
  2 |       2049 | 10100000 | \x0300000007000000
  3 |       2048 |          | \x040000000900000002000000
(3 rows)

postgres=#

可以看到,null值没有在元组的数据部分标记出来。

同时,可以看到带有null值的第二条记录的't_infomask'和’t_bits‘的值与第一条和第三条是不一致的。因此,这可能就是如何读取null值的秘密所在。

't_bits'设计

‘t_bits’是一个uint8的数组,当元组中没有null值的时候,t_bits可以被认为是空的,当元组有null值的列时,t_bits使用一个位(bit)来表示一个列是否为null。

postgres=# create table t0(i1 int,i2 int,i3 int,i4 int,i5 int,i6 int,
postgres(# i7 int,i8 int,i9 int,i10 int,i11 int,i12 int
postgres(# );
CREATE TABLE
postgres=# insert into t0 values(1,2,3,4,5,6,7,8,9,10,NULL,12);
INSERT 0 1
postgres=# insert into t0 values(1,2,3,4,5,6,7,8,9,NULL,NULL,12);
INSERT 0 1
postgres=# insert into t0 values(1,2,3,4,5,6,7,8,NULL,NULL,NULL,12);
INSERT 0 1
postgres=# insert into t0 values(1,2,3,4,5,6,7,8,9,10,11,12);
INSERT 0 1
postgres=#
postgres=# select lp,t_infomask,t_bits  from  heap_page_items(get_raw_page('t0', 0));
 lp | t_infomask |      t_bits      
----+------------+------------------
  1 |       2049 | 1111111111010000
  2 |       2049 | 1111111110010000
  3 |       2049 | 1111111100010000
  4 |       2048 | 
(4 rows)

postgres=#

上面的脚本会创建一个表t0,共有12个列,然后插入几条元组,执行pageinspect工具查询研究元组。注意看元组的t_bits值:

可以得出结论:

·在t_bits中,尚未使用的标记为是0

·1表示对应的列是非空的(not null),否则就是null

表中被删除的列

在PostgreSQL中,表的列被删除后,这个列的数据结构会被保留在目录中,但是对用户是不可见的。完成下面的测试:

postgres=# alter table t0 drop column i1;
ALTER TABLE
postgres=# insert into t0 values(2,3,4,5,6,7,8,9,10,11,12);
INSERT 0 1
postgres=# select lp,t_infomask,t_bits  from  heap_page_items(get_raw_page('t0', 0));
 lp | t_infomask |      t_bits      
----+------------+------------------
  1 |       2049 | 1111111111010000
  2 |       2049 | 1111111110010000
  3 |       2049 | 1111111100010000
  4 |       2048 | 
  5 |       2049 | 0111111111110000
(5 rows)

postgres=#

在这个测试中,我们删除了表t0的列'i1'并插入一个不含有null值的记录。使用pageinspact工具,我们可以发现插入的不含有null值的数据(lp=5)。被删除的列的t_bits值显示其也被认为是null值。

结论

当记录没有任何空值时,t_bits不会存储在记录中。一旦存在空值,t_bits将存储在元组中,以记录所有列的空值状态。表的删除列被视为空列。因此,当表中有许多列时,请勿删除列,否则将为每条记录生成额外的t_bit,这将导致存储膨胀。

 

 

 

https://www.highgo.ca/2020/10/20/the-way-to-store-null-value-in-pg-record/?utm_source=rss&utm_medium=rss&utm_campaign=the-way-to-store-null-value-in-pg-record