本周,我在开源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/