暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

模拟PG触发freeze

zhou 2024-04-18
163

模拟触发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: 不管哪种数据库,对大事务,长事务都不待见;及时提交事务

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论