前言
在 PostgreSQL 中,支持多种类型的表 — 临时表、普通表,以及无日志表,顾名思义,”无日志”其优势在于不用记录 WAL,那么写入速度自然也杠杠的,同理,无日志表在备库上没有数据 (只有一个壳),也无法进行访问,会提示 ERROR: cannot access temporary or unlogged relations during recovery
,pg_basebackup 的时候也会跳过无日志表 (除了 init 分支)。那么无日志表又有哪些鲜为人知的细节呢?
分析
首先,让我们思考一下,无日志表的数据会落盘吗?或者说,按照常识,执行正常检查点的时候会落盘吗?
However, they are not crash-safe: an unlogged table is automatically truncated after a crash or unclean shutdown. The contents of an unlogged table are also not replicated to standby servers. Any indexes created on an unlogged table are automatically unlogged as well.
根据官网解释,在崩溃或者非正常关闭的时候,表文件会被截断,也就是说,在正常关闭的情况下,数据是会正常落盘的,让我们验证一下:
1 | postgres=# create unlogged table t1(id int,info text); |
hexdump 会将相同的都是 0 的行标记为 *,用 vim 打开也确实全是 0。
1 | 0000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................ |
让我们正常关闭,再观察一下
1 | postgres=# \q |
这次就可以很清楚地看到,数据都被刷盘了。那么这二者有什么区别?其实代码的注释就很清楚
1 | /* |
也就是说,如果 checkpoint 的时候,其 flags 不是 CHECKPOINT_IS_SHUTDOWN
、CHECKPOINT_END_OF_RECOVERY
或者 CHECKPOINT_FLUSH_ALL
,那么只会刷持久化的表。让我们验证一下:
1 | (gdb) b CreateCheckPoint |
108 等于:
- CHECKPOINT_REQUESTED (0x0040 = 64)
- CHECKPOINT_WAIT (0x0020 = 32)
- CHECKPOINT_FORCE (0x0008 = 8)
- CHECKPOINT_IMMEDIATE (0x0004 = 4)
说明不带有 CHECKPOINT_IS_SHUTDOWN
或者 CHECKPOINT_FLUSH_ALL
,对应到我们前面的现象,没有刷新 unlogged tables。再看看正常关机的情况 (注意此处需要用 SIGUSR2 的信号):
1 | (gdb) b CreateCheckPoint |
flags = 5
表示 CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE
,所以对应到我们前面的现象,正常关机会进行刷盘。
另外上面细心的读者可能发现了,执行正常的 checkpoint,其大小也变为了同样的大小,我没有看具体代码,猜测是类似于打开 wal_init_zero 参数,调用 pg_pwrite_zeros,直接调用 mdextend -> FileWrite
1 | /* |
见微知著
简单浏览了一下代码,CHECKPOINT_FLUSH_ALL
一共在两处进行了调用
CreateDatabaseUsingFileCopy:Create a new database using the FILE_COPY strategy. FILE_COPY 可以参照官网
movedb:对应到 ALTER DATABASE SET TABLESPACE
所以,对应到这些场景的时候,包括 unlogged tables,也需要进行刷盘,注意潜在的性能影响。
1 | /* |
表的转换
还有需要注意的点在于,普通表和无日志表之间的转换
1 | ALTER TABLE mytable SET UNLOGGED; -- cheap! |
也就是将无日志表转为普通表的时候,会需要写大量的 REDO,不难理解,不然崩溃了就无法恢复数据了。反之则很快,只需要修改一下元数据即可。因此,这种行为也可以进行发散,直接通过修改元数据的方式,避免将无日志表转为普通表的时候写入大量 WAL,但是有什么幺蛾子就不好说了,比如 PITR,都没有这个表的 WAL,那自然恢复出来的数据也不一致了。
块结构
还有一点值得注意的细节是,块上面的 LSN👇🏻
无日志表的 LSN 为 0,这意味着无日志表由其自己的数据文件中的数据表示,但不由日志文件中的数据表示。
小结
最后就用一张引用的图做总结吧!
参考
https://www.crunchydata.com/blog/postgresl-unlogged-tables
https://levelup.gitconnected.com/logged-unlogged-and-temporary-tables-in-postgresql-d390d9a4ef15
https://blog.japinli.top/2022/11/postgresql-debug-checkpointer/
https://github.com/digoal/blog/blob/master/202405/20240510_03.md
https://blog.csdn.net/baidu_41642080/article/details/132835382