PostgreSQL 11 preview - 添加非空默认值不需要 rewrite table

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介:

标签

PostgreSQL , add column default , rewrite table


背景

PostgreSQL ,我们在给表加列时,如果不设置列的默认值,不需要rewrite table,只是对元数据进行修改。

但是如果需要设置新增列的默认值,那么必须rewrite table。

PostgreSQL 11,新特性,在元数据中新增了2列(attmissingval, atthasmissing),当新增stable或immutable表达式(非volatile)作为默认值时,在ALTER TABLE时,即刻评估表达式的值作为常量,存入新增列在pg_attribute元数据中的attmissingval字段中,同时将atthasmissing标记为true.

Allow ALTER TABLE to add a column with a non-null default without a table rewrite (Andrew Dunstan, Serge Rielau)

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=16828d5c0273b4fe5f10f42588005f16b415b2d8

Fast ALTER TABLE ADD COLUMN with a non-NULL default  
  
Currently adding a column to a table with a non-NULL default results in  
a rewrite of the table. For large tables this can be both expensive and  
disruptive. This patch removes the need for the rewrite as long as the  
default value is not volatile. The default expression is evaluated at  
the time of the ALTER TABLE and the result stored in a new column  
(attmissingval) in pg_attribute, and a new column (atthasmissing) is set  
to true. Any existing row when fetched will be supplied with the  
attmissingval. New rows will have the supplied value or the default and  
so will never need the attmissingval.  
  
Any time the table is rewritten all the atthasmissing and attmissingval  
settings for the attributes are cleared, as they are no longer needed.  
  
The most visible code change from this is in heap_attisnull, which  
acquires a third TupleDesc argument, allowing it to detect a missing  
value if there is one. In many cases where it is known that there will  
not be any (e.g.  catalog relations) NULL can be passed for this  
argument.  
  
Andrew Dunstan, heavily modified from an original patch from Serge  
Rielau.  
Reviewed by Tom Lane, Andres Freund, Tomas Vondra and David Rowley.  
  
Discussion: https://postgr.es/m/31e2e921-7002-4c27-59f5-51f08404c858@2ndQuadrant.com  

原理

新增stable或immutable表达式(非volatile)作为默认值时,在ALTER TABLE时,即刻评估表达式的值作为常量,存入新增列在pg_attribute元数据中的attmissingval字段中,同时将atthasmissing标记为true. 结合tuple head的t_infomask掩码以及null bitmaps,可以实现输出时是否使用元数据中存储的默认值。

未来如果对新增字段进行更新、或者写入新记录时带上非默认值,则新值会直接写入TUPLE中。

heap tuple : t_infomask 掩码

src/include/access/htup_details.h

/*  
 * information stored in t_infomask:  
 */  
#define HEAP_HASNULL                    0x0001  /* has null attribute(s) */  
#define HEAP_HASVARWIDTH                0x0002  /* has variable-width attribute(s) */  
#define HEAP_HASEXTERNAL                0x0004  /* has external stored attribute(s) */  
#define HEAP_HASOID                             0x0008  /* has an object-id field */  
#define HEAP_XMAX_KEYSHR_LOCK   0x0010  /* xmax is a key-shared locker */  
#define HEAP_COMBOCID                   0x0020  /* t_cid is a combo cid */  
#define HEAP_XMAX_EXCL_LOCK             0x0040  /* xmax is exclusive locker */  
#define HEAP_XMAX_LOCK_ONLY             0x0080  /* xmax, if valid, is only a locker */  
  
  
.........  
  
  
/*  
 * BITMAPLEN(NATTS) -  
 *              Computes size of null bitmap given number of data columns.  
 */  
#define BITMAPLEN(NATTS)        (((int)(NATTS) + 7) / 8)  
   1 /*-------------------------------------------------------------------------  
   2  *  
   3  * tupdesc_details.h  
   4  *    POSTGRES tuple descriptor definitions we can't include everywhere  
   5  *  
   6  *  
   7  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group  
   8  * Portions Copyright (c) 1994, Regents of the University of California  
   9  *  
  10  * src/include/access/tupdesc_details.h  
  11  *  
  12  *-------------------------------------------------------------------------  
  13  */  
  14   
  15 #ifndef TUPDESC_DETAILS_H  
  16 #define TUPDESC_DETAILS_H  
  17   
  18 /*  
  19  * Structure used to represent value to be used when the attribute is not  
  20  * present at all in a tuple, i.e. when the column was created after the tuple  
  21  */  
  22   
  23 typedef struct attrMissing  
  24 {  
  25     bool        ammissingPresent;   /* true if non-NULL missing value exists */  
  26     Datum       ammissing;      /* value when attribute is missing */  
  27 } AttrMissing;  
  28   
  29 #endif                          /* TUPDESC_DETAILS_H */  

PostgreSQL 11测试

1、创建测试表,新增若干数据(5.6GB)

postgres=# create unlogged table aaa(id int, info text, crt_time timestamp);  
CREATE TABLE  
Time: 6.259 ms  
postgres=# insert into aaa select generate_series(1,1000000),repeat(md5(random()::text),10), now();  
INSERT 0 1000000  
Time: 2151.531 ms (00:02.152)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 1000000  
Time: 1235.480 ms (00:01.235)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 2000000  
Time: 2688.409 ms (00:02.688)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 4000000  
Time: 4782.437 ms (00:04.782)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 8000000  
Time: 11367.010 ms (00:11.367)  
  
  
postgres=# \dt+ aaa  
                    List of relations  
 Schema | Name | Type  |  Owner   |  Size   | Description   
--------+------+-------+----------+---------+-------------  
 public | aaa  | table | postgres | 5618 MB |   
(1 row)  

2、新增字段,并添加默认值,由于只需要修改元数据,瞬间完成。

postgres=# alter table aaa add column c1 text default 'digoal';  
ALTER TABLE  
Time: 3.013 ms  

3、查看元数据表,可以看到,atthasmissing=true, attmissingval=我们设置的默认值。

postgres=# \x  
Expanded display is on.  
postgres=# select * from pg_attribute where attrelid='aaa'::regclass and attname='c1';  
-[ RECORD 1 ]-+---------  
attrelid      | 99498  
attname       | c1  
atttypid      | 25  
attstattarget | -1  
attlen        | -1  
attnum        | 4  
attndims      | 0  
attcacheoff   | -1  
atttypmod     | -1  
attbyval      | f  
attstorage    | x  
attalign      | i  
attnotnull    | f  
atthasdef     | t  
atthasmissing | t  
attidentity   |   
attisdropped  | f  
attislocal    | t  
attinhcount   | 0  
attcollation  | 100  
attacl        |   
attoptions    |   
attfdwoptions |   
attmissingval | {digoal}  
  
Time: 0.470 ms  

4、查看数据,默认值会从元数据表中获取。

postgres=# select id,c1 from aaa limit 10;  
 id |   c1     
----+--------  
  1 | digoal  
  2 | digoal  
  3 | digoal  
  4 | digoal  
  5 | digoal  
  6 | digoal  
  7 | digoal  
  8 | digoal  
  9 | digoal  
 10 | digoal  
(10 rows)  
  
Time: 0.510 ms  

5、写入新值,更新老值,符合最终要求

postgres=# insert into aaa values (0,'test',now(),null);  
INSERT 0 1  
Time: 0.487 ms  
postgres=# insert into aaa values (-1,'test',now(),'new');  
INSERT 0 1  
Time: 0.415 ms  
postgres=# update aaa set c1='hello digoal' where id=1;  
UPDATE 16  
Time: 2010.873 ms (00:02.011)  
  
postgres=# select id,c1 from aaa where id in (0,-1,1);  
 id |      c1        
----+--------------  
  0 |   
 -1 | new  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
  1 | hello digoal  
(18 rows)  
  
Time: 1033.896 ms (00:01.034)  

PostgreSQL 低版本测试

postgres=# create unlogged table aaa(id int, info text, crt_time timestamp);  
CREATE TABLE  
Time: 2.930 ms  
postgres=# insert into aaa select generate_series(1,1000000),repeat(md5(random()::text),10), now();  
INSERT 0 1000000  
Time: 2992.188 ms (00:02.992)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 1000000  
Time: 1521.732 ms (00:01.522)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 2000000  
Time: 2781.948 ms (00:02.782)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 4000000  
Time: 5565.887 ms (00:05.566)  
postgres=# insert into aaa select * from aaa;  
INSERT 0 8000000  
Time: 10903.184 ms (00:10.903)  
postgres=# \dt+ aaa  
                    List of relations  
 Schema | Name | Type  |  Owner   |  Size   | Description   
--------+------+-------+----------+---------+-------------  
 public | aaa  | table | postgres | 5683 MB |   
(1 row)  
  
postgres=# alter table aaa add column c1 text default 'digoal';  
ALTER TABLE  
Time: 22484.222 ms (00:22.484)  

小结

PostgreSQL 11 终于可以愉快的加字段加默认值了。

但是依旧需要注意DDL lock, 所以建议还是要设置LOCK_TIME再执行DDL。

postgres=# set lock_timeout ='1s';  
SET  
  
alter table ....;  

参考

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=16828d5c0273b4fe5f10f42588005f16b415b2d8

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
4月前
|
SQL 关系型数据库 MySQL
mysql使用default给列设置默认值的问题
mysql使用default给列设置默认值的问题
63 0
|
2月前
|
SQL 存储 索引
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
|
4月前
|
关系型数据库 MySQL 数据处理
实时计算 Flink版产品使用合集之如果在 MySQL 表中为某个字段设置了默认值,并且在插入数据时指定了该字段为 NULL,那么 MySQL 是否会使用默认值来填充这个字段
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
10月前
|
关系型数据库 MySQL 数据库
Flink CDC中mysql 字段设置了默认值 ,然后插入数据时 指定该字段为null 会返回字段默认值 而不是null?
Flink CDC中mysql 字段设置了默认值 ,然后插入数据时 指定该字段为null 会返回字段默认值 而不是null?
204 2
|
SQL 关系型数据库 MySQL
MySQL默认值(DEFAULT)
MySQL默认值(DEFAULT)
164 0
|
关系型数据库 MySQL
mysql怎么改里面一列的默认值
mysql怎么改里面一列的默认值
|
关系型数据库 MySQL
MySQL字段默认值设置详解
在 MySQL 中,我们可以为表字段设置默认值,在表中插入一条新记录时,如果没有为某个字段赋值,系统就会自动为这个字段插入默认值。关于默认值,有些知识还是需要了解的,本篇文章我们一起来学习下字段默认值相关知识。
1098 0
|
SQL 关系型数据库 MySQL
史上最简单的 MySQL 教程(九)「列属性 之 空属性、列描述和默认值」
史上最简单的 MySQL 教程(九)「列属性 之 空属性、列描述和默认值」
72 0
|
存储 SQL 数据可视化
约束,MySQL约束,非空默认值,主键外键唯一自增,完整详细可收藏
约束,MySQL约束,非空默认值,主键外键唯一自增,完整详细可收藏
521 2
约束,MySQL约束,非空默认值,主键外键唯一自增,完整详细可收藏
|
SQL 弹性计算 算法
PostgreSQL 普通表在线转换为分区表 - online exchange to partition table
标签 PostgreSQL , 分区表 , 在线转换 背景 非分区表,如何在线(不影响业务)转换为分区表? 方法1,pg_pathman分区插件 《PostgreSQL 9.5+ 高效分区表实现 - pg_pathman》 使用非堵塞式的迁移接口 partition_table_concurrently( relation REGCLASS,
2673 0

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 下一篇
    DDNS