我们有一个非常简单的表,DDL如下:
CREATE TABLE public.next_id (
id varchar(255) NOT NULL,
next_value int8 NOT NULL,
CONSTRAINT next_id_pk PRIMARY KEY (id)
);
该表只有约120行,除主键外没有任何索引。
当我在DBeaver中对运行PostgreSQL 10.5或11.2的两台Linux服务器之一执行以下UPDATE查询时,大约需要50毫秒:
update NEXT_ID set next_value=next_value+1 where id='Session';
但是,如果我将DBeaver指向运行PostgreSQL 9.5.3的服务器,则平均大约需要3毫秒。
现在,如果我按如下方式创建一个FOR循环:
do $$
begin
for i in 1..100000 loop
update NEXT_ID set next_value=next_value+1 where id='Session';
end loop;
end;
$$;
在所有机器上花费大约相同的时间(〜1.5s)。换句话说,错误的余量可能等于一条记录更新带来的额外延迟。
感觉语句周围的事务涉及某种开销。
如何获取有关PostgreSQL花时间的更多信息?
我已经尝试在“较慢”的服务器上对上述单个记录UPDATE进行EXPLAIN ANALYSE
,并且得到以下信息:
Update on next_id (cost=0.00..2.58 rows=1 width=36) (actual time=0.057..0.057 rows=0 loops=1)
-> Seq Scan on next_id (cost=0.00..2.58 rows=1 width=36) (actual time=0.043..0.044 rows=1 loops=1)
Filter: ((id)::text = 'Session'::text)
Rows Removed by Filter: 125
Planning Time: 0.066 ms
Execution Time: 0.080 ms
这似乎表明该查询实际上仅需要几毫秒即可计划和执行。那么剩下的时间在哪里?
所有涉及的服务器都使用相同的数据库,并且已经在所有服务器上复制了数据库。
顺便说一句,我对让我说对主键使用VARCHAR(255)的主意不感兴趣,因为在所有服务器上都一样,这不是问题的重点
更新:我们已经注意到,速度较慢的Linux计算机与速度较快的Linux计算机之间的主要区别是文件系统。我的机器将BTRFS用于Postgres所在的文件系统,而速度更快的机器正在使用XFS。
在各种文件系统上快速搜索Postgres的人发现,有些人说在BTRFS上使用Postgres不好(不要越过信息流!)。
我们将尝试重新格式化我的计算机以使用XFS,以查看是否有区别。
同时,我仍然有兴趣将赏金给予任何可以告诉我如何记录多余时间的人。
UPDATE2:按照尼克·巴恩斯(Nick Barnes)在评论中的建议,我显式地运行了一系列BEGIN; UPDATE ...; COMMIT;
语句,日志给出了以下输出:
LOG: duration: 0.025 ms parse <unnamed>: begin
LOG: duration: 0.014 ms bind <unnamed>: begin
LOG: duration: 0.003 ms execute <unnamed>: begin
LOG: duration: 0.045 ms parse <unnamed>: update NEXT_ID set next_value=next_value+1 where id='Session'
LOG: duration: 0.055 ms bind <unnamed>: update NEXT_ID set next_value=next_value+1 where id='Session'
LOG: duration: 0.059 ms execute <unnamed>: update NEXT_ID set next_value=next_value+1 where id='Session'
LOG: duration: 0.004 ms parse <unnamed>: commit
LOG: duration: 0.003 ms bind <unnamed>: commit
LOG: duration: 50.237 ms execute <unnamed>: commit
是的,尼克,开销肯定在COMMIT中。但是它在做什么?有什么方法可以在日志中获取有关在50毫秒内它正在做什么的更详细的信息?
答案 0 :(得分:3)
UPDATE
本身很便宜;在提交事务之前,您的新数据无需在崩溃后幸存,因此仅对内存中的缓冲区(服务器空闲时将其刷新到磁盘)进行更改。
直到您提交事务后,服务器才需要为您提供持久性保证。 Postgres使用write-ahead log(WAL)处理碰撞安全,并且当您COMMIT
时,您正在等待WAL同步写入磁盘。
这使得提交延迟高度依赖于文件系统和底层硬件,并且如果您的PG10实例正在等待BTRFS完成写时复制或其他操作,那肯定可以解释您所造成的差异看到。
要确认这是原因,可以通过禁用fsync
来跳过同步磁盘写入操作(尽管这样做会使您面临数据损坏的风险,因此请务必在一次性实例上对其进行测试)。一个比较安全和侵入性较小的选项是在事务开始时使用SET LOCAL synchronous_commit = off
,如果您没有运行同步复制,则应该具有相同的效果。