Truncate table命令是DBA常用的快速清空某张表以及其索引的命令,这条命令我在Oracle上已经用了二十多年了。PG的TRUNCATE 命令的实现和Oracle有较大的差异,这是因为PG的底层表的存储结构和Oracle大不相同。Oracle是采用共享的DATAFILE来存储表数据的,多张表共享共同的数据文件,在同一个数据文件里分配extents。而PG数据库的底层存储是以文件系统上的文件为单位的,不同的表之间不需要共享文件存储数据,因此在PG中不存在和Oracle类似的OBJECT REUSE问题,也不存在CHECKPOINT和FAST OBJECT REUSE方面等待。在Oracle数据库中,因为TRUNCATE后的表的存储空间存在重复使用问题,因此需要快速的完成对象级的CHECKPOINT,这些问题对于PG来说是不需要的。因此PG数据库的TRUNCATE操作比Oracle的效率更高。

这张表存储在默认表空间下面,relfilenode是2450530,我们可以在文件系统下好到这张表:



不过原来的对象2450530还存在,多了一个字节数为0的新对象2450533。这时候如果我们回滚这个操作会发生什么现象呢?

数据又回来了,而且relfilenode又恢复成truncate之前的了。和Oracle不同,PG的truncate操作是可以回滚的。从PG的官方文档上我们也可以看到,truncate操作是transaction-safe的,这一点从我们的实验上看是完全吻合的。


2450530这个文件还在,不过文件已经被清空了,这时候,如果想要回滚肯定是没办法了。那么,这个老文件什么时候会被删除掉呢?我们再来看下面的实验。


通过今天这个实验我们了解了PG数据库的TRUNCATE操作的内部实现方式,似乎其中只有TRUNCATE操作可以回滚这个对我们日常应用和维护PG数据库有点帮助。不过通过了这个实验了解了PG truncate与Oracle的差异之后,可以让我们在执行这个操作的时候,可以对其影响有一个更为深入的了解,也可以在今后安排此类操作的时候少一些顾虑。和Oracle相比,PG的truncate操作对当前业务系统的影响更小,我们可以放心的在生产业务比较繁忙的时候操作。这对于遇到表空间所在文件系统满了,需要清理不用的数据的时候,还是很有用的。
从PG TRUNCATE 实现的原理上,我们也可以看出,这种TRUNCATE虽然和oracle的DDL操作不同,可以rollback,但是TRUNCATE操作不是MVCC-SAFE的,如果我们在一个会话中执行TRUNCATE操作,那么访问这张表的会话是会受到影响的。我们可以做个实验,在一个PSQL会话中设置AUTOCOMMIT=’off’,然后执行TRUNCATE TABLE命令,在另外一个PSQL会话中执行select这张表的操作,此时,这个SELECT操作会被直接挂起,从pg_stat_activity中可以看到,这个会话在等待一个relation锁。

从PG的TRUNCATE命令的实现内部原理上看,TRUNCATE
不是MVCC-safe的,执行该命令后,其他并发会话会存在一定的MVCC方面的问题。如果在TRUNCATE命令执行之前,已经有一个并发会话在读取该表的数据,那么在该读取操作没有完成之前,一直会有一个ACCESS SHARE的表锁存在,TRUNCATE 操作会等待该锁释放。而如果一个在TRUNCATE命令执行前启动的查询操作,启动前的一瞬间还没放完这张表的数据,那么TRUNCATE命令很快就执行完成了,此时如果这条SQL要访问这张表的数据,则会访问到新的relation,这时候得到的结果是该表有0条记录。这样会导致业务逻辑上的数据不一致性。虽然这种情况发生的概率很低,不过发生的可能性是存在的。因此我们一定要注意这个问题。




