暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL性能难题

原创 eternity 2022-08-05
670

本周,我在开源PostgreSQL中偶然发现了一个非常大的惊喜。我认为这将是一个有趣的难题,挑战你们所有在互联网上认为自己是PostgreSQL专家的人的技能。

这很简单。我在EC2实例上创建了Centos 7的新副本,并从官方PG yum存储库加载了最新的开源PostgreSQL生产版本(14.4)。然后,我创建了一个简单的表,其中包含一个文本列和两个非空的bigint列。我使用generate_series()将1亿行伪数据填充到表中,然后以相同的方式对两个bigint列进行索引。我做了一次真空分析,以获得统计数据,并确保表格准备好进行简单测试。最后,我快速跳转了PG并清除了OS页面缓存,这样我们就可以冷启动测试了。

现在我运行了一个非常简单的查询:

select count(mydata) from test where mynumber1 < 500000;
复制

现在让我震惊的是,每次从一个冷缓存开始,那里的查询总是在不到500毫秒的时间内完成。但是当我在另一个数字列上运行相同的查询时,它始终需要超过100秒!是的,是秒,而不是毫秒。使用完全相同的执行计划,处理相同数量的行!(50万行,占表格的0.5%。)

现在,看到表中的特定数据会影响性能,我并不感到惊讶——这对于任何熟悉RDBMS性能的人来说都是101。但这种差异的程度远远高于我的预期。我可以一遍又一遍地重复这两个查询,但执行时间相差相同。

同样:这是一个简单的表,有两个非空的bigint列。列的定义是相同的——相同的数据类型、相同的索引类型、相同执行计划、相同的处理行数。唯一的区别是表中的数据。不涉及空值。而且执行时间的差异是天文数字——对于我的一些迭代,执行速度要慢600倍!!

你能猜出表中的数据是什么吗?

下面是一份完整而详细的复制副本——我刚刚排除了用于填充测试表的表达式。我很好奇是否有人能猜出那些表情是什么!!

如果有人在博客评论或twitter上告诉我一个猜测,我将使用下面的文字记录来测试他们的表达。那么,我相信任何一个在两篇专栏文章中表现出同样惊人差异的人。名誉和荣耀,我的朋友们。在一周左右的时间里(如果有人猜得很快的话,可能会更早),我将发布我自己从下面的会话记录中编辑的两个表达式。

如果有人能解释原因的话,那就是重大的荣誉和荣誉。

PS. Mohamed,如果你在读这篇文章,你是不允许猜的!(他是第一个发现的,但我们一起努力想清楚到底发生了什么。)

instance_type: r5.large
volume_type: gp2
volume_size: 100

# CentOS Linux 7 x86_64 HVM EBS ENA 2002_01
us-west-2: ami-0bc06212a56393ee1

[root@ip-172-31-36-129 ~]# rpm -q postgresql14-server
postgresql14-server-14.4-1PGDG.rhel7.x86_64
[root@ip-172-31-36-129 ~]#
复制
pg-14.4 rw root@db1=# create table test ( mydata text, mynumber1 bigint not null, mynumber2 bigint not null);
CREATE TABLE
Time: 6.617 ms

pg-14.4 rw root@db1=# insert into test
[more] - >   select 'data-XXXXXXXXXXXX'
[more] - >     , <GUESS THIS FIRST EXPRESSION>
[more] - >     , <GUESS THIS SECOND EXPRESSION>
[more] - >   from generate_series(1,100000000) as n
[more] - > ;
INSERT 0 100000000
Time: 224101.677 ms (03:44.102)

pg-14.4 rw root@db1=# create index test_mynumber1 on test(mynumber1);
CREATE INDEX
Time: 229785.314 ms (03:49.785)

pg-14.4 rw root@db1=# create index test_mynumber2 on test(mynumber2);
CREATE INDEX
Time: 108900.146 ms (01:48.900)

pg-14.4 rw root@db1=# vacuum verbose analyze test;
INFO:  00000: vacuuming "public.test"
LOCATION:  lazy_scan_heap, vacuumlazy.c:940
INFO:  00000: launched 1 parallel vacuum worker for index cleanup (planned: 1)
LOCATION:  do_parallel_vacuum_or_cleanup, vacuumlazy.c:2766
INFO:  00000: table "test": found 0 removable, 79913800 nonremovable row versions in 665949 out of 833334 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 757
Skipped 0 pages due to buffer pins, 0 frozen pages.
CPU: user: 8.44 s, system: 3.97 s, elapsed: 96.48 s.
LOCATION:  lazy_scan_heap, vacuumlazy.c:1674
INFO:  00000: vacuuming "pg_toast.pg_toast_16454"
LOCATION:  lazy_scan_heap, vacuumlazy.c:940
INFO:  00000: table "pg_toast_16454": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
DETAIL:  0 dead row versions cannot be removed yet, oldest xmin: 758
Skipped 0 pages due to buffer pins, 0 frozen pages.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
LOCATION:  lazy_scan_heap, vacuumlazy.c:1674
INFO:  00000: analyzing "public.test"
LOCATION:  do_analyze_rel, analyze.c:331
INFO:  00000: "test": scanned 30000 of 833334 pages, containing 3600000 live rows and 0 dead rows; 30000 rows in sample, 100000080 estimated total rows
LOCATION:  acquire_sample_rows, analyze.c:1357
VACUUM
Time: 100010.196 ms (01:40.010)



[root@ip-172-31-36-129 ~]# service postgresql-14 restart;
Redirecting to /bin/systemctl restart postgresql-14.service
[root@ip-172-31-36-129 ~]# sync; echo 1 > /proc/sys/vm/drop_caches
[root@ip-172-31-36-129 ~]#



pg-14.4 rw root@db1=# select count(mydata) from test where mynumber1<500000;
 count
--------
 499999
(1 row)

Time: 155.314 ms



[root@ip-172-31-36-129 ~]# service postgresql-14 restart;
Redirecting to /bin/systemctl restart postgresql-14.service
[root@ip-172-31-36-129 ~]# sync; echo 1 > /proc/sys/vm/drop_caches
[root@ip-172-31-36-129 ~]#



pg-14.4 rw root@db1=# select count(mydata) from test where mynumber2<500000;
 count
--------
 499180
(1 row)

Time: 105202.086 ms (01:45.202)



[root@ip-172-31-36-129 ~]# service postgresql-14 restart;
Redirecting to /bin/systemctl restart postgresql-14.service
[root@ip-172-31-36-129 ~]# sync; echo 1 > /proc/sys/vm/drop_caches
[root@ip-172-31-36-129 ~]#


pg-14.4 rw root@db1=# select count(mydata) from test where mynumber1<500000;
 count
--------
 499999
(1 row)

Time: 494.063 ms



[root@ip-172-31-36-129 ~]# service postgresql-14 restart;
Redirecting to /bin/systemctl restart postgresql-14.service
[root@ip-172-31-36-129 ~]# sync; echo 1 > /proc/sys/vm/drop_caches
[root@ip-172-31-36-129 ~]#



pg-14.4 rw root@db1=# select count(mydata) from test where mynumber2<500000;
 count
--------
 499180
(1 row)

Time: 104084.311 ms (01:44.084)
复制

更新7/18:这是我原始测试的完整插入语句:

pg-14.4 rw root@db1=# insert into test
[more] - >   select 'data-XXXXXXXXXXXX'
[more] - >     ,n
[more] - >     ,random()*100000000
[more] - >   from generate_series(1,100000000) as n
[more] - > ;
INSERT 0 100000000
Time: 224101.677 ms (03:44.102)
复制

第一个正确猜测的人是Franck Pachot,他于17日UTC 06:05通过twitter DM进行了猜测。UTC 17日14:33,东华在博客评论中第一个通过公开帖子正确猜测的人是donghual@。

UTC 17日14时48分,推特上的RustProof Labs猜测了我所做的事情背后的想法,尽管解释没有一个表达式供我测试/测量。UTC 18日00:14,Denish Patel在twitter上做出了一个技术上不匹配的猜测,但我对此表示赞赏,因为他的解释表明这只是一个打字错误。我没有运行测试,但我预计会出现类似的行为。

两人还猜测了其他表现出类似行为的表情,尽管每次执行不到100秒。UTC 17日06时38分,Andy Sayer在twitter上猜测mod(n,200)*500000,每次执行约50秒。UTC 18日16:26,Joe Wildish在一篇博客评论中猜测,当1然后n/120或500000+n结束时,情况为n%120,每次执行约30秒。乔在17日UTC 09:15从马蒂斯那里得到了一条博客评论,这也是值得称赞的,因为他的解释表明他的猜测是正确的,他的表达式只需要稍微调整一下,以避免整数超出范围的错误。

关于Jeremy

构建和运行可扩展和可靠数据平台台.about.me/jeremy_schneider查看Jeremy的所有帖子。

原文标题:PostgreSQL Performance Puzzle
原文作者:JEREMY
原文链接:https://ardentperf.com/2022/07/16/postgresql-performance-puzzle/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论