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

PostgreSQL savepoint 的内存开销以及子事务溢出问题

原创 digoal 2022-01-20
702

作者

digoal

日期

2021-11-03

标签

PostgreSQL , savepoint , subtransactions


1、每个savepoint都会消耗一个xid

2、每个savepoint消耗8K的会话本地内存(CurTransactionContext)

3、当有并发事务时, 建议每个事务的子事务不要超过64个, 否则会引入SubtransControlLock等待事件, 可能影响性能.

create unlogged table a1 (id int, info text);  
\x  
do language plpgsql $$  
declare  
  v text := '';  
begin  
  v := 'begin;';  
  for i in 1..66 loop  
    v := v||format($_$insert into a1 values (1,'test'); savepoint a%s ;$_$, i);  
  end loop;  
  -- v := v|| 'end;';  
  raise notice '%', v;  
end;  
$$;  
begin;insert into a1 values (1,'test'); savepoint a1 ;insert into a1 values (1,'test'); savepoint a2 ;insert into a1 values (1,'test'); savepoint a3 ;insert into a1 values (1,'test'); savepoint a4 ;insert into a1 values (1,'test'); savepoint a5 ;insert into a1 values (1,'test'); savepoint a6 ;insert into a1 values (1,'test'); savepoint a7 ;insert into a1 values (1,'test'); savepoint a8 ;insert into a1 values (1,'test'); savepoint a9 ;insert into a1 values (1,'test'); savepoint a10 ;  
BEGIN  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  

每个savepoint 8K内存(CurTransactionContext), 同时在top 事务中消耗一部分内存.

select * from pg_get_backend_memory_contexts();  
                  name                   |                   ident                   |        parent         | level | total_bytes | total_nblocks | free_bytes | free_chunks | used_bytes   
-----------------------------------------+-------------------------------------------+-----------------------+-------+-------------+---------------+------------+-------------+------------  
 TopMemoryContext                        |                                           |                       |     0 |      351872 |            10 |      54352 |          14 |     297520  
 Record information cache                |                                           | TopMemoryContext      |     1 |        8192 |             1 |       1576 |           0 |       6616  
 pgstat TabStatusArray lookup hash table |                                           | TopMemoryContext      |     1 |        8192 |             1 |       1448 |           0 |       6744  
 TopTransactionContext                   |                                           | TopMemoryContext      |     1 |       16384 |             2 |       3024 |           6 |      13360  
 CurTransactionContext                   |                                           | TopTransactionContext |     2 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     3 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     4 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     5 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     6 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     7 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     8 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |     9 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    10 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    11 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    12 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    13 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    14 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    15 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    16 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    17 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    18 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    19 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    20 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    21 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    22 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    23 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    24 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    25 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    26 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    27 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    28 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    29 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    30 |        8192 |             1 |       7928 |           0 |        264  
 CurTransactionContext                   |                                           | CurTransactionContext |    31 |        8192 |             1 |       7928 |           0 |        264  

每个子事务也会消耗一个事务号

BEGIN  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
INSERT 0 1  
SAVEPOINT  
postgres=# select ctid,xmin,xmax from a1;  
  ctid  |  xmin   | xmax   
--------+---------+------  
 (0,1)  | 2273563 |    0  
 (0,2)  | 2273564 |    0  
 (0,3)  | 2273565 |    0  
 (0,4)  | 2273566 |    0  
 (0,5)  | 2273567 |    0  
 (0,6)  | 2273568 |    0  
 (0,7)  | 2273569 |    0  
 (0,8)  | 2273570 |    0  
 (0,9)  | 2273571 |    0  
 (0,10) | 2273572 |    0  
(10 rows)  
create unlogged table a1 (id int, info text);  
\x  
do language plpgsql $$  
declare  
  v text := '';  
begin  
  v := 'begin;';  
  for i in 1..500 loop  
    v := v||format($_$insert into a1 values (1,'test'); savepoint a%s ;$_$, i);  
  end loop;  
  v := v|| 'end;';  
  raise notice '%', v;  
end;  
$$;  
vi test.sql  
...  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 900 -j 900 -T 120  
postgres=# select wait_event_type,wait_event,count(*) from pg_stat_activity group by 1,2 order by 3 desc;  
 wait_event_type |     wait_event      | count   
-----------------+---------------------+-------  
 Client          | ClientRead          |    48  
                 |                     |    36  
 LWLock          | SubtransControlLock |     6  
 Activity        | LogicalLauncherMain |     1  
 Activity        | BgWriterHibernate   |     1  
 Activity        | WalWriterMain       |     1  
 Activity        | CheckpointerMain    |     1  
 Activity        | AutoVacuumMain      |     1  
 LWLock          | lock_manager        |     1  
(9 rows)  

参考:
https://www.cybertec-postgresql.com/en/subtransactions-and-performance-in-postgresql/

Implementation of subtransactions

To understand what is going on, we have to understand how transactions and subtransactions are implemented.

Whenever a transaction or subtransaction modifies data, it is assigned a transaction ID. PostgreSQL keeps track of these transaction IDs in the commit log, which is persisted in the pg_xact subdirectory of the data directory.

However, there are some differences between transactions and subtransactions:

  • Each subtransaction has a containing transaction or subtransaction (the “parent”)
  • Committing a subtransaction does not require a WAL flush
  • There can be only one transaction per database session, but many subtransactions

The information which (sub)transaction is the parent of a given subtransaction is persisted in the pg_subtrans subdirectory of the data directory. Since this information becomes obsolete as soon as the containing transaction has ended, this data do not have to be preserved across a shutdown or crash.

Subtransactions and visibility

The visibility of a row version (“tuple”) in PostgreSQL is determined by the xmin and xmax system columns, which contain the transaction ID of the creating and destroying transactions. If the transaction ID stored is that of a subtransaction, PostgreSQL also has to consult the state of the containing (sub)transaction to determine if the transaction ID is valid or not.

To determine which tuples a statement can see, PostgreSQL takes a snapshot of the database at the beginning of the statement (or the transaction). Such a snapshot consists of:

  • a maximum transaction ID: everything from that transaction on is invisible
  • the list of transactions and subtransactions that were active when the snapshot was taken
  • the command number of the earliest visible command in the current (sub)transaction

A snapshot is initialized by looking at the process array, which is stored in shared memory and contains information about all currently running backends. This, of course, contains the current transaction ID of the backend and has room for at most 64 non-aborted subtransactions per session. If there are more than 64 such subtransactions, the snapshot is marked as suboverflowed.

Explaining the test results

A suboverflowed snapshot does not contain all data required to determine visibility, so PostgreSQL will occasionally have to resort to pg_subtrans. These pages are cached in shared buffers, but you can see the overhead of looking them up in the high rank of SimpleLruReadPage_ReadOnly in the perf output. Other transactions have to update pg_subtrans to register subtransactions, and you can see in the perf output how they vie for lightweight locks with the readers.

Diagnosing the problem of too many subtransactions

Apart from looking at “perf top”, there are other symptoms that point at the direction of this problem:

  • Your workload performs well when run single-threaded, but badly when run in several concurrent database sessions
  • You often see the wait event “SubtransControlLock” in pg_stat_activity
  • If you export a snapshot using the “pg_export_snapshot()” function, the resulting file in the pg_snapshots subdirectory of the data directory will contain the line “sof:1” to indicate that the subtransaction array overflowed

Conclusion

Subtransactions are a great tool, but you should use them wisely. If you need concurrency, don’t start more than 64 subtransactions per transaction.

The diagnostics shown in this article should help you determine whether you are suffering from the problem, or not.

Finding the cause of that problem can be tricky. For example, it may not be obvious that a function with an exception handler that you call for each result row of an SQL statement (perhaps in a trigger) starts a new subtransaction.

《PostgreSQL 20200819当天代码 - 14 对比 13 高并发性能优化 数据对比 - get snapshot improve》

https://www.cybertec-postgresql.com/en/subtransactions-and-performance-in-postgresql/

https://github.com/digoal/blog/blob/master/202107/20210728_02.md

https://developer.aliyun.com/learning/course/813

https://developer.aliyun.com/learning/course/813/detail/13918

第四课:PolarDB forPG核心 feature介绍 https://developer.aliyun.com/topic/download?id=7902

期望 PostgreSQL 增加什么功能?

类似Oracle RAC架构的PostgreSQL已开源: 阿里云PolarDB for PostgreSQL云原生分布式开源数据库!

PostgreSQL 解决方案集合

德哥 / digoal's github - 公益是一辈子的事.

digoal's wechat

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

评论