Postgres 9.2에서 work_mem 및 shared_buffers를 늘리면 쿼리 속도가 크게 느려집니다.


39

16GB RAM이 장착 된 8 코어 시스템 인 RHEL 6.3에서 실행되는 PostgreSQL 9.2 인스턴스가 있습니다. 서버는이 데이터베이스 전용입니다. 기본 postgresql.conf가 메모리 설정과 관련하여 상당히 보수적이므로 Postgres가 더 많은 메모리를 사용하도록하는 것이 좋습니다. 놀랍게도 wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server에 대한 조언을 따르면 실제로 실행하는 모든 쿼리가 상당히 느려졌지만 더 복잡한 쿼리에서는 분명히 눈에.니다.

또한 pgtune을 실행하여 더 많은 매개 변수를 조정하여 다음 권장 사항을 제공했지만 아무것도 변경하지 않았습니다. 다른 곳 (특히 PG wiki)에 대한 조언과 일치하는 1/4 크기의 RAM 크기의 shared_buffers를 제안합니다.

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

설정을 변경 한 후 (을 사용하여 reindex database) 전체 데이터베이스를 다시 색인화하려고 시도했지만 도움이되지 않았습니다. 나는 shared_buffers와 work_mem을 가지고 놀았습니다. 매우 보수적 인 기본값 (128k / 1MB)에서 점차 변경하면 성능이 점차 저하됩니다.

EXPLAIN (ANALYZE,BUFFERS)몇 가지 쿼리를 실행 했으며 해시 조인이 상당히 느려진 것으로 보입니다. 이유가 명확하지 않습니다.

구체적인 예를 들기 위해 다음과 같은 쿼리가 있습니다. 기본 구성에서는 ~ 2100ms, 버퍼 크기가 증가한 구성에서는 ~ 3300ms로 실행됩니다.

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%';

EXPLAIN (ANALYZE,BUFFERS) 위의 쿼리의 경우 :

문제는 버퍼 크기를 늘릴 때 성능이 저하되는 이유무엇입니까? 머신에 메모리가 부족하지 않습니다. OS의 공유 메모리가 ( shmmaxshmall)가 매우 큰 값으로 설정된 경우에는 문제가되지 않습니다. 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

서버에서 정확히 동일한 데이터를 사용하여 정확히 동일한 쿼리 (위의 쿼리)를 수행하면 work_mem = 1MB에서 2100ms, 96MB에서 3200ms가 표시됩니다.

Mac에는 SSD가 있으므로 이해하기가 더 빠르지 만 예상되는 동작을 보여줍니다.

pgsql-performance에 대한 후속 토론 도 참조하십시오 .


1
느린 경우에는 모든 단계가 구성 적으로 느려집니다. 다른 설정이 동일하게 유지 되었습니까?
dezso

1
아마도 이것이 일반적인 포럼보다는 좀 더 전문화 된 포럼에서 이것을 물어 보는 것이 좋습니다. 이 경우 pgsql-general 메일 링리스트 archives.postgresql.org/pgsql-general
Colin 't Hart

1
아, 답을 찾으면 다시보고하고 자신의 질문에 대답하십시오! (이것은 허용되고 심지어 권장됩니다).
Colin 't Hart

1
나는 이와 관련하여 Postgres가 Oracle과 얼마나 유사한 지 궁금합니다. Jonathan Lewis (Oracle guru)의 강의를 기억합니다. 구체적인 내용은 잊어 버렸지 만 Oracle이 부분 정렬을 수행 한 다음 임시 스토리지에 쓴 다음 나중에 결합하는 것과 관련이 있습니다. 어떻게 든 더 많은 메모리 가이 프로세스를 느리게 만들었습니다.
Colin 't Hart

2
질문은 이제 pgsql-performance에 게시됩니다 : archives.postgresql.org/pgsql-performance/2012-11/msg00004.php
Petr Praus

답변:


28

우선 work_mem은 작업 당 이므로 너무 빨리 초과 될 수 있습니다. 일반적으로 정렬 속도가 느려지는 경우 문제가 발생하지 않으면 필요할 때까지 work_mem을 그대로 둡니다.

쿼리 계획을 살펴보면 버퍼 히트가 두 계획을 보면 매우 다르며 순차적 스캔조차도 느리다는 것입니다. 문제는 미리 읽기 캐싱과 관련이 있고 공간이 적을 것으로 생각됩니다. 이것이 의미하는 것은 인덱스 재사용을 위해 메모리를 바이어스하고 디스크에서 테이블을 읽는 것에 대한 것입니다.


내 이해는 PostgreSQL이 OS 캐시에 해당 페이지가 포함 될지 여부를 실제로 알지 못하기 때문에 디스크에서 페이지를 읽기 전에 페이지의 캐시를 찾습니다. 그러면 페이지가 캐시에 유지되고 캐시가 OS 캐시보다 느리기 때문에 빠른 쿼리와 느린 쿼리의 종류가 변경됩니다. 실제로 work_mem 문제를 제외하고 계획을 읽으면 모든 쿼리 정보가 캐시에서 나온 것처럼 보이지만 캐시의 문제입니다.

work_mem : 정렬 또는 관련 조인 작업에 할당 할 수있는 메모리 양. 이는 문 또는 백엔드가 아닌 작업 별이므로 단일 복합 쿼리에서이 메모리 양을 여러 번 사용할 수 있습니다. 이 한계에 도달했는지는 확실하지 않지만 알아 두어야 할 가치가 있습니다. 이것을 너무 늘리면 읽기 캐시 및 공유 버퍼에 사용 가능한 메모리가 손실됩니다.

shared_buffers : 실제 PostgreSQL 페이지 큐에 할당 할 메모리 양. 이제 이상적으로 흥미로운 데이터베이스 세트는 여기 캐시 된 메모리와 읽기 버퍼에 보관됩니다. 그러나 이것이하는 일은 모든 백엔드에서 가장 자주 사용되는 정보가 캐시되고 디스크로 플러시되지 않도록하는 것입니다. Linux에서이 캐시는 OS 디스크 캐시보다 상당히 느리지 만 OS 디스크 캐시가 PostgreSQL에 영향을 미치지 않으며 투명하다는 것을 보장합니다. 이것은 당신의 문제가있는 곳입니다.

따라서 요청이있을 때 PostgreSQL이이 캐시에 대해 깊이 알고 있기 때문에 공유 버퍼를 먼저 확인하고 페이지를 찾습니다. 파일이 없으면 OS에서 파일에서 파일을 열도록 요청하고 OS가 결과를 캐시하면 캐시 된 사본을 반환합니다 (공유 버퍼보다 ​​빠르지 만 Pg는 캐시 여부를 알 수 없습니다) 디스크와 디스크가 훨씬 느리므로 PostgreSQL은 일반적으로 그 기회를 얻지 못합니다). 이는 임의 및 순차적 페이지 액세스에도 영향을 미칩니다. 따라서 낮은 shared_buffers 설정으로 더 나은 성능을 얻을 수 있습니다.

내 생각은 더 큰 shared_buffer 설정을 사용하는 동시성 환경에서 더 나은 또는 최소한 더 일관된 성능을 얻을 수 있다는 것입니다. 또한 PostgreSQL은이 메모리를 잡고 보유하므로 시스템에서 다른 작업을 실행중인 경우 읽기 버퍼는 다른 프로세스에서 읽은 파일을 보유합니다. 매우 크고 복잡한 주제입니다. 큰 공유 버퍼 설정이 더 나은 제공 을 보장 성능을하지만, 어떤 경우에는 더 적은 성능을 제공 할 수 있습니다.


10

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%')
  • 실제 검색 패턴이 더 선택적인 것으로 가정하면 트라이 그램 색인을 사용 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);

또는 하나의 다중 열 인덱스 :

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

쿼리를 좀 더 빠르게 만들어야합니다. 이를 위해 추가 모듈 pg_trgm 을 설치해야합니다 . 이러한 관련 질문에 대한 세부 사항 :


또한 work_mem 현재 트랜잭션에 대해서만 로컬 설정을 시도 했습니까?

SET LOCAL work_mem = '96MB';

이렇게하면 동시 트랜잭션이 더 많은 RAM을 사용하지 못하게되어 서로 굶주릴 수 있습니다.


3
Erwin의 로컬 work_mem 제안을 따르고 싶습니다. work_mem은 더 빠른 쿼리 종류를 변경하므로 일부 쿼리에 대해서는 변경해야 할 수도 있습니다. 즉, work_mem 수준이 낮 으면 복잡한 방식 (예 : 많은 조인)으로 적은 수의 레코드를 정렬 / 결합하는 쿼리에 가장 적합한 반면, work_mem 수준이 높으면 몇 가지 정렬은 있지만 한 번에 많은 수의 행을 정렬하거나 조인하는 쿼리에는 최고입니다 .
Chris Travers

그 동안 쿼리를 개선했지만 (질문은 지난해 10 월부터) 감사합니다 :)이 질문은 특정 쿼리보다 예기치 않은 영향에 관한 것입니다. 쿼리는 주로 효과를 보여줍니다. 색인에 대한 팁을 주셔서 감사합니다, 나는 그것을 시도 할 것입니다!
Petr Praus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.