Skip to content

Latest commit

 

History

History
163 lines (110 loc) · 6.75 KB

20240510_03.md

File metadata and controls

163 lines (110 loc) · 6.75 KB

DB吐槽大会,第93期 - PG unlogged table转logged会写大量REDO/WAL

作者

digoal

日期

2024-05-10

标签

PostgreSQL , PolarDB , DuckDB , 吐槽 , unlogged table , logged table , redo , wal


背景

视频回放

1、产品的问题点

PG unlogged table转为logged表会写大量REDO/WAL.

alter table ...   
SET { LOGGED | UNLOGGED }   

写wal明显会影响性能, 所以PG的新版本在不断优化这块.

《PostgreSQL 17 preview - WAL锁竞争优化 - reading WAL buffer contents without a lock, Additional write barrier in AdvanceXLInsertBuffer()》

《PostgreSQL 17 preview - 优化wal insert lock, 提升高并发写入吞吐性能》

2、问题点背后涉及的技术原理

unlogged表, 顾名思义, 这个表的写操作不会产生wal日志. 当数据库崩溃恢复时, 会自动清理unlogged table的数据. 有点类似全局临时表, 但是每个会话都能看到里面的数据.

logged table, 就是常规表, 对这个表的写操作都会产生wal日志, 数据库崩溃恢复后, 数据仍在.

PG支持这两种表之间的转换, 从logged table转换到unlogged 表, 不会产生wal日志. 反之, 有点类似对空表灌入数据, 会产生大量的wal日志.

为什么从logged table转换到unlogged 表, 要产生wal日志呢? 其实是要解决备份可恢复的问题, 例如在这个操作之前有一个全量备份, 然后有所有的wal归档日志, 如果从logged table转换到unlogged 表不写wal, 那么未来拿着全量+归档就无法恢复出这个转换后的表的数据, 因为这个表的block对应的wal:full page不存在.

3、这个问题将影响哪些行业以及业务场景

实际要保证备份可用, 在转换为logged表后, 只需要对数据文件持久化一次, 然后做一次全量备份. 假设数据库的备份是存储快照, 全量备份很快, 所以写wal显得多此一举.

4、会导致什么问题?

本来想通过unlogged table加速导入, 然后转换为logged table保证未来这个表的可靠性. 但是PG unlogged table转logged会写大量REDO/WAL 这就违背了用户的初衷.

5、业务上应该如何避免这个坑

直接修改元数据pg_class.relpersistence , 避免unlogged table转logged写大量REDO/WAL

postgres=# create table a (id int primary key, info text);  
CREATE TABLE  
postgres=# create unlogged table b (id int primary key, info text);  
CREATE TABLE  
  
postgres=# \d a  
                 Table "public.a" // 显示是logged table  
 Column |  Type   | Collation | Nullable | Default   
--------+---------+-----------+----------+---------  
 id     | integer |           | not null |   
 info   | text    |           |          |   
Indexes:  
    "a_pkey" PRIMARY KEY, btree (id)  
  
postgres=# \d b  
             Unlogged table "public.b" // 显示是unlogged table  
 Column |  Type   | Collation | Nullable | Default   
--------+---------+-----------+----------+---------  
 id     | integer |           | not null |   
 info   | text    |           |          |   
Indexes:  
    "b_pkey" PRIMARY KEY, btree (id)  
postgres=# select relname,reltoastrelid::regclass,relpersistence from pg_class where relname in ('a','b','a_pkey','b_pkey');  
 relname |      reltoastrelid      | relpersistence   
---------+-------------------------+----------------  
 b       | pg_toast.pg_toast_17853 | u  
 b_pkey  | -                       | u  
 a       | pg_toast.pg_toast_17846 | p  
 a_pkey  | -                       | p  
(4 rows)  
  
postgres=# select relname,reltoastrelid::regclass,relpersistence from pg_class where relname in ('a','b','a_pkey','b_pkey','pg_toast_17853','pg_toast_17846');  
    relname     |      reltoastrelid      | relpersistence   
----------------+-------------------------+----------------  
 b              | pg_toast.pg_toast_17853 | u  
 pg_toast_17853 | -                       | u  
 b_pkey         | -                       | u  
 a              | pg_toast.pg_toast_17846 | p  
 pg_toast_17846 | -                       | p  
 a_pkey         | -                       | p  
(6 rows)  

将table, 相关的index, toast, index toast都要进行修改.

postgres=# update pg_class set relpersistence='p' where relname in ('b','b_pkey','pg_toast_17853');  
UPDATE 3  
postgres=# select relname,reltoastrelid::regclass,relpersistence from pg_class where relname in ('b','b_pkey','pg_toast_17853');  
    relname     |      reltoastrelid      | relpersistence   
----------------+-------------------------+----------------  
 b              | pg_toast.pg_toast_17853 | p  
 pg_toast_17853 | -                       | p  
 b_pkey         | -                       | p  
(3 rows)  
  
postgres=# \d b  
                 Table "public.b"  // 这里已经显示是logged table了  
 Column |  Type   | Collation | Nullable | Default   
--------+---------+-----------+----------+---------  
 id     | integer |           | not null |   
 info   | text    |           |          |   
Indexes:  
    "b_pkey" PRIMARY KEY, btree (id)  

6、业务上避免这个坑牺牲了什么, 会引入什么新的问题

直接修改元数据其实是比较危险的动作, 风险不知.

7、数据库未来产品迭代如何修复这个坑

PG 如果能支持一个选项, 数据库在做这个转换的时候, 让用户来根据情况选择要不要写wal日志就好了.

例如:

alter table ...   
SET { LOGGED | UNLOGGED }   
  
  
with nowal | wal  

digoal's wechat