pg事务:子事务

news/2024/7/9 22:28:19 标签: postgresql, 子事务, savepoint

什么是子事务

一般事务只能整体提交或回滚,而子事务允许部分事务回滚。

SAVEPOINT p1 在事务里面打上保存点标记。不能直接提交子事务子事务也是通过事务的提交而提交。不过可以通过ROLLBACK TO SAVEPOINT p1回滚到该保存点。

子事务在大批量数据写入的时候很有用。如果事务中存在多个子事务,而其中一小段子事务失败,只需要重做这小部分数据就行,而不需要整个事务数据全部重做。

子事务在SQL语句中的使用

SAVEPOINT savepoint_name
ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name
RELEASE [ SAVEPOINT ] savepoint_name

注意:

  • savepoints语句必须在事务块中
  • savepoint执行保存点;rollback回滚到指定保存点;release擦除保存点,不会回滚子事务数据
  • cursor不会被savepoint事务影响

sql中的子事务示例:

lzldb=# begin;
BEGIN
lzldb=*#  insert into lzl1 values(0);
INSERT 0 1
lzldb=*# savepoint p1;
SAVEPOINT
lzldb=*#  insert into lzl1 values(1);
INSERT 0 1
lzldb=*# savepoint p2;
SAVEPOINT
lzldb=*# insert into lzl1 values(2);
INSERT 0 1
lzldb=*# savepoint p3;
SAVEPOINT
lzldb=*# insert into lzl1 values(3);
INSERT 0 1
lzldb=*# rollback to savepoint p2;
ROLLBACK
lzldb=*# commit;
COMMIT
lzldb=# select xmin,xmax,cmin,a from lzl1;
 xmin | xmax | cmin | a 
------+------+------+---
  731 |    0 |    0 | 0
  732 |    0 |    1 | 1
(2 rows)
--回滚到p2时,p3也被回滚

lzldb=# select * from vlzl1;
 t_ctid | lp | lp_flags  | t_xmin | t_xmax | t_cid |                      raw_flags                       | combined_flags 
--------+----+-----------+--------+--------+-------+------------------------------------------------------+----------------
 (0,1)  |  1 | LP_NORMAL |    731 |      0 |     0 | {HEAP_HASNULL,HEAP_XMIN_COMMITTED,HEAP_XMAX_INVALID} | {}
 (0,2)  |  2 | LP_NORMAL |    732 |      0 |     1 | {HEAP_HASNULL,HEAP_XMIN_COMMITTED,HEAP_XMAX_INVALID} | {}
 (0,3)  |  3 | LP_NORMAL |    733 |      0 |     2 | {HEAP_HASNULL,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID}   | {}
 (0,4)  |  4 | LP_NORMAL |    734 |      0 |     3 | {HEAP_HASNULL,HEAP_XMIN_INVALID,HEAP_XMAX_INVALID}   | {}
(4 rows)
--子事务infomask跟一般事务区别不大,同一个事务中多个命令通过cid和HEAP_XMIN_INVALID等就可以判断可见性
--子事务产生写入同样会消耗transaction id,而且cid在父事务框架下增加

其他场景中产生子事务

即使不用savepoint,也有其他方法产生子事务

  • EXCEPTION语句会触发子事务,这在一些工具或架构中常见,也很容易被忽略。每次EXCEPTION都会产生一个子事务

语法如:BEGIN / EXCEPTION WHEN .. / END

参考:https://fluca1978.github.io/2020/02/05/PLPGSQLExceptions.html

  • PL/Python代码引用plpy.subtransaction()

子事务SLRU缓存

子事务提交日志在pg_xact,父子对应关系在pg_subtrans存储子事务缓存subXID和父XID的映射。当PostgreSQL需要查找subXID时,它会计算这个ID驻留在哪个内存页中,然后在内存页中进行搜索。如果页面不在缓存中,它会驱逐一个页面,并将所需的页面从pg_subtrans加载到内存中。大量的子事务cache miss会消耗系统的IO和cpu。

子事务用的buffer只有32个并在源码中写死

源码src/include/access/subtrans.h

/* Number of SLRU buffers to use for subtrans */
\#define NUM_SUBTRANS_BUFFERS    32

buffer默认为8k,xid是32位占4个bytes,所以

SUBTRANS_BUFFER大小为32*8k=256k

SUBTRANS_BUFFER能存储最多32*8k/4=65536个xid

在这里插入图片描述

通过transactionid找到子事务的在page中的位置
源码src/backend/access/transam/subtrans.c

/* We need four bytes per xact */
#define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
//每个页面最多存储8k/4bytes=2048个子事务id

#define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
//通过子事务xid计算page号=xid/2048
#define TransactionIdToEntry(xid) ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE)
//通过子事务xid计算在page中的offset=xid%2048

子事务xid在page中不一定是紧凑的,一个page可能少于2048个子事务id

子事务的危害

  1. PGPROC_MAX_CACHED_SUBXIDS溢出

PGPROC_MAX_CACHED_SUBXIDS不是GUI参数,在源码中写死,只能通过改源码修改该参数。

源码src/include/storage/proc.h

	/* 
	*每个backend都有子事务cache上限PGPROC_MAX_CACHED_SUBXIDS。
	*我们必须跟踪cache是否溢出(比如,事务至少有一个缓存不了的子事务)
	*如果一个cache都没有溢出,我们可以确认没有在pgproc array中xid一定不是一个运行中的事务。
	*(没有在任何proc,又没有溢出,说明没有跑)
	*如果有溢出,我们必须查看pg_subtrans
	*/
	#define PGPROC_MAX_CACHED_SUBXIDS 64	/* XXX guessed-at value */
	
	struct XidCache
	{
		TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
	};

阅读这段源码,得到两个重要信息

copy大佬压测:子事务刚好超过64个的时候,性能下降。所以,每个会话的子事务最好不要超过64个

在这里插入图片描述

  1. 子事务导致multixact异常等待

原文:https://buttondown.email/nelhage/archive/notes-on-some-postgresql-implementation-details/

for update本身是行级排他锁,本身不应该产生multixact id,但在此场景中产生了多个MultiXact等待,导致数据库性能断崖

  • LWLock:MultiXactMemberControlLock
  • LWLock:MultiXactOffsetControlLock
  • LWLock:multixact_member
  • LwLock:multixact_offset

后来发现在Django框架中有子事务语句

SELECT [some row] FOR UPDATE;
 SAVEPOINT save;
 UPDATE [the same row];
  1. 从库性能急剧下降

原文:https://about.gitlab.com/blog/2021/09/29/why-we-spent-the-last-month-eliminating-postgresql-subtransactions/

一个长事务和一个savepoint子事务也可能造成查询库性能断崖

如果读取发生在主库的快照上,则生成的快照包含xmin,xmax,txip事务列表,subxip保存一个在进行的子事务列表。但是,无论是原数组还是快照都不会直接与从库共享,从库从WAL中读取所需的所有数据

在这里插入图片描述

当存在子事务时,一个长事务的运行会使从库性能断崖式下滑

在这里插入图片描述

  1. 生产性能急剧下降

当数据库运行繁忙,又存在较多子事务时,性能可能急剧下降,并伴随子事务的等待事件。这个场景即使每个会话子事务没有超过64,而且不是在从库而是在主库上时,也会发生。

我们发现工具(OGG)中默认是50个子事务,此时我们将工具中的子事务数据量降低到10-20个时,数据库性能得到缓解。

子事务的使用建议

子事务已经在国内外生产环境造成了非常多问题,有许多案例和问题分析。引用一下“Subtransactions are basically cursed. Rip em out.”

子事务参考

https://postgres.ai/blog/20210831-postgresql-subtransactions-considered-harmful

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

https://fluca1978.github.io/2020/02/05/PLPGSQLExceptions.html

https://about.gitlab.com/blog/2021/09/29/why-we-spent-the-last-month-eliminating-postgresql-subtransactions/

https://buttondown.email/nelhage/archive/notes-on-some-postgresql-implementation-details/


http://www.niftyadmin.cn/n/344376.html

相关文章

Tiny+ 语言词法之C语言

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 语义分析本质上就是在语法分析的基础上进一步完善分析的功能。举个例子来说,在语法分析部分的 if_stmt 函数中,在语义上判断条件必须返回布尔类型的值,因此我们加入一个判断,判断…

关于Java中的抽象类注意事项

文章目录 🏆文章导读🍂抽象类的定义🍂抽象类的特性🍂总结:面试题普通类和抽象类有哪些区别?抽象类能使用final继承吗? 🏆文章导读 在本篇文章中,对抽象类进行了一个详细的…

YOLO-NAS C++部署 2023.5.17

这不最近新出了网络,YOLO-NAS,听过性能和速度都不错,而且int8量化后效果也不错。 一、吐槽 但是我打开该项目阅读readme.txt时候,发现这些示例网站一个都打不开! 而且readme.txt很不详细,你想训练自己的模…

Redis介绍及Linux单机安装

1.Redis介绍 1.1.什么是Redis Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 Redis是完全开源的&#…

centos7.9一键部署ceph-v15.2.17集群(cephadm方式)

cephadm方式安装ceph集群 服务器规划 centos7.9 docker-v20.10.17 ceph1 ceph2 ceph3 ceph4 ceph5 磁盘:2T*36T(共30T) 内存、cpu 磁盘 操作系统 8核8g 2T*3 centos7.9 8核8g 2T*3 centos7.9 8核8g 2T*3 centos7.9 8核8g 2T*3 …

外围信息搜集-渗透测试模拟环境(1)

外围信息搜集属于情报搜集技术之一,本篇将介绍外围信息搜集的渗透模拟环境下整理的各类收集方法、各类工具技术使用的演示 。 往期系列文章: 渗透攻防环境搭建与攻防知识体系思维导图 渗透模拟环境配置和工具介绍-渗透测试模拟环境(0)_luozhonghua2000的博客-CSDN博客 外围…

【ChatGPT】IOS如何下载注册使用ChatGPT的APP(教学)

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、…

使用Let‘s Encrypt免费证书

要使用Let’s Encrypt免费证书,您需要完成以下步骤: 1、安装Certbot Certbot是Let’s Encrypt官方提供的自动化证书管理工具。您可以在Certbot官网上找到适用于不同操作系统的安装指南。例如,在Ubuntu操作系统上,您可以使用以下命…