我们有一个非常简单的表,DDL如下:
CREATE TABLE public.next_id (
id varchar(255) NOT NULL,
next_value int8 NOT NULL,
CONSTRAINT next_id_pk PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)
该表仅包含约120行,除主键外没有任何索引。
当我在DBeaver中对运行PostgreSQL 10.5或11.2的两台Linux服务器之一执行以下UPDATE查询时,大约需要50毫秒:
update NEXT_ID set next_value=next_value+1 where id='Session';
Run Code Online (Sandbox Code Playgroud)
但是,如果我将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;
$$;
Run Code Online (Sandbox Code Playgroud)
在所有机器上花费大约相同的时间(〜1.5s)。换句话说,错误容限可能等同于一条记录更新所经历的额外延迟。
感觉语句周围的事务涉及某种开销。
如何获得有关PostgreSQL花时间的更多信息?
我尝试EXPLAIN ANALYSE在“较慢”的服务器上对上述单条记录UPDATE进行操作,我得到以下信息:
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
Run Code Online (Sandbox Code Playgroud)
这似乎表明查询实际上仅花费了几毫秒即可计划和执行。那么剩下的时间在哪里呢?
所涉及的所有服务器都使用已在所有服务器上复制的同一数据库。
顺便说一句,我对告诉我使用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
Run Code Online (Sandbox Code Playgroud)
是的,尼克,开销肯定在COMMIT中。但是它在做什么?有什么方法可以在日志中获取有关该50毫秒内正在执行的操作的更详细的信息?
其UPDATE本身相当便宜;在提交事务之前,新数据无需在崩溃后幸存下来,因此仅对内存缓冲区进行更改(服务器闲暇时将其刷新到磁盘)。
直到您提交事务后,服务器才需要为您提供持久性保证。Postgres 使用预写日志(WAL) 来处理崩溃安全,当您执行此操作时COMMIT,您正在等待 WAL 同步写入磁盘。
这使得提交延迟高度依赖于文件系统和底层硬件,如果您的 PG10 实例正在等待 BTRFS 完成写时复制或其他操作,这肯定可以解释您所看到的差异。
要确认这就是原因,您可以通过禁用来跳过同步磁盘写入fsync(尽管这会使您面临数据损坏的风险,因此请务必在一次性实例上进行测试)。一个更安全且侵入性较小的选项是在SET LOCAL synchronous_commit = off事务开始时执行,如果您没有运行同步复制,这应该具有相同的效果。
| 归档时间: |
|
| 查看次数: |
173 次 |
| 最近记录: |