有时,我们的一位客户会查看数据库中最耗时的语句(使用pg_stat_statements或pgBadger),并发现COMMIT排名靠前。通常,COMMIT这是 PostgreSQL 中非常快的语句,因此值得研究。在本文中,我将探讨速度缓慢的可能原因COMMIT并讨论您可以采取的措施。
#PG培训#PG考试#postgresql培训#postgresql考试#postgresql认证
Commit PostgreSQL中的基本活动
缓慢COMMIT是一个令人惊讶的观察结果,因为在 PostgreSQL 中提交事务是一个非常简单的活动。在大多数情况下,COMMIT只需
- 将提交日志中事务的两位设置为TRANSACTION_STATUS_COMMITTED(0b01) (持久保存pg_xact)
- 如果track_commit_timestamp设置为on,则记录提交时间戳(保留在 中pg_commit_ts)
- 将预写日志(WAL)(保存在)刷新pg_wal到磁盘,除非synchronous_commit设置为off
请注意,由于 PostgreSQL 的多版本架构,和通常COMMIT都是ROLLBACK非常快的操作:它们都不需要触及表,它们仅在提交日志中注册事务的状态。
最常见的速度慢原因Commit:磁盘问题
从上面可以看出,导致速度缓慢的一个潜在原因是磁盘 I/O。毕竟,将 WAL 刷新到磁盘会导致 I/O 请求。因此,您应该检查的第一件事是磁盘是否有问题或负载过大:
-
在 Linux 上,您可以使用“ vmstat 1”或“ sar -p 1”等命令来测量等待 I/O 所花费的 CPU 时间百分比(“wa”vmstat和“ %iowait” sar)。如果该值持续高于 10,则可以肯定 I/O 系统处于压力之下。
-
使用NAS时,您应该检查 TCP 网络是否过载。
-
如果存储是共享SAN或NAS,则磁盘可能会与其他机器共享,您应该检查存储系统是否存在争用。
-
磁盘故障、其他硬件问题或操作系统问题可能会导致间歇性性能问题。检查内核日志以获取消息。
如果我们可以排除磁盘问题是导致提交缓慢的原因,我们就必须进行进一步调查。
阅读源代码以了解更多信息
如果我们想了解事务提交期间发生了什么,最简单的方法就是阅读源代码。许多人没有意识到开源的真正威力:你不必猜测或从软件供应商那里购买支持,而是可以自己查看。PostgreSQL 源代码写得很好,文档齐全,许多部分不需要专家 C 技能即可理解。相关代码位于CommitTransaction()中的函数中src/backend/access/transam/xact.c。
在特殊情况下,导致速度变慢的原因有多种COMMIT,例如许多并发NOTIFY语句的争用(您可以从 中的数据库 0 上的锁来诊断这种情况pg_locks)。但是,通常罪魁祸首是以下部分中描述的三种情况之一。
Commit由于延迟约束和触发器导致速度缓慢
通常,PostgreSQL 会在修改受约束表的语句中检查约束。使用延迟约束时,PostgreSQL 会等待检查,直到事务结束。一个用例是,如果您将数据插入具有循环外键约束的表中:
CREATE TABLE department (department_id bigint PRIMARY KEY,name text NOT NULL,manager bigint NOT NULL
);CREATE TABLE employee (employee_id bigint PRIMARY KEY,name text NOT NULL,department_id bigintREFERENCES department NOT NULL
);-- deferred foreign key
ALTER TABLE departmentADD FOREIGN KEY (manager) REFERENCES employeeDEFERRABLE INITIALLY DEFERRED;
延迟外键可以轻松创建新的部门:
START TRANSACTION;-- won't raise a foreign key violation yet
INSERT INTO department(department_id, name, manager)
VALUES (12, 'Flower Picking', 123);INSERT INTO employee(employee_id, name, department_id)
VALUES (123, 'John Wurzelrupfer', 12);-- deferred constraint is valid now
COMMIT;
由于 PostgreSQL 在提交时检查延迟约束,因此它们可能会减慢COMMIT处理速度。通常,检查约束非常快 — 这是索引查找。但是,许多此类检查可能会累积在较大的事务中,并且总执行时间可能会大大减慢COMMIT。
PostgreSQL 还具有可延迟的约束触发器COMMIT,并且像延迟约束一样会减慢速度。有关约束触发器用例的进一步讨论,请参阅本文。
如果您能确定延迟约束或触发器是导致 缓慢的原因COMMIT,那么可能就没问题了,无需担心。毕竟,您必须在某个时候检查这些约束。
Commit游标导致速度慢With Hold
游标允许客户端分块提取查询结果集,这可以简化处理并避免客户端内存不足。但是,常规游标只能存在于数据库事务的上下文中。因此,在涉及用户交互时不能使用游标:游标持有的快照会阻碍进程VACUUM并导致表膨胀和更严重的问题。此外,ACCESS SHARE在事务期间持有的锁会导致并发ALTER TABLE或问题TRUNCATE。
为了避免与事务绑定的限制,您可以使用游标WITH HOLD。此类游标可以比创建它的事务存活更久,例如可用于实现分页。PostgreSQL 通过在提交时具体化结果集来实现WITH HOLD游标。如果游标后面的查询很昂贵,那么速度会变得非常慢。此外,您一定不要忘记在完成后关闭此类游标,否则具体化结果集将占用服务器资源,直到数据库会话结束。COMMIT
如果您可以将游标确定WITH HOLD为提交缓慢的原因,则可以通过调整游标定义中的查询以使其运行得更快来改善这种情况。由于 PostgreSQL 不会优化游标中的查询以加快计算完整结果集的速度,因此有时将其设置cursor_tuple_fraction为 1.0 可以帮助加快提交处理速度。
Commit同步复制导致速度慢
流式复制和逻辑复制默认都是异步的。如果您使用复制来实现高可用性,并且不想冒丢失已提交事务的风险,则可以使用同步复制。使用同步复制时,提交时的操作顺序为:
将COMMIT记录写入WAL并刷新(这是实际的持久提交)
等待同步备用数据库报告已获取所有WAL信息
使事务在主服务器上可见
向客户报告成功
如果主服务器和同步备用服务器之间的网络延迟较高,COMMIT则需要很长时间。您可以通过检查pg_stat_activity频繁或持续时间较长的“ SyncRep”等待事件来诊断。通常,您只想在网络延迟较低的机器(即物理上靠近的机器)之间使用同步复制。
Commit第三方扩展导致速度缓慢
PostgreSQL 最出色的特性之一是其可扩展性。它允许您编写与 PostgreSQL 核心交互的扩展,而无需修改服务器代码。第三方代码可以“挂接”到 PostgreSQL 中以修改其行为。在许多其他选项中,您可以使用 C 函数RegisterXactCallback()注册 PostgreSQL 在提交时执行的回调。因此,如果您要查找 PostgreSQL 速度缓慢的原因COMMIT,还应该查看数据库中安装的扩展。例如,在远程数据源实现事务处理的外部数据包装器可能希望在 PostgreSQL 提交本地事务时提交远程事务。那么,远程端的高网络延迟或缓慢的事务处理将减慢 PostgreSQL 的速度COMMIT。
结论
查看源代码,我们可以轻松找到导致速度慢的可能原因COMMIT:除了磁盘问题的明显原因之外,还应该将延迟约束、游标WITH HOLD和同步复制视为可能的原因。