Postgres가 특정 인덱스를 사용하도록하려면 어떻게해야합니까?


112

Postgres가 순차 스캔을 고집 할 때 인덱스를 사용하도록 강제하는 방법은 무엇입니까?



1
+1이 기능을보고 싶습니다. 다른 답변이 말했듯이 단순히 seq 스캔을 비활성화하는 문제가 아닙니다. PG가 특정 인덱스 를 사용하도록 강제하는 기능이 필요합니다 . 이것은 실제 단어에서 통계가 완전히 잘못 될 수 있고 그 시점에서 신뢰할 수없는 / 부분적인 해결 방법을 사용해야하기 때문입니다. 간단한 경우에는 먼저 인덱스 및 기타 설정을 확인해야하지만 빅 데이터의 안정성과 고급 사용을 위해서는 이것이 필요합니다.
collimarco

MySQL과 Oracle 둘 다 가지고 있습니다. Postgres의 플래너가 왜 그렇게 신뢰할 수 없는지 확실하지 않습니다.
Kevin Parker

답변:


103

많은 데이터베이스에서 발견되는 일반적인 "인덱스 힌팅"기능에 대해 질문한다고 가정하면 PostgreSQL은 이러한 기능을 제공하지 않습니다. 이것은 PostgreSQL 팀이 내린 의식적인 결정이었습니다. 이유와 대신 수행 할 수있는 작업에 대한 개요는 여기 에서 찾을 수 있습니다 . 그 이유는 기본적으로 데이터가 변경됨에 따라 나중에 더 많은 문제를 일으키는 경향이있는 성능 해킹 인 반면 PostgreSQL의 최적화 프로그램은 통계를 기반으로 계획을 재평가 할 수 있기 때문입니다. 즉, 오늘날 좋은 쿼리 계획이 될 수있는 것은 아마도 항상 좋은 쿼리 계획이 아닐 것이며 인덱스 힌트는 항상 특정 쿼리 계획을 강제합니다.

테스트에 유용한 매우 무딘 망치로 enable_seqscanenable_indexscan매개 변수를 사용할 수 있습니다 . 보다:

이들은 지속적인 용도로 사용하기에는 적합하지 않습니다 . 쿼리 계획 선택에 문제가있는 경우 쿼리 성능 문제 추적에 대한 설명서를 참조해야 합니다 . enable_매개 변수를 설정 하고 떠나지 마십시오 .

색인을 사용하는 데 충분한 이유가 없다면 Postgres가 올바른 선택을 할 수 있습니다. 왜?

  • 작은 테이블의 경우 순차 스캔을 수행하는 것이 더 빠릅니다.
  • Postgres는 데이터 유형이 제대로 일치하지 않을 때 인덱스를 사용하지 않으므로 적절한 캐스트를 포함해야 할 수 있습니다.
  • 플래너 설정으로 인해 문제가 발생할 수 있습니다.

이 오래된 뉴스 그룹 게시물을 참조하십시오 .


4
동의합니다. postgres가 당신의 방식대로하도록 강요한다는 것은 일반적으로 당신이 잘못했다는 것을 의미합니다. 9/10 계획자는 당신이 생각할 수있는 모든 것을 이길 것입니다. 다른 하나는 당신이 잘못했기 때문입니다.
Kent Fredric

인덱스 보유의 실제 연산자 클래스를 확인하는 것이 좋은 생각이라고 생각합니다.
metdos

2
나는 오래된 질문을 되살리는 것을 싫어하지만 Postgres 문서, 토론 및 여기에서 자주 볼 수 있지만 작은 테이블에 적합한 것에 대한 일반화 된 개념이 있습니까? 5000 행 또는 50000 등입니까?
waffl

1
@waffl 벤치마킹을 고려해 보셨습니까? 인덱스가있는 간단한 테이블과 n 행의 임의 정크 로 채우기위한 함수를 만듭니다 . 그런 다음 n의 다른 값에 대한 쿼리 계획을 살펴 봅니다. 색인을 사용하기 시작하면 야구장 답이 있어야합니다. PostgreSQL이 인덱스 스캔이 너무 많은 행을 제거하지 않을 것이라고 (통계를 기반으로) 결정하는 경우에도 순차 스캔을 얻을 수 있습니다. 따라서 벤치마킹은 실제 성능 문제가있을 때 항상 좋은 생각입니다. 일화적인 추측으로 2 천 개는 보통 "작은"것입니다.
jpmc26 2014 년

11
Oracle, Teradata 및 MSSQL과 같은 플랫폼에서 30 년 이상의 경험을 쌓은 저는 PostgreSQL 10의 최적화 프로그램이 특히 똑똑하지 않다는 것을 알게되었습니다. 최신 통계가 있더라도 특별한 방향으로 강제하는 것보다 덜 효율적인 실행 계획을 생성합니다. 이러한 문제를 보완하기위한 구조적 힌트를 제공하면 PostgreSQL이 더 많은 시장 부문에서 성장할 수있는 솔루션이 될 것입니다. IMHO.
Guido Leenders 2010 년

75

아마도 사용하는 유일한 유효한 이유

set enable_seqscan=false

쿼리를 작성할 때 테이블에 많은 양의 데이터가있는 경우 쿼리 계획이 실제로 무엇인지 빠르게 확인하려는 경우입니다. 또는 데이터 세트가 너무 작기 때문에 쿼리가 인덱스를 사용하지 않는지 빠르게 확인해야하는 경우도 있습니다.


41
이 짧은 답변은 실제로 테스트 목적으로 좋은 힌트를 제공합니다
dwery

3
아무도 질문에 대답하지 않습니다!
Ivailo Bardarov 2014

@IvailoBardarov 다른 모든 제안이 여기에있는 이유는 PostgreSQL에이 기능이 없기 때문입니다. 이것은 일반적으로 사용되는 방식과 그로 인한 장기적인 문제를 기반으로 개발자가 내린 의식적인 결정이었습니다.
jpmc26 2014 년

테스트에 좋은 트릭 : 실행은 set enable_seqscan=false, 쿼리를 실행하고 신속 실행 set enable_seqscan=true(단지 개발, 생산에서이 작업을 수행하지 않는 분명히하고!) 적절한 행동에 PostgreSQL을을 반환
브라이언 Hellekin에게

2
@BrianHellekin 더 나은, SET SESSION enable_seqscan=false자신 만 영향을 미치는합니다
Izkata

20

때때로 PostgreSQL이 특정 조건에 대한 최상의 인덱스 선택을하지 못합니다. 예를 들어, 특정 날짜에 대해 수백 개의 행이있는 트랜잭션 테이블이 있고 테이블에 transaction_id, client_id, date 및 description의 네 가지 인덱스가 있다고 가정합니다. 다음 쿼리를 실행하려고합니다.

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL은 transaction_date_idx 대신 transaction_description_idx 인덱스를 사용하도록 선택할 수 있습니다. 이로 인해 쿼리에 1 초 미만이 아닌 몇 분이 소요될 수 있습니다. 이 경우 다음과 같이 조건을 푸징하여 날짜에 인덱스를 강제로 사용할 수 있습니다.

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id

3
좋은 생각. 그러나이 방법으로 현재 인덱스 사용을 비활성화하면 postgresql 쿼리 최적화 프로그램이 다음 적합한 인덱스로 대체됩니다. 따라서 옵티마이 저가을 선택한다는 보장은 없으며 your_wanted_indexpostgresql 엔진이 대신 시퀀스 / 기본 키 스캔을 수행하도록 할 수 있습니다. 결론-PostgreSql 서버에 대한 일부 인덱스 사용을 강제하는 100 % 신뢰할 수있는 방법은 없습니다.
Agnius Vasiliauskas

where조건이 없지만 테이블이 두 개이거나 조인되고 Postgres가 인덱스를 가져 오지 못하면 어떻게 될까요?
Luna Lovegood

@Surya는 WHERE와 JOIN에 모두 적용됩니다. ON 조건
Ziggy Crueltyfree Zeitgeister dec.

18

짧은 답변

이 문제는 일반적으로 인덱스 스캔의 예상 비용이 너무 높고 현실을 올바르게 반영하지 못할 때 발생합니다. random_page_cost이 문제를 해결 하려면 구성 매개 변수를 낮춰야 할 수 있습니다 . 로부터 포스트 그레스 문서 :

이 값을 줄이면 [...] 시스템이 인덱스 스캔을 선호하게됩니다. 값을 올리면 인덱스 스캔이 상대적으로 더 비싸 보이게됩니다.

더 낮은 값이 실제로 Postgres가 인덱스를 사용하도록 할 것인지 여부를 확인할 수 있습니다 (그러나 테스트 용으로 만 사용 ).

EXPLAIN <query>;              # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>;              # May use index scan now

SET random_page_cost = DEFAULT;다시 기본값으로 복원 할 수 있습니다 .

배경

인덱스 스캔에는 비 순차적 디스크 페이지 가져 오기가 필요합니다. Postgres는 random_page_cost순차 페치와 관련하여 이러한 비 순차 페치의 비용을 추정하는 데 사용 합니다. 기본값은입니다 4.0. 따라서 순차 가져 오기와 비교 하여 평균 비용 요소 4 를 가정합니다 (캐싱 효과 고려).

그러나 문제는이 기본값이 다음과 같은 중요한 실제 시나리오에 적합하지 않다는 것입니다.

1) 솔리드 스테이트 드라이브

문서에서 인정하는대로 :

순차에 비해 임의 읽기 비용이 낮은 스토리지 (예 : 솔리드 스테이트 드라이브)는에 대해 더 낮은 값으로 모델링하는 것이 더 좋습니다 random_page_cost.

PostgresConf 2018에서 발표 한이 슬라이드 의 마지막 요점 에 따르면는 솔리드 스테이트 드라이브 와 random_page_cost사이의 값으로 설정되어야 합니다.1.02.0

2) 캐시 된 데이터

필요한 인덱스 데이터가 이미 RAM에 캐시 된 경우 인덱스 스캔은 항상 순차 스캔보다 훨씬 빠릅니다. 문서는 다음과 같이 말합니다.

따라서 데이터가 완전히 캐시에있을 가능성이있는 경우 [...] 감소 random_page_cost가 적절할 수 있습니다.

문제는 물론 관련 데이터가 이미 캐시되었는지 여부를 쉽게 알 수 없다는 것입니다. 그러나 특정 인덱스가 자주 쿼리되고 시스템에 충분한 RAM이 있으면 데이터가 캐시 될 가능성이 높 random_page_cost으므로 더 낮은 값으로 설정해야합니다. 다양한 값을 실험하고 무엇이 자신에게 적합한 지 확인해야합니다.

명시 적 데이터 캐싱을 위해 pg_prewarm 확장 을 사용할 수도 있습니다 .



2
Ubuntu의 Pg 10.1에서 대형 (~ 600M 행 테이블)에서 인덱스 스캔이 작동하도록하려면 random_page_cost = 0.1을 설정해야했습니다. 비틀기없이, seq 스캔 (병렬 임에도 불구하고)은 12 분이 걸렸습니다 (분석 테이블이 수행되었습니다!). 드라이브는 SSD입니다. 조정 후 exec 시간은 1 초가되었습니다.
Anatoly Alekseev

당신은 내 하루를 구했습니다. 양쪽 끝에서 분석을 실행 한 후에도 동일한 데이터베이스에서 정확히 동일한 쿼리가 한 컴퓨터에서는 30 초, 다른 컴퓨터에서는 1 초 미만이 걸리는지 파악하려고 미쳐 가고있었습니다. ALTER SYSTEM SET random_page_cost = x '는 새 기본값을 전역 적으로 설정합니다.
Julien

10

그 자체에 대한 질문은 매우 잘못되었습니다. 강제 (예 : enable_seqscan = off)는 매우 나쁜 생각입니다. 더 빠른지 확인하는 것이 유용 할 수 있지만 프로덕션 코드는 이러한 트릭을 사용해서는 안됩니다.

대신 쿼리 분석을 설명하고 읽고 PostgreSQL이 잘못된 계획을 선택한 이유를 알아보십시오.

웹에는 Explain 분석 출력을 읽는 데 도움이되는 도구가 있습니다. 그중 하나는 Explain.depesz.com 입니다.

또 다른 옵션은 freenode irc 네트워크의 #postgresql 채널에 가입 하여 도움을 줄 수있는 사람들과 대화하는 것입니다. 쿼리를 최적화하는 것은 "질문을하고 답을 얻으십시오. 확인해야 할 사항이 많고 배워야 할 사항이 많은 대화와 비슷합니다.


2

OFFSET 0하위 쿼리에 추가하는 seqscan을 선호하도록 postgres를 푸시하는 트릭이 있습니다.

이는 필요한 모든 것이 첫 번째 / 마지막 요소 n 개뿐 일 때 크고 거대한 테이블을 연결하는 요청을 최적화하는 데 유용합니다.

100k (또는 그 이상) 항목이있는 여러 테이블이 포함 된 처음 / 마지막 20 개 요소를 찾고 있다고 가정 해 보겠습니다. 검색하려는 항목이 처음 100 개 또는 1000 개에있을 때 모든 데이터에 대한 모든 쿼리를 구성 / 연결하지 않습니다. 항목. 예를 들어이 시나리오에서는 순차 스캔을 수행하는 것이 10 배 이상 빠릅니다.

Postgres가 하위 쿼리를 인라인하지 않도록 하려면 어떻게해야합니까?를 참조하십시오 .


좋은 속임수. 좋은 옵티마이 저는 물론 오프셋 0을 최적화해야하지만 :-)
Guido Leenders
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.