希望有更深的Postgres /数据库知识的人可以分享一些见识。
背景:
我有一个Postgres v11数据库,其中运行着一些数据。现在,我想作为一个事务在其上运行约20万次操作(它们应该全部成功,或者不进行任何更改)。这些事务包括INSERT和UPDATE语句的混合,还涉及数据库中的一些触发器。
如果我在事务中运行查询,则大约需要70分钟
BEGIN;
// statement
// statement
// statement
// ...
COMMIT;
如果我删除周围的事务块并仅运行查询,则将花费7分钟。
// statement
// statement
// statement
// ...
现在,我怀疑性能差异与事务保留一些临时表有关,这些临时表会遇到缓冲区大小或索引问题,从而导致速度变慢,但是由于我对Postgres内部没有深入的了解,因此我实际上并不了解。我已经尝试过更改各种WAL设置,但是没有明显的不同。
所以我有2个问题:
编辑1
有关表和查询的其他信息
在不透露具体细节的情况下,我将尽我所能提供尽可能多的信息。
有5个与此交易相关的主表-accounts
,currencies
,transactions
,transaction_logs
,balance
。
transactions
表具有3个外键-2至accounts
和1至currencies
。
balances
表具有2个外键-1到accounts
和1到currencies
。
transaction_logs
表具有3个外键-1到transactions
,1到accounts
,1到currencies
。
批处理开始于插入所有相关的帐户和货币。 (约1万个帐户和2种货币)
接下来,它是插入和事务表更新的混合-插入在一个插入中分组最多300行,而更新基于PK,因此一次仅更新1行。总共大约有14万次插入和6万次更新。
插入和更新操作都依次执行触发器,该触发器将更新余额表并将两行插入transaction_log表中。
典型的插入看起来像
INSERT INTO transactions (from_account_id, to_account_id, currency_id, amount, metadata, status) VALUES (1, 1, 1, 10.00, '{"json":"blob"}', 'pending');
典型更新如下
UPDATE transactions SET status = 'finished' WHERE id = 1;
编程环境为node.js
,驱动程序为pg
。
编辑2
对每个人都无法提供更多信息深表歉意-我试图创建一个封闭的测试环境以重现该错误。在这样做的同时,我设法解决了问题的症结。结构本身似乎无关紧要。 变慢是由于在同一笔交易中一遍又一遍地更新同一行引起的。
我管理的最简单的复制是创建一个包含2个字段的表,添加几行,然后重复更新第一行。没有交易,操作时间保持相对恒定。但是,在一笔交易中,交易开始攀升-大约10万次更新,比开始时高出约3倍。在我的计算机上,进行10万次更新测试的总运行时差异是交易版本和非交易版本之间的2.5倍。
但是,如果您的数据更加多样化-而不是更新同一行,而是更新不同的行,那么就不会出现此问题,并且使用分布良好的数据,交易实际上会更快。
PS。在v12上也进行了测试,效果大致相同。
有人知道为什么会这样吗?
编辑3
我已经创建了一个存储库来演示此问题https://github.com/DeadAlready/pg-test
答案 0 :(得分:2)
我把这个问题带到了postgres官方邮件列表中,这就是我得到的答案,我将在这里分享给以后的搜索者。
TL; DR;
链接到线程:
https://www.postgresql.org/message-id/flat/7624.1581628574%40sss.pgh.pa.us
完整答案:
是的,这并不奇怪。每个新更新都会创建一个新版本的 它的行。当您在单独的交易中进行交易时, 事务N + 1提交,系统可以识别该行版本 由交易N创建的交易已失效(不再对任何人可见),并且 回收它,允许磁盘上存在的行版本数 保持大致恒定。但是,没有那么好 通过事务仍然创建的行版本的内部管理 运行。因此,当您在一笔交易中进行N次更新时, 成为磁盘上N个注定但尚未回收的行版本。
除了磁盘空间膨胀之外,这还很糟糕,因为以后的更新 必须浏览由早期更新创建的所有行版本, 寻找他们应该更新的版本。所以你有一个O(N ^ 2) 与此相关的成本,这无疑是您所观察的。
除了“不要那样做”之外,没有其他任何真正好的解决方案。 David在附近使用临时表的建议无济于事,因为 无论表是临时表还是常规表,这种行为都是相同的。
原则上,也许我们可以改善死行的粒度 检测,这样,如果行版本既被创建又被删除 当前的交易,我们没有可以 看到它,我们可以继续并将行标记为已死。但是还不清楚 那将是值得的额外费用。当然没有现有的PG 发布会尝试这样做。
致谢,汤姆·莱恩
答案 1 :(得分:0)
根据我的经验,有一个最佳的中等交易规模。
此事务中有许多新记录。此交易仅更改那些新记录吗?该算法对以前已经存在的记录的影响有多大。我的想法是,您可以非常快速地在多个中间范围内构建数据,并且在必要时通过删除这些新记录在单独的事务中删除这些新数据。
如果现有记录已更改,则可以在算法末尾的一个较小事务中移动对这些现有记录的处理。如果需要回滚,则可以回滚此小事务,然后删除希望可以很容易识别的那些新记录。