PostgreSQL DELETE 会真实删除数据吗
为测试逻辑简单,测试表无约束索引等其他附加项。
test=# \d t1
Table "public.t1"
Column | Type | Collation | Nullable | Default
--------+-----------------------------+-----------+----------+---------
id | integer | | |
time | timestamp without time zone | | |
test=# delete from t1 where id=1;
找到对应的数据行
此过程类似select 逻辑,在此不详细展开
删除数据行
删除数据行的主要操作位于函数 heap_delete 之中。从其步骤可以看出,这里并没有如想象中那样的清除操作。(不妨脑补一下用橡皮擦进行作业的画面,哈哈。)
事务提交 CommitTransaction(void)
事务提交的主要操作如下:
- 写入日志确保持久性 事务提交时首先会写入 xlog 日志和 clog 日志,做到了在写入数据前先写入日志
- 通知其他事务与更新状态 在这个步骤中,会重置 ProcGlobal 里对应事务的 ID。此时,snapshot 能够立刻得知此事务不再执行中。同时,还会修改 TransamVariables 的最后事务号。由于 snapshot 依赖这个变量,所以在这一步骤中会获取 ProcArrayLock 的独占锁
看不到删除数据的原理
个人理解难免存在差错偏差,最准确完善的信息应参考源代码
在 PostgreSQL 中,进行数据查询时,会首先申请一个 snapshot(快照),它就如同一个神奇的滤镜,能将可被看到的数据反馈给用户,同时把不可见的数据巧妙地隐藏起来。
在隔离级别为 Read Committed 时,数据被删除且事务提交后,对其他进程不可见。其实现原理主要依靠 snapshot。 当一个进程获取到数据行后,会通过 HeapTupleSatisfiesMVCC (htup, snapshot, buffer) 函数来判断当前数据对自身是否可见。 在 heap_delete 函数中,数据行的 informask 标记位被去除了 HEAP_XMAX_BITS(其中包含 HEAP_XMAX_INVALID 标记),仅剩下 HEAP_XMIN_COMMITTED 标记。 而已提交的数据行如果只有 HEAP_XMIN_COMMITTED 标记,在 snapshot 中会被判断为对进程不可见,所以删除后的数据对其他进程不可见。
综上所述,普通的删除操作仅仅是修改了数据行的头部标记信息,并未实际操作数据行的内容。数据的可见与不可见是通过 snapshot 来实现的。这样的设计既保证了数据的一致性和隔离性,又提高了数据库的性能和并发处理能力。通过巧妙地运用 snapshot 和标记位的管理,PostgreSQL 能够在不同的事务隔离级别下,为用户提供准确、可靠的数据视图。
什么时候数据会被真正删除
VACUUM reclaims storage occupied by dead tuples. In normal PostgreSQL operation, tuples that are deleted or obsoleted by an update are not physically removed from their table; they remain present until a VACUUM is done. Therefore it’s necessary to do VACUUM periodically, especially on frequently-updated tables.
参照官方的解释,数据会一直保存到 vacuum 执行。因此,有必要定期进行 VACUUM 操作,特别是在那些频繁更新的表上。这样可以确保数据库不会因为大量的死亡元组而浪费存储空间,同时也有助于提高数据库的性能和稳定性