PG中文社区 /

PostgreSQL子事务及性能分析

原作者:Laurenz Albe  创作时间:2020-03-30 16:56:45+08
wangliyun 发布于2020-03-31 08:00:00           评论: 7   浏览: 9230   顶: 686  踩: 639 

作者介绍

Laurenz Albe:Cybertec的高级顾问和支持工程师。自2006年以来,一直与PostgreSQL合作并为其做出贡献。

译者简介

陈雁飞:开源PostgreSQL爱好者,一直从事PostgreSQL数据库运维工作

最近,在排查PostgreSQL性能问题的时候,两次遇到子事务相关问题。所以,我想这个话题非常适合作为博客内容。

什么是子事务?

每个人都了解数据库事务。在PostgreSQL中,事务是默认工作在自动提交模式下,多语句情况下,需要显示调用BEGIN或者START TRANSACTION来开启一个事务,最后使用END或者COMMIT结束它。如果用ROLLBACK中断一个事务(或者数据库会话结束的时候没有执行提交操作),那么在事务中的操作将成为没有完成的。

现在子事务允许你回滚部分已经在事务中完成的工作。可以使用下面的标准语句在一个事务中开启子事务:

SAVEPOINT name;

“name”表示一个子事务的标识符(没有单引号!)。不能在SQL中提交一个子事务(将和包含它的事务一起自动提交),但是可以使用下面的命令回滚:

ROLLBACK TO SAVEPOINT name;

子事务的使用

子事务在长事务中有非常大的作用。在PostgreSQL中,事务中任何一个错误都会中断整个事务:

test=> BEGIN;
BEGIN
test=*> SELECT 'Some work is done';
     ?column?      
-------------------
 Some work is done
(1 row)

test=*> SELECT 12 / (factorial(0) - 1);
ERROR:  division by zero
test=!> SELECT 'try to do more work';
ERROR:  current transaction is aborted, commands ignored until end of transaction block
test=!> COMMIT;
ROLLBACK

对于一个做了很多工作的事务来说,这是非常烦人的,因为这意味着失去到目前为止完成的所有工作。子事务可以帮助我们从这种情况中进行恢复

test=> BEGIN;
BEGIN
test=*> SELECT 'Some work is done';
     ?column?      
-------------------
 Some work is done
(1 row)

test=*> SAVEPOINT a;
SAVEPOINT
test=*> SELECT 12 / (factorial(0) - 1);
ERROR:  division by zero
test=!> ROLLBACK TO SAVEPOINT a;
ROLLBACK
test=*> SELECT 'try to do more work';
      ?column?       
---------------------
 try to do more work
(1 row)

test=*> COMMIT;
COMMIT

注意ROLLBACK TO SAVEPOINT回滚一个旧事务a的时候,会重新开始一个新的子事务。

PL/pgSQL中子事务

即使你从来没有使用过SAVEPOINT语句,但是你可能遇到过子事务。在PL/pgSQL中,上面的代码类似下面

BEGIN   PERFORM 'Some work is done';    BEGIN  -- a block inside a block      PERFORM 12 / (factorial(0) - 1);   EXCEPTION      WHEN division_by_zero THEN         NULL;  -- ignore the error   END;    PERFORM 'try to do more work';END;

每次输入带有EXCEPTION子句的语句块时,都会开启一个新的子事务。当离开这个块的时候会提交该子事务,进入异常处理分支的时候表示回滚。

数据库之间兼容性

其它数据库处理事务中错误的方式不尽相同。不会中止完整的事务,而是仅仅回滚导致错误的语句,从而使事务本身处于活动状态。

当从这样的数据库迁移或移植到PostgreSQL中时,你可能需要在子事务中包装每个语句,以模拟上面的行为。

PostgreSQL JDBC驱动程序中有一个连接参数“autosave”,如果将其设置为“always”,就会在每条语句之前自动设置一个保存点,方便在失败的时候回滚。

如下所示,这种转换技巧存在严重的性能瓶颈。

能测试用例

为了说明由于过度使用子事务导致性能问题,创建下面的测试用例表

CREATE UNLOGGED TABLE contend (
    id integer PRIMARY KEY,
    val integer NOT NULL
)
WITH (fillfactor='50');

INSERT INTO contend (id, val)
SELECT i, 0
FROM generate_series(1, 10000) AS i;

VACUUM (ANALYZE) contend;

这个表数据量很少、不记录日志以及低的填充因子,这些都是为了尽可能降低I/O。这样,可以更好地观察子事务的影响。

我将使用pgbench(一个PostgreSQL附带的基准测试工具)来运行下面的自定义SQL脚本。

BEGIN;
PREPARE sel(integer) AS
   SELECT count(*)
   FROM contend
   WHERE id BETWEEN $1 AND $1 + 100;
PREPARE upd(integer) AS
   UPDATE contend SET val = val + 1
   WHERE id IN ($1, $1 + 10, $1 + 20, $1 + 30);

SAVEPOINT a;
\set rnd random(1,990)
EXECUTE sel(10 * :rnd + :client_id + 1);
EXECUTE upd(10 * :rnd + :client_id);

SAVEPOINT a;
\set rnd random(1,990)
EXECUTE sel(10 * :rnd + :client_id + 1);
EXECUTE upd(10 * :rnd + :client_id);

...

SAVEPOINT a;
\set rnd random(1,990)
EXECUTE sel(10 * :rnd + :client_id + 1);
EXECUTE upd(10 * :rnd + :client_id);

DEALLOCATE ALL;
COMMIT;

第一组测试用例将设置60个子事务,第二组测试用例将设置90个子事务。通过使用预备语句方式尽可能减少查询解析的影响。

在每个数据库会话中,pgbench将:client_id替换成一个唯一的数字。所以只要没有不超过10个客户端,每个客户端的更新操作不会产生冲突,但是会查询其他客户端产生的数据行。

性能测试

由于机器只有8核,因此在测试中将使用6个并发客户端运行十分钟。

为了让“perf top”能查看到重要的信息,需要安装PostgreSQL调试符号信息。这在生产系统上也是推荐的。

TEST 1(60个子事务)

pgbench -f subtrans.sql -n -c 6 -T 600

transaction type: subtrans.sql
scaling factor: 1
query mode: simple
number of clients: 6
number of threads: 1
duration: 600 s
number of transactions actually processed: 100434
latency average = 35.846 ms
tps = 167.382164 (including connections establishing)
tps = 167.383187 (excluding connections establishing)

下面是在测试运行中,使用“perf top --no-children --call-graph=fp --dsos=/usr/pgsql-12/bin/postgres”命令展示的信息

+    1.86%  [.] tbm_iterate
+    1.77%  [.] hash_search_with_hash_value
1.75%  [.] AllocSetAlloc
+    1.36%  [.] pg_qsort
+    1.12%  [.] base_yyparse
+    1.10%  [.] TransactionIdIsCurrentTransactionId
+    0.96%  [.] heap_hot_search_buffer
+    0.96%  [.] LWLockAttemptLock
+    0.85%  [.] HeapTupleSatisfiesVisibility
+    0.82%  [.] heap_page_prune
+    0.81%  [.] ExecInterpExpr
+    0.80%  [.] SearchCatCache1
+    0.79%  [.] BitmapHeapNext
+    0.64%  [.] LWLockRelease
+    0.62%  [.] MemoryContextAllocZeroAligned
+    0.55%  [.]_bt_checkkeys                  
    0.54%  [.] hash_any
+    0.52%  [.] _bt_compare
     0.51%  [.] ExecScan

Test2(90个子事务)

pgbench -f subtrans.sql -n -c 6 -T 600

transaction type: subtrans.sql
scaling factor: 1
query mode: simple
number of clients: 6
number of threads: 1
duration: 600 s
number of transactions actually processed: 41400
latency average = 86.965 ms
tps = 68.993634 (including connections establishing)
tps = 68.993993 (excluding connections establishing)

下面是命令“perf top --no-children --call-graph=fp --dsos=/usr/pgsql-12/bin/postgres”得到的内容

+   10.59%  [.] LWLockAttemptLock
+    7.12%  [.] LWLockRelease
+    2.70%  [.] LWLockAcquire
+    2.40%  [.] SimpleLruReadPage_ReadOnly
+    1.30%  [.] TransactionIdIsCurrentTransactionId
+    1.26%  [.] tbm_iterate
+    1.22%  [.] hash_search_with_hash_value
+    1.08%  [.] AllocSetAlloc
+    0.77%  [.] heap_hot_search_buffer
+    0.72%  [.] pg_qsort
+    0.72%  [.] base_yyparse
+    0.66%  [.] SubTransGetParent
+    0.62%  [.] HeapTupleSatisfiesVisibility
+    0.54%  [.] ExecInterpExpr
+    0.51%  [.] SearchCatCache1

即使考虑到test2都是长事务,与test1相比,仍然有60%性能差距。

子事务实现

要了解发生什么,我们需要了解事务和子事务实现方式。

当一个事务或者子事务中修改了数据后,会为该事务分配一个事务ID(transaction ID)。PostgreSQL在提交日志(commit log)中跟踪这些事务ID信息,日志信息持久化存储在数据目录下pg_xact子目录中。

但是,事务和子事务之间有下面几点差异:

l. 每个子事务包含一个事务或者子事务(“父亲”)

l. 提交子事务不会刷新WAL

l. 一个数据库会话中有且只能有一个事务,但是可以有多个子事务

存储给定子事务的父信息相关的(子)事务信息持久化存储在数据目录下的pg_subtrans子目录。由于这些信息随着包含事务结束后立即变成过去时,因此不必在关闭或者崩溃期间保留这些数据。

子事务和可见性

PostgreSQL中行级版本(元组)可见性由xmin和xmax系统列决定的,分别表示创建和删除事务的事务ID。如果存储的事务ID是子事务信息,那么PostgreSQL还必须查询包含(子)事务的状态,以确定对该事务ID是否可见。

为了确定语句可以看到哪些元组,PostgreSQL在语句(或事务)开始的地方首先获取数据库的快照信息。快照主要包含如下信息:

l. 最大事务ID:任何超过该事务ID都是不可见的

l. 获取快照的时候处于活跃状态的事务和子事务

l. 当前(子)事务中可见的最早命令号(commnad number)

快照通过查询进程数组(process array)信息来进行初始化,进程数组保存在共享内存中并包含有当前运行进程的相关信息。当前,它也包含后端进程的当前事务ID,并且每个会话最多可以容纳64个未中止的子事务。如果有超过64个这样的子事务,那么快照被标记为子事务溢出(suboverflowed)。

结果分析

一个子溢出的快照不会包含检测可见性的所有数据信息,所以PostgreSQL有时将不得不求助于pgsubtrans。这些页缓存在共享内存中,但是在perf中可以看到SimpleLruReadPageReadOnly函数排在前面输出。其它事务必须更新pg_subtrans后才能注册子事务,可以在perf输出中看到如何与读进程争夺轻量级锁。

分析子事务太多问题

除了查看”perf top”,还有其它指向该问题方向的可疑点:

l 运行单个进程的时候负载表现很好,但是并发多个数据库会话后会变高

l 在pgstatactivity视图中经常看到等待实践“SubtransControlLock”

l 如果使用“pgexportsnapshot()”函数导出快照信息,数据目录下的pg_snapshots 子目录保存的结果文件中包含有“sof:1”信息,其表示子事务数组溢出

结论

子事务是一个很好的工具,但是需要合理使用它。如果需要并发,每个事务不要启动超过64个子事务。

本文中提供的分析方法应该可以帮助你确定是否存在类似问题。

找到问题的根因可能很棘手。例如:对于SQL语句的每个结果行(可能在触发器中)调用的带有异常处理程序的函数,启动新的子事务不会那么明显。

原文地址

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

CENTER_PostgreSQL_Community


评论:7   浏览: 9230                   顶: 686  踩: 639 

请在登录后发表评论,否则无法保存。

1# __ xcvxcvsdf 回答于 2024-11-11 20:20:04+08
http://bjtcxxw.cn/hnzk/ https://niujie.tiancebbs.cn/ http://ly.shtcxxw.cn/huangshi/ http://yz.cqtcxxw.cn/yingkou/ http://tuiguang.hntcxxw.cn/wulumuqi/ http://bjtcxxw.cn/dp/ http://cf.lstcxxw.cn/shjinshan/ https://fenlei.tiancebbs.cn/fsq/ http://gx.lztcxxw.cn/qyzp/ http://cf.lstcxxw.cn/ali/ https://guansheng.tiancebbs.cn/ http://gx.lztcxxw.cn/jlly/ http://ouyu.hftcbmw.cn/essjj/ http://jingren.hftcbmw.cn/fuyang/ https://xuchi.tiancebbs.cn/ https://qiuxiapu.tiancebbs.cn/ https://cyqheping.tiancebbs.cn/

2# __ xcvxcvsdf 回答于 2024-11-10 03:13:47+08
https://xing.tiancebbs.cn/ http://jinqiang.ahtcbmw.cn/zhoushan/ https://binhedong.tiancebbs.cn/ http://shenghuo.china-bbs.com/wujiaqu/ http://km.lstcxxw.cn/hongkoush/ http://huilong.sctcbmw.cn/ktvzp/ http://nalei.zjtcbmw.cn/qyx/ http://ruanwen.xztcxxw.cn/jiamusi/ http://taiying.njtcbmw.cn/hnly/ http://ruanwen.xztcxxw.cn/luwanqu/ https://taiyanggong.tiancebbs.cn/ http://huaguang.jxtcbmw.cn/fyfz/ http://shenghuo.china-bbs.com/sanmenxia/ https://ncgaoping.tiancebbs.cn/ https://yangchun.tiancebbs.cn/ http://js.sytcxxw.cn/sclz/ https://huichun.tiancebbs.cn/

3# __ xcvxcvsdf 回答于 2024-10-30 03:25:19+08
http://shenghuo.china-bbs.com/xuchang/ http://fs.shtcxxw.cn/changchun/ https://jingxi.tiancebbs.cn/ https://hdqchengxi.tiancebbs.cn/ http://jinqiang.ahtcbmw.cn/hnkf/ http://ty.cqtcxxw.cn/wenshan/ http://ruanwen.xztcxxw.cn/cqlp/ http://bjtcxxw.cn/qinzhou/ http://jinqiang.ahtcbmw.cn/xsqzw/ http://fs.shtcxxw.cn/lanzhou/ https://honglan.tiancebbs.cn/nang/ http://tuiguang.hntcxxw.cn/wuzhong/ https://xisaishan.tiancebbs.cn/ https://zjkgaoxin.tiancebbs.cn/ http://js.sytcxxw.cn/linxia/ http://nalei.zjtcbmw.cn/esmbc/ http://taiying.njtcbmw.cn/hywl/

4# __ xcvxcvsdf 回答于 2024-10-20 14:48:04+08
https://www.tiancebbs.cn/ershoufang/469995.html https://aihuishou.tiancebbs.cn/sh/4902.html https://www.tiancebbs.cn/ershoufang/473541.html https://www.tiancebbs.cn/ershoufang/471313.html https://zulin.tiancebbs.cn/sh/2707.html https://www.tiancebbs.cn/ershoufang/473944.html https://changshushi.tiancebbs.cn/hjzl/467489.html https://www.tiancebbs.cn/qitafuwu/56824.html https://zulin.tiancebbs.cn/sh/2516.html https://sz.tiancebbs.cn/qtzypx/54413.html https://changshushi.tiancebbs.cn/hjzl/460096.html https://taicang.tiancebbs.cn/hjzl/458239.html https://aihuishou.tiancebbs.cn/sh/776.html https://zulin.tiancebbs.cn/sh/2440.html https://hezhou.tiancebbs.cn/qths/454658.html https://zulin.tiancebbs.cn/sh/2154.html https://zulin.tiancebbs.cn/sh/1841.html

5# __ xcvxcvsdf 回答于 2024-10-17 22:48:28+08
http://ly.shtcxxw.cn/tangshan/ http://wogao.ahtcbmw.cn/jiujiang/ http://jingren.hftcbmw.cn/huaian/ http://tuiguang.hntcxxw.cn/ziyang/ http://ly.shtcxxw.cn/jingzhou/ http://shengshun.njtcbmw.cn/sansha/ http://nalei.zjtcbmw.cn/guyuan/ http://cf.lstcxxw.cn/tcjy/ http://bjtcxxw.cn/qiandongnan/ http://yz.cqtcxxw.cn/cqmj/ https://yongan.tiancebbs.cn/ http://nalei.zjtcbmw.cn/ysbz/ http://yz.cqtcxxw.cn/xunwu/ http://jingren.hftcbmw.cn/nanhuish/ http://nalei.zjtcbmw.cn/zjnb/ https://fenlei.tiancebbs.cn/yu-lin/ http://gx.lztcxxw.cn/yantai/

6# __ xiaowu 回答于 2024-04-24 10:43:25+08
送元二使安西的诗意:https://www.nanss.com/xuexi/18302.html 伟人名言:https://www.nanss.com/yulu/19250.html 党员学习心得:https://www.nanss.com/xuexi/19682.html 芦蒿的营养价值:https://www.nanss.com/yinshi/19560.html 足球起源于哪个国家:https://www.nanss.com/wenti/18359.html 童年读后感500字:https://www.nanss.com/xuexi/19179.html 旅游行业报告:https://www.nanss.com/gongzuo/19313.html 社区低保工作总结:https://www.nanss.com/gongzuo/20098.html 老鼠肉可以吃吗:https://www.nanss.com/yinshi/18244.html 好听的游戏女名字:https://www.nanss.com/mingcheng/18247.html 在我成长的道路上:https://www.nanss.com/xuexi/20092.html 立冬当天吃什么食物:https://www.nanss.com/wenti/19527.html 云母是什么:https://www.nanss.com/wenti/18975.html 奖励通知:https://www.nanss.com/gongzuo/19085.html 薪酬报告:https://www.nanss.com/gongzuo/19913.html 英语名句:https://www.nanss.com/xuexi/18832.html 医院科主任述职报告:https://www.nanss.com/gongzuo/19171.html 花胶的功效与作用:https://www.nanss.com/yinshi/19766.html 中秋节来历简介简短:https://www.nanss.com/shenghuo/19412.html 电磁炉辐射有多大:https://www.nanss.com/wenti/19550.html 公司的规章制度:https://www.nanss.com/gongzuo/19083.html 煮豆燃萁:https://www.nanss.com/xuexi/18908.html 牙龈肿痛10秒快速消肿:https://www.nanss.com/wenti/19473.html 最打动领导的祝福语简短:https://www.nanss.com/yulu/19135.html 生活作文:https://www.nanss.com/xuexi/19308.html 建筑材料论文:https://www.nanss.com/xuexi/19176.html 压强单位:https://www.nanss.com/xuexi/18957.html 青蓝工程徒弟总结:https://www.nanss.com/gongzuo/19217.html 高中研究性学习报告:https://www.nanss.com/xuexi/18604.html 七年级语文教学反思:https://www.nanss.com/gongzuo/19285.html

7# __ achao 回答于 2020-04-07 19:51:34+08

字节跳动武汉研发中心急招PG DBA, 有意者联系pengqianchao@bytedance.com

职位描述 1、保证 Postgresql 数据库正常稳定运行; 2、根据业务发展需求,改进 DB 集群架构,改进负载均衡方案、备份方案、安全方案; 3、及时定位和排除 DB 故障,并对 DB 进行持续性能优化,DB 参数调优; 4、提供数据库开发支持,负责 SQL 代码的上线审核; 5、完善数据库自动化监控和运维方案; 6、参与数据安全合规和异地多活方案实施。 职位要求 1、统招本科以上学历,3年以上数据库管理经验; 2、熟悉并能独立进行 Postgresql 的集群搭建及管理; 3、熟悉 Shell & Python 编程,能使用脚本编写工具提升工作效率; 4、熟悉 Linux 平台服务器进行管理和维护工作; 5、有系统、网络、存储、系统或硬件等方面经验者优先; 6、良好的团队协作能力,积极主动,乐于接受挑战,能应对突发应急情况。



发表评论:
加入我们
QQ群1:5276420
QQ群2:3336901
QQ群3:254622631
文档群:150657323
文档翻译平台:按此访问
社区邮件列表:按此订阅
扫码关注
© PostgreSQL中文社区 ... (自2010年起)