Pet*_*aus 46 postgresql performance query-performance
我有一个 PostgreSQL 9.2 实例,它在 RHEL 6.3、8 核机器上运行,内存为 16GB。服务器专用于此数据库。鉴于默认的 postgresql.conf 在内存设置方面相当保守,我认为允许 Postgres 使用更多内存可能是个好主意。令我惊讶的是,遵循wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server 上的建议实际上显着降低了我运行的每个查询的速度,但在更复杂的查询中显然更明显。
我还尝试运行 pgtune,它给出了以下建议并调整了更多参数,但这并没有改变任何东西。它建议 1/4 RAM 大小的 shared_buffers 这似乎符合其他地方的建议(特别是在 PG wiki 上)。
default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80
Run Code Online (Sandbox Code Playgroud)
我尝试在更改设置后重新索引整个数据库(使用reindex database),但这也无济于事。我玩过 shared_buffers 和 work_mem。从非常保守的默认值 (128k / 1MB) 逐渐改变它们会逐渐降低性能。
我运行EXPLAIN (ANALYZE,BUFFERS)了一些查询,罪魁祸首似乎是 Hash Join 明显变慢了。我不清楚为什么。
举一些具体的例子,我有以下查询。它在默认配置下运行约 2100 毫秒,在增加缓冲区大小的配置上运行约 3300 毫秒:
select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';
Run Code Online (Sandbox Code Playgroud)
EXPLAIN (ANALYZE,BUFFERS) 对于上面的查询:
问题是为什么当我增加缓冲区大小时会观察到性能下降?机器肯定不会内存不足。如果 OS 中的共享内存是 (shmmax和shmall) 设置为非常大的值,则分配应该不是问题。我也没有在 Postgres 日志中收到任何错误。我在默认配置中运行 autovacuum,但我不认为这与它有任何关系。所有查询都在同一台机器上运行,相隔几秒钟,只是更改了配置(并重新启动了 PG)。
编辑:我刚刚发现了一个特别有趣的事实:当我在 2010 年年中的 iMac(OSX 10.7.5)上使用 Postgres 9.2.1 和 16GB RAM 执行相同的测试时,我没有遇到速度变慢的情况。具体来说:
set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms
Run Code Online (Sandbox Code Playgroud)
当我在服务器上使用完全相同的数据执行完全相同的查询(上面的查询)时,work_mem=1MB 得到 2100 毫秒,96 MB 得到 3200 毫秒。
Mac 有 SSD,所以它的速度是可以理解的,但它表现出我期望的行为。
Chr*_*ers 37
首先,请记住 work_mem 是 每个操作的,因此它会很快变得过多。一般来说,如果你没有遇到排序缓慢的问题,我会让 work_mem 一个人待着,直到你需要它。
查看您的查询计划,让我印象深刻的一件事是,查看两个计划的缓冲区命中非常不同,甚至顺序扫描也更慢。我怀疑这个问题与预读缓存有关,并且空间较小。这意味着您正在为重用索引和读取磁盘上的表而偏向内存。
我的理解是 PostgreSQL 会在从磁盘读取页面之前查找页面的缓存,因为它并不真正知道操作系统缓存是否会包含该页面。因为页面随后会保留在缓存中,并且因为该缓存比操作系统缓存慢,所以这会改变快速查询的类型与慢速查询的类型。实际上阅读计划,除了 work_mem 问题外,看起来您的所有查询信息都来自缓存,但这是哪个缓存的问题。
工作内存:我们可以为排序或相关连接操作分配多少内存。这是每个操作,而不是每个语句或每个后端,因此单个复杂查询可以使用很多倍的内存量。目前尚不清楚您是否达到了此限制,但值得注意并注意这一点。如果将此值增加太多,则会丢失可用于读取缓存和共享缓冲区的内存。
shared_buffers:分配给实际 PostgreSQL 页面队列的内存量。现在,理想情况下,有趣的数据库集将保留在此处缓存的内存中和读取缓冲区中。但是,这样做是为了确保所有后端中最常用的信息都被缓存而不是刷新到磁盘。在 Linux 上,此缓存比 OS 磁盘缓存慢得多,但它保证 OS 磁盘缓存不会并且对 PostgreSQL 透明。这很明显是你的问题所在。
所以发生的事情是,当我们有一个请求时,我们首先检查共享缓冲区,因为 PostgreSQL 对这个缓存有深入的了解,并寻找页面。如果它们不存在,我们要求操作系统从文件中打开它们,如果操作系统缓存了结果,它返回缓存的副本(这比共享缓冲区快,但 Pg 无法判断它是缓存还是打开)磁盘,而磁盘要慢得多,所以 PostgreSQL 通常不会抓住这个机会)。请记住,这也会影响随机与顺序页面访问。因此,使用较低的 shared_buffers 设置可以获得更好的性能。
我的直觉是,在具有更大 shared_buffer 设置的高并发环境中,您可能会获得更好或至少更一致的性能。还请记住,PostgreSQL 会获取并保存这些内存,因此如果您在系统上运行其他东西,则读取缓冲区将保存其他进程读取的文件。这是一个非常庞大而复杂的话题。较大的共享缓冲区设置可提供更好的性能保证,但在某些情况下可能会降低性能。
Erw*_*ter 14
除了增加会work_mem降低性能的看似矛盾的效果(@Chris可能有解释),您至少可以通过两种方式改进您的功能。
LEFT JOIN的JOIN。这可能会混淆查询计划程序并导致计划质量较差。SELECT count(*) AS ct
FROM contest c
JOIN contestparticipant cp ON cp.contestId = c.id
JOIN personinfo pi ON pi.id = cp.personinfoid
LEFT JOIN teammember tm ON tm.contestparticipantid = cp.id
LEFT JOIN staffmember sm ON sm.contestparticipantid = cp.id
LEFT JOIN person p ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR pi.lastname LIKE '%b%')
Run Code Online (Sandbox Code Playgroud)
pi.firstname并pi.lastname支持非锚定LIKE搜索。('%a%'也支持更短的模式,但索引不太可能对非选择性谓词有帮助。):CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx ON personinfo USING gin (lastname gin_trgm_ops);
Run Code Online (Sandbox Code Playgroud)
或者一个多列索引:
CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);
Run Code Online (Sandbox Code Playgroud)
应该使您的查询速度更快。您需要为此安装附加模块pg_trgm。这些相关问题下的详细信息:
另外,您是否尝试过work_mem 在本地设置- 仅针对当前交易?
SET LOCAL work_mem = '96MB';
Run Code Online (Sandbox Code Playgroud)
这可以防止并发事务也吃更多的 RAM,可能会互相饿死。