模拟触发freeze,分析冻结txids
freeze分析准备
查看freeze设置
select version(); version --------------------------------------------------------------------------------------------------------- PostgreSQL 15.6 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bit (1 row)
复制
SELECT name, setting, unit FROM pg_settings WHERE name LIKE 'vacuum_freeze%'; name | setting | unit -------------------------+---------+------ vacuum_freeze_min_age | 50000 | vacuum_freeze_table_age | 150000 | (2 rows)
复制
SELECT name, setting, unit FROM pg_settings WHERE name LIKE 'vacuum_freeze%'; name | setting | unit -------------------------------------+---------+------ autovacuum_freeze_max_age | 200000 | autovacuum_multixact_freeze_max_age | 250000 | vacuum_freeze_min_age | 50000 | vacuum_freeze_table_age | 150000 | vacuum_multixact_freeze_min_age | 5000000 | vacuum_multixact_freeze_table_age | 250000 |
复制
模仿事务消耗存储过程
对表test_table插入:insert_trx
对表test_table修改:update_trx
对表test_table删除:delete_trx
CREATE OR REPLACE PROCEDURE insert_trx (trx_num integer) LANGUAGE plpgsql AS $$ DECLARE v_step INT := 0; begin while v_step < trx_num loop INSERT INTO test_table (name) SELECT 'test' || v_step; commit; v_step:=v_step+1; end loop; end; $$ ; CREATE OR REPLACE PROCEDURE delete_trx (trx_num integer) LANGUAGE plpgsql AS $$ DECLARE v_step INT := 0; v_start INT := 0; begin select min(id) into v_start from test_table; while v_step < trx_num loop delete from test_table WHERE id = v_start+v_step; commit; v_step:=v_step+1; end loop; end; $$ ; CREATE OR REPLACE PROCEDURE update_trx (trx_num integer) LANGUAGE plpgsql AS $$ DECLARE v_step INT := 0; v_start INT := 0; begin select min(id) into v_start from test_table; while v_step < trx_num loop UPDATE test_table SET name = 'update '||name||v_step WHERE id = v_start+v_step; commit; v_step:=v_step+1; end loop; end; $$ ;
复制
页面分析准备
CREATE EXTENSION IF NOT EXISTS pageinspect; CREATE FUNCTION heap_page( relname text, pageno_from integer, pageno_to integer ) RETURNS TABLE( ctid tid, state text, xmin text, xmin_age integer, xmax text ) AS $$ SELECT (pageno,lp)::text::tid AS ctid, CASE lp_flags WHEN 0 THEN 'unused' WHEN 1 THEN 'normal' WHEN 2 THEN 'redirect to '||lp_off WHEN 3 THEN 'dead' END AS state, t_xmin || CASE WHEN (t_infomask & 256+512) = 256+512 THEN ' f' WHEN (t_infomask & 256) > 0 THEN ' c' WHEN (t_infomask & 512) > 0 THEN ' a' ELSE '' END AS xmin, age(t_xmin) AS xmin_age, t_xmax || CASE WHEN (t_infomask & 1024) > 0 THEN ' c' WHEN (t_infomask & 2048) > 0 THEN ' a' ELSE '' END AS xmax FROM generate_series(pageno_from, pageno_to) p(pageno), heap_page_items(get_raw_page(relname, pageno)) ORDER BY pageno, lp; $$ LANGUAGE sql;
复制
模拟触发freeze
实验1事务不及时提交
time | session1 | session 2 | session3 | session4 |
---|---|---|---|---|
T1 | begin; INSERT INTO test_table (name) values(‘first’ ) ; | tail -f postgresql-Thu.log | select datname, age(datfrozenxid) from pg_database; | |
T2 | call insert_trx(50000); | postgres | 50003 | ||
T3 | call insert_trx(50000); | postgres | 100004 | ||
T4 | call update_trx(50000); | postgres | 150006 | ||
T5 | call update_trx(50000); | postgres | 200006 | Close open transactions soon to avoid wraparound problems |
#session 1 begin; INSERT INTO test_table (name) values('first' ) ; #session 2 call insert_trx(50000); call update_trx(50000); call update_trx(50000); call update_trx(50000); # session 3 select datname, age(datfrozenxid) from pg_database; select t_xmin, t_xmax, t_infomask, t_infomask2, age(t_xmin) from heap_page_items(get_raw_page('test_table', 1)) where lp_flags=1 ; SELECT * FROM heap_page('test_table',0,1); select datname, age(datfrozenxid) from pg_database;
复制
触发freeze,日志warning输出如下
2024-04-18 16:05:51.106 CST [11888] HINT: Close open transactions soon to avoid wraparound problems. You might also need to commit or roll back old prepared transactions, or drop stale replication slots.
复制
SELECT * FROM heap_page(‘test_table’,0,1);
ctid | state | xmin | xmin_age | xmax ---------+--------+-------------+----------+------------- (0,1) | normal | 171295009 | 250006 | 0 a (0,2) | normal | 171295010 c | 250005 | 171395012 c (0,3) | normal | 171295011 c | 250004 | 171395013 c (0,4) | normal | 171295012 c | 250003 | 171395014 c (0,5) | normal | 171295013 c | 250002 | 171395015 c (0,6) | normal | 171295014 c | 250001 | 171395016 c (0,7) | normal | 171295015 c | 250000 | 171395017 c (0,8) | normal | 171295016 c | 249999 | 171395018 c (0,9) | normal | 171295017 c | 249998 | 171395019 c (0,10) | normal | 171295018 c | 249997 | 171395020 c
复制
select
t_xmin,
t_xmax,
t_infomask,
t_infomask2,
age(t_xmin)
from heap_page_items(get_raw_page(‘test_table’, 1)) ;
t_xmin | t_xmax | t_infomask | t_infomask2 | age -----------+-----------+------------+-------------+-------- 171945210 | 171995211 | 1282 | 2 | 249820 171945211 | 171995212 | 1282 | 2 | 249819 171945212 | 171995213 | 1282 | 2 | 249818 171945213 | 171995214 | 1282 | 2 | 249817 171945214 | 171995215 | 1282 | 2 | 249816 171945215 | 171995216 | 1282 | 2 | 249815 171945216 | 171995217 | 1282 | 2 | 249814 171945217 | 171995218 | 1282 | 2 | 249813 171945218 | 171995219 | 1282 | 2 | 249812 171945219 | 171995220 | 1282 | 2 | 249811 171945220 | 171995221 | 1282 | 2 | 249810 171945221 | 171995222 | 1282 | 2 | 249809 171945222 | 171995223 | 1282 | 2 | 249808 171945223 | 171995224 | 1282 | 2 | 249807
复制
实验2事务及时提交
time | session1 | session 2 | session3 | session4 |
---|---|---|---|---|
T1 | begin; INSERT INTO test_table (name) values(‘first’ ) ;commit; | tail -f postgresql-Thu.log | select datname, age(datfrozenxid) from pg_database; | |
T2 | call insert_trx(50000); | postgres | 50003 | ||
T3 | call insert_trx(50000); | postgres | 100004 | ||
T4 | call update_trx(50000); | postgres | 150004 | ||
T5 | call update_trx(50000); | postgres | 200005 | ||
T6 | postgres | 100002 |
实验3调整参数长事务
vacuum_failsafe_age =600000
select name, setting, short_desc from pg_settings where name in ('autovacuum', 'autovacuum_freeze_max_age', 'log_autovacuum_min_duration', 'vacuum_failsafe_age', 'vacuum_freeze_min_age', 'vacuum_freeze_table_age' ); name | setting | short_desc -----------------------------+-----------+-------------------------------------------------------------------------------- autovacuum | on | Starts the autovacuum subprocess. autovacuum_freeze_max_age | 200000 | Age at which to autovacuum a table to prevent transaction ID wraparound. log_autovacuum_min_duration | 0 | Sets the minimum execution time above which autovacuum actions will be logged. vacuum_failsafe_age | 600000 | Age at which VACUUM should trigger failsafe to avoid a wraparound outage. vacuum_freeze_min_age | 50000 | Minimum age at which VACUUM should freeze a table row. vacuum_freeze_table_age | 150000 | Age at which VACUUM should scan whole table to freeze tuples.
复制
time | session1 | session 2 | session3 | session4 |
---|---|---|---|---|
T1 | begin; INSERT INTO test_table (name) values(‘first’ ) ; | tail -f postgresql-Thu.log | select datname, age(datfrozenxid) from pg_database; | |
T2 | call insert_trx(50000); | postgres | 50003 | ||
T3 | call insert_trx(50000); | postgres | 100004 | ||
T4 | call update_trx(50000); | postgres | 150004 | automatic aggressive vacuum to prevent wraparound | |
T5 | call update_trx(50000); | postgres | 200005 | Close open transactions soon to avoid wraparound problems | |
T6 | call update_trx(50000); | postgres | 250004 | Close open transactions soon to avoid wraparound problems | |
T7 | call update_trx(50000); | postgres | 300006 | Close open transactions soon to avoid wraparound problems | |
T8 | call update_trx(50000); |
call update_trx(50000);
call update_trx(50000);
call update_trx(50000);
call update_trx(50000);
call update_trx(50000); | postgres | 600008 | oldest xmin is far in the past |
| T9 | | call update_trx(50000); | postgres | 650009 | oldest xmin is far in the past |
| T10 | commit; | | postgres | 50000 | |
T4时日志
2024-04-18 17:55:39.238 CST [15090] HINT: Close open transactions soon to avoid wraparound problems. You might also need to commit or roll back old prepared transactions, or drop stale replication slots. 2024-04-18 17:55:39.238 CST [15090] LOG: automatic aggressive vacuum to prevent wraparound of table "postgres.pg_toast.pg_toast_14109": index scans: 0 pages: 0 removed, 0 remain, 0 scanned (100.00% of total) tuples: 0 removed, 0 remain, 0 are dead but not yet removable removable cutoff: 172295034, which was 200004 XIDs old when operation ended new relfrozenxid: 172295034, which is 1 XIDs ahead of previous value index scan not needed: 0 pages from table (100.00% of total) had 0 dead item identifiers removed avg read rate: 195.312 MB/s, avg write rate: 0.000 MB/s buffer usage: 17 hits, 1 misses, 0 dirtied WAL usage: 1 records, 0 full page images, 188 bytes system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
复制
T8时日志
2024-04-18 18:13:39.761 CST [15608] WARNING: oldest xmin is far in the past 2024-04-18 18:13:39.761 CST [15608] HINT: Close open transactions soon to avoid wraparound problems. You might also need to commit or roll back old prepared transactions, or drop stale replication slots. 2024-04-18 18:13:39.761 CST [15608] WARNING: bypassing nonessential maintenance of table "postgres.pg_toast.pg_toast_14114" as a failsafe after 0 index scans 2024-04-18 18:13:39.761 CST [15608] DETAIL: The table's relfrozenxid or relminmxid is too far in the past. 2024-04-18 18:13:39.761 CST [15608] HINT: Consider increasing configuration parameter "maintenance_work_mem" or "autovacuum_work_mem". You might also need to consider other ways for VACUUM to keep up with the allocation of transaction IDs. 2024-04-18 18:13:39.761 CST [15608] LOG: automatic aggressive vacuum to prevent wraparound of table "postgres.pg_toast.pg_toast_14114": index scans: 0 pages: 0 removed, 0 remain, 0 scanned (100.00% of total) tuples: 0 removed, 0 remain, 0 are dead but not yet removable removable cutoff: 172295034, which was 650009 XIDs old when operation ended index scan bypassed by failsafe: 0 pages from table (100.00% of total) have 0 dead item identifiers avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s buffer usage: 16 hits, 0 misses, 0 dirtied WAL usage: 0 records, 0 full page images, 0 bytes system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
复制
结论
1.超过autovacuum_freeze_max_age会触发自动vacuum
2.长事务会制约自动触发vacuum清理,需要保存MVCC所需版本
3.数据库只会报警告“Close open transactions soon to avoid wraparound problems”,未能模拟出
4.调整vacuum_failsafe_age强制触发autovacuum也被长事务阻塞
PS: 不管哪种数据库,对大事务,长事务都不待见;及时提交事务