上一篇中,我们知道postgresql表结构定义存储在系统表pg_attribute中,那么在做表结构变更的时候是否只需要变更系统表就可以了呢?
首先我们构造4000万数据的大表(过程略)来体验PostgreSQL添加字段的速度。
从这里可以看到添加字段只花了7毫秒,对于饱受Mysql做表结构变更困扰的老哥们来说简直不可思议(以前搞Mysql),Mysql 8之后支持通过修改元数据的方式添加索引。
相信到这里,你已经发现PostgreSQL表结构变更实际上是对系统表pg_attribute做插入记录操作,所以速度非常快。
那么,你可能会问,我的生产系统中为什么做表结构更新经常卡住呢?而且有时候耗时非常长。。
接下来我们看一些加字段耗时长的场景:
场景1-字段指定默认值:
示例SQL: alter table xx add column xx varchar(30) default 'xxxx';
这个例子中清楚感觉到加字段使用DEFAULT选项时,对同样数据量的表来说,耗时从7毫秒上升到12分钟,耗时相差1800多倍!
对比加字段前、后记录ctid字段可知,在ADD COLUMN ... DEFAULT ..场景下,除了修改系统表pg_attribute外,还对存储数据行进行内部迁移。
到这里,你可能会说那就通过ctid是否发生变化来判断加字段耗时长短就行,但是凡事总有例外,我们来看一个ctid不变,但是耗时仍然非常长的例子:
场景1-修改字段长度:
示例SQL: alter table xx modify column xx varchar(256) ; pad字符长度从varchar(60)扩大到varchar(256)。
根据刚刚的理论,我们来看看ctid:
下图可以看出变结果变更前后ctid并没有发生变化,那么到底原因是什么呢?
改字段长度前后数据ctid并没有变化,说明表上的数据没有发生变动。 而在改字段过程中硬盘使用率先增加,加完字段之后使用率恢复,说明有临时文件产生。
这里我的理解是PostgreSQL为了安全,在对原表操作前先COPY一份临时数据来预防修改过程中,数据出现异常或者PostgreSQL Crash等场景,保证数据能够回滚。