为什么PostgreSQL的TRUNCATE TABLE操作不具备MVCC安全性?
咱先从TRUNCATE的底层实现唠起,它跟DELETE完全不是一个路数!
你想啊,DELETE删数据的时候,是逐行给数据打“已删除”的标记(在MVCC机制里,就是让新事务看不到这些行的新版本),本质上还是在原有的表存储结构上做修改,所以能完美兼容MVCC的快照机制——老事务拿着之前的快照,该看到啥历史数据还是能看到啥。
但TRUNCATE就硬核多了,它根本不跟你逐行玩,直接把整个表的物理存储段(简单说就是存表数据的文件)给重置了,相当于直接给表换了个“空壳子”。这种操作是绕开了MVCC那套逐行版本管理逻辑的,直接在表的元数据层面动手脚。
这么做的好处是性能拉满——删个几百万行的大表,TRUNCATE一瞬间就能搞定,要是换成DELETE逐行删,那得等到猴年马月?但代价就是牺牲了MVCC兼容性:所有在TRUNCATE执行前创建的快照,再去读这个表的时候,根本找不到原来的存储数据了,自然就返回空表。
就像你调试时碰到的情况,并行事务如果在TRUNCATE跑起来之前就拿到了快照,那之后再查这个表,看到的就是空的,这就是因为快照对应的旧存储已经被TRUNCATE清得一干二净了。
官方文档里也明确标注了这个特性:
TRUNCATEis not MVCC-safe. After truncation, the table will appear empty to concurrent transactions, if they are using a snapshot taken before the truncation occurred.
备注:内容来源于stack exchange,提问作者WArnold

