PostgreSQL Practice & Tips - 3 - 실행 계획
모든 조회 요청 에 대해 PostgreSQL 은 그 위 에 실행 계획 (조회 계획) 을 설정 하고 조회 문구 구조 와 데이터 구 조 를 통 해 조회 계획 을 정확하게 설정 할 수 있 는 설정 은 시스템 성능 을 현저히 향상 시 킬 수 있 습 니 다. 우리 에 게 는 조회 계획 을 통 해 느 린 조회 의 원인 을 찾 는 경우 가 많 습 니 다. 가장 중요 한 성능 도구 라 고 할 수 있 습 니 다.물론 계획 을 집행 하 는 분석 과 최적화 가 어렵 기 때문에 본 편 은 기본 적 인 점 을 덮어 쓰 려 고 한다.
일반적으로 사용
EXPLAIN
은 조회 계획 을 표시 할 수 있 습 니 다. 물론 일부 option 을 사용 하여 이 를 바 꿀 수 있 습 니 다. 예 를 들 어 흔히 볼 수 있 는 ANALYZE
COSTS
등 입 니 다.가장 많이 사용 되 는 것 은 ANALYZE
일 수 있 습 니 다. 실제 실 행 된 SQL 을 통 해 실제 실행 계획 을 얻 기 때문에 구체 적 인 시간 소비 와 줄 수 를 볼 수 있 습 니 다. 만약 에 INSERT 나 DELETE 가 데 이 터 를 얻 으 면 데이터 베 이 스 를 수정 할 것 입 니 다.따라서 BEGIN
를 사용 하여 EXPLAIN ANALYZE
을 하나의 업무 에 포함 시 키 고 실행 이 끝 난 후에 스크롤 백 할 수 있 습 니 다.만약 당신 이 사용 하지 않 는 다 면 ANALYZE
PostgreSQL 은 무 작위 샘플, 기 존의 성능 데이터, 또는 추정 등 수단 으로 cost 를 추측 하기 때문에 정확 하지 않 지만 성능 을 충분히 분석 할 수 있 습 니 다. 특히 느 린 조 회 는 오래 기다 릴 수 있 고 사용 ANALYZE
은 매우 비효 율 적 입 니 다.사실 공식 문 서 는 가장 좋 은 해석 입 니 다. 다른 options 나 깊이 공부 하고 싶 은 것 은 참고 하 시기 바 랍 니 다.
출력 결과 설명
우리 먼저 실제 해 보 자
EXPLAIN
.CREATE SEQUENCE user_id_seq;
CREATE TABLE users (
id BIGINT NOT NULL DEFAULT nextval('user_id_seq'),
type VARCHAR(10) NOT NULL,
name VARCHAR(128) NOT NULL,
address TEXT,
married BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (id)
);
#
INSERT INTO users (type, name, address)
SELECT 'testing', left(md5(i::text), 10), left(md5(random()::text), 50)
FROM generate_series(1, 100000) s(i);
그래서 우리 가 진행 한 결 과 는 다음 과 같다.
EXPLAIN select * from users;
QUERY PLAN
--------------------------------------------------------------
Seq Scan on users (cost=0.00..1600.80 rows=26680 width=369)
(1 row)
EXPLAIN
우리 가 흔히 말 하 는 전체 표 스 캔, 처음부터 끝까지 의 순서 스 캔 을 나타 낸다.Seq Scan
는 비용 이 든다 고 밝 혔 고 cost=0.00..1600.80
는 첫 줄 을 얻 는 데 얼마의 비용 이 필요 하 다 고 밝 혔 으 며 0.00
는 모두 되 돌아 가 는 데 얼마의 비용 이 필요 하 다 고 밝 혔 다.1600.80
몇 줄 로 돌아 가 는 지 표시 rows
줄 당 평균 몇 바이트 (rows 와 width 로 데이터 크기 를 추산 할 수 있 음) width
은 많은 성능 데이터 에 근거 하여 추측 할 것 이다. 왜냐하면 이 표 가 막 세 워 졌 기 때문에 데이터 가 충분 하지 않 아서 결과 의 편차 가 매우 크다.너 는 EXPLAIN
문 구 를 만 든 후에 방금 SELECT COUNT(*)
문 구 를 다시 시도 해 보면 결과 가 크게 달라 질 것 이다.EXPLAIN
우리 의 실제 실행 시간 과 연결 되 지 않 습 니 다. 다만 PostgreSQL 은 실행 원 가 를 설명 하 는 단 위 를 사용 합 니 다. 이 물건 은 그 어떠한 시간 단위 와 도 환산 할 수 없습니다. PSQL 은 이렇게 정의 합 니 다. 예 를 들 어 cost
의 대 가 는 1.0 입 니 다. 한 줄 의 데 이 터 를 처리 하 는 대 가 는 0.01 입 니 다. 물론 이런 상수 들 도 스스로 수정 할 수 있 습 니 다.다른 표현 은 공식 문 서 를 참고 할 수 있 습 니 다. 일반적인 상황 에서 cost 가 무엇 인지 특별히 알 필요 가 없습니다. 성능 분석 만 할 수 있 으 면 충분 합 니 다.내 려 와 서
seq_page_cost
사용 후의 효 과 를 살 펴 보 자.EXPLAIN ANALYZE select * from users;
QUERY PLAN
-----------------------------------------------------------------
Seq Scan on users (cost=0.00..2334.00 rows=100000 width=77) (actual time=0.013..631.094 rows=100000
loops=1)
Planning time: 0.068 ms
Execution time: 1249.210 ms
(3 rows)
결과 에 구체 적 인 조회 시간 과 데 이 터 를 되 돌려 주 는 총 시간 이 있 습 니 다.
ANALYZE
를 사용 하여 캐 시 명중 상황 을 볼 수 있 습 니 다.EXPLAIN (ANALYZE true, BUFFERS true) select * from users;
QUERY PLAN
-----------------------------------------------------------------
Seq Scan on users (cost=0.00..2334.00 rows=100000 width=77) (actual time=0.020..634.054 rows=100000
loops=1)
Buffers: shared hit=1334
Planning time: 0.223 ms
Execution time: 1254.985 ms
(4 rows)
1334 개의 block 을 명중 시 키 는 것 을 볼 수 있 습 니 다. 특히 데이터 시트 의 prewarm 을 만 든 후 캐 시 명중 도 필요 합 니 다. 이 를 통 해
buffers
의 효 과 를 평가 할 수 있 습 니 다.스캐닝
PostgreSQL 의 스 캔 은 세 가지 가 있 습 니 다. 순서 스 캔
pg_prewarm
, 색인 스 캔 seq scan
과 비트 맵 스 캔 index scan
이 있 습 니 다. 순서 스 캔 은 전체 표 스 캔 이 라 고도 부 릅 니 다. 표 의 모든 데이터 블록 을 처음부터 끝까지 스 캔 한 다음 에 조건 에 맞 는 데 이 터 를 얻 는 것 입 니 다. 색인 스 캔 은 색인 에서 필요 한 데이터 의 위 치 를 찾 은 다음 에 데 이 터 를 추출 하 는 과정 입 니 다.다음은 두 가지 예 가 있다.EXPLAIN select * from users;
QUERY PLAN
--------------------------------------------------------------
Seq Scan on users (cost=0.00..2334.00 rows=100000 width=77)
(1 row)
EXPLAIN select * from users where id = 9999;
QUERY PLAN
-------------------------------------------------------------------------
Index Scan using users_pkey on users (cost=0.29..8.31 rows=1 width=77)
Index Cond: (id = 9999)
(2 rows)
첫 번 째 조 회 는 표 의 100, 000 개의 데 이 터 를 검색 할 것 입 니 다. where 자구 가 없 기 때문에 이 조 회 는 seq scan 만 있 고 두 번 째 조 회 는 색인 에서 직접 검색 할 것 입 니 다 (두 번 째 편 에 있 는 B - Tree Index 를 기억 하 십 니까? 이 를 검색 하 는 과정 은 Index Scan 입 니 다).
한편, 비트 맵 스 캔
bitmap scan
도 색인 스 캔 방식 이다. 원 리 는 색인 스 캔 을 하고 조건 을 만족 시 키 는 줄 의 지침 bitmap scan
을 즉시 꺼 내 메모리 에 저 장 된 비트 맵 에 저장 하 는 것 이다. 스 캔 이 끝 난 후에 비트 맵 의 tuple-pointer
을 실제 데이터 블록 에 있 는 데 이 터 를 읽 는 것 이다.이러한 조회 방식 은 비 등가 조회 의 경우 에 자주 사용 되 며, 두 개의 색인 을 걸 으 면 두 개의 색인 비트 맵 tuple-pointer
과 and
의 계산 을 통 해 새로운 비트 맵 으로 합 쳐 그 위치 에 따라 실제 데 이 터 를 추출 할 수 있 으 며, 이 과정 에서 각 데이터 블록 은 스 캔 에서 한 번 만 읽 혔 다.예 를 들 어 우 리 는 특정한 색인 에 대해 조건 조 회 를 한다.# scan, bitmap scan
SET enable_indexscan = off;
SET enable_seqscan = off;
#
CREATE INDEX users_name ON users (name);
#
EXPLAIN SELECT * FROM users WHERE name IN ('a', 'b', 'c');
QUERY PLAN
--------------------------------------------------------------------------
Bitmap Heap Scan on users (cost=13.28..24.89 rows=3 width=77)
Recheck Cond: ((name)::text = ANY ('{a,b,c}'::text[]))
-> Bitmap Index Scan on users_name (cost=0.00..13.28 rows=3 width=0)
Index Cond: ((name)::text = ANY ('{a,b,c}'::text[]))
(4 rows)
Recheck 의 이 유 는 MVCC 때문에 데 이 터 를 읽 은 후에 조건 을 다시 확인 해 야 하기 때 문 입 니 다.
EXPLAIN SELECT * FROM users WHERE name IN ('a', 'b', 'c') OR name IN ('p', 'b', 'k');
QUERY PLAN
--------------------------------------------------------------------------
Bitmap Heap Scan on users (cost=26.56..49.45 rows=6 width=77)
Recheck Cond: (((name)::text = ANY ('{a,b,c}'::text[])) OR ((name)::text = ANY ('{p,b,k}'::text[])
))
-> BitmapOr (cost=26.56..26.56 rows=6 width=0)
-> Bitmap Index Scan on users_name (cost=0.00..13.28 rows=3 width=0)
Index Cond: ((name)::text = ANY ('{a,b,c}'::text[]))
-> Bitmap Index Scan on users_name (cost=0.00..13.28 rows=3 width=0)
Index Cond: ((name)::text = ANY ('{p,b,k}'::text[]))
(7 rows)
이것 은 bitmap scanOR 의 예 입 니 다. 저 희 는 OR 를 사용 하여 두 개의 bitmap 를 합 친 다음 에 마지막 으로 데 이 터 를 읽 습 니 다.
조건, 정렬 등
조건, 정렬, limit 등 조작 에 대해 서도 해당 하 는 조회 계획 이 있 습 니 다. scan 보다 쉽게 이해 할 수 있 습 니 다. 예 를 들 어 다음 조회 계획:
EXPLAIN SELECT * FROM users WHERE id > 100 ORDER BY created_at DESC LIMIT 10;
QUERY PLAN
-------------------------------------------------------------------------
Limit (cost=4742.89..4742.91 rows=10 width=77)
-> Sort (cost=4742.89..4992.65 rows=99904 width=77)
Sort Key: created_at
-> Seq Scan on users (cost=0.00..2584.00 rows=99904 width=77)
Filter: (id > 100)
(5 rows)
or
limit
sort
는 모두 계획 에서 정렬 이 매우 비 싼 조작 (cost = 4742.8.9. 4992.65) 임 을 알 수 있 고 합 리 적 인 사용 순 서 는 성능 을 현저히 향상 시 킬 수 있다.Join
PostgreSQL 은 3 가지 JOIN 방식
filter
nestloop join
hash join
이 있 으 며, PostgreSQL 은 데이터 양의 크기 와 성능 의 통계 데이터 등에 따라 적합 한 방식 을 선택한다.merge join
내장 순환 이 라 고도 하 는데 말 그대로 내장 순환 방식 을 사용 하여 먼저 외모 에서 데 이 터 를 얻 은 다음 에 내 표를 찾 아 일치 하 는 것 이다.그래서 이런 방식 은 데이터 양 이 비교적 많은 상황, 예 를 들 어 줄 수가 수만 에 달 하 는 상황 에 적합 하지 않다.분명 한 복잡 도 는 O (M * N) 이 고 M 과 N 은 join 이 필요 한 데이터 양 이지 만 nestloop join
이 효율 적 이지 않 아 보이 더 라 도 이런 방식 은 모든 다 중 표 연결 검사 상황 을 계산 할 수 있 는 가장 기본 적 인 기능 이 라 고 볼 수 있다.그러나 inner 표 에 join 에 사용 되 는 필드 에 색인 이 존재 한다 면
nestloop join
의 효율 이 약간 좋 을 것 입 니 다. 우 리 는 B - Tree 의 조회 복잡 도가 log (N) 라 는 것 을 알 기 때문에 index scan nestloop join
에서 O(M*log(N))
보다 좋 습 니 다 nestloop join
.다음은 nestloop JOIN 의 인 스 턴 스 이 며, Index Scan 이 있 습 니 다.
O(M*N)
전체 표를 스 캔 한 후, 모든 데 이 터 를 JOIN 하여 users 를 조회 하 는 것 을 볼 수 있 습 니 다.2 표 에 조건 을 만족 시 키 는 데이터 가 있 는 지, 즉 사용 users
.# nestloop
SET enable_nestloop = ON;
SET enable_hashjoin = OFF;
SET enable_mergejoin = OFF;
EXPLAIN SELECT * FROM users JOIN users_2 ON users.id = users_2.id;
QUERY PLAN
-----------------------------------------------------------------------------------
Nested Loop (cost=0.42..79748.00 rows=100000 width=154)
-> Seq Scan on users (cost=0.00..2334.00 rows=100000 width=77)
-> Index Scan using users_2_pkey on users_2 (cost=0.42..0.76 rows=1 width=77)
Index Cond: (id = users.id)
Index Cond
는 hash join
보다 약간 앞서 있다. 일반적으로 PostgreSQL 조회 최적화 기 는 두 개의 표 중 작은 표를 사용 하여 메모리 에 넣 고 하나의 산 목록 을 만 든 다음 에 큰 표를 스 캔 하고 메모리 의 산 목록 을 조회 한 다음 에 데 이 터 를 찾아낸다.이 경우 조인 할 때 작은 시 계 를 메모리 에 완전히 넣 는 것 이 좋 습 니 다. 그러나 작은 데이터 시트 가 메모리 에 모두 넣 지 못 하면 PostgreSQL 은 파 티 션 을 나 누고 메모리 에 넣 을 수 없 는 부분 을 임시 세그먼트 에 기록 합 니 다. 그러면 많은 I / O 비용 이 필요 합 니 다.# hash join
SET enable_nestloop = OFF;
SET enable_hashjoin = ON;
SET enable_mergejoin = OFF;
EXPLAIN SELECT * FROM users JOIN users_2 ON users.id = users_2.id;
QUERY PLAN
--------------------------------------------------------------------------
Hash Join (cost=4854.00..36487.00 rows=100000 width=154)
Hash Cond: (users_2.id = users.id)
-> Seq Scan on users_2 (cost=0.00..11667.00 rows=500000 width=77)
-> Hash (cost=2334.00..2334.00 rows=100000 width=77)
-> Seq Scan on users (cost=0.00..2334.00 rows=100000 width=77)
이 조회 계획 은 매우 명확 합 니 다. 먼저 표 users 에 게 seq scan 을 한 다음 에 그 결 과 를 메모리 에 넣 은 hash 입 니 다. 표 users 는 100, 000 개의 데이터 만 있 고 작은 표 이 며 users2 표 에서 seq scan 을 한 다음 hash 에 hash join 을 들 어가 기 때문에 그 결과 의 총 cost 는 nestloop 보다 작 습 니 다. 즉, 36487.00 < 79748.00 이지 만 시작 시간 (첫 번 째 데이터 가 돌아 오 는 시간) 은 nestloop 즉 4854.00 > 0.42 보다 큽 니 다. hash table 을 만들어 야 하기 때 문 입 니 다.
일반적으로
nestloop
의 효 과 는 hash join
보다 좋 지만, JOIN 의 필드 에 색인 이 있 거나 정렬 (예 를 들 어 JOIN 의 결과 표) 이 필요 하 다 면 오랫동안 hash 를 만 들 필요 가 없다. 이때 merge join
의 성능 은 merge join
보다 좋 을 것 이다.그래서 위의 예 에서 모든 JOIN 옵션 을 열 면 PostgreSQL 은 기본적으로 더 효율 적 인 hash join
을 사용 합 니 다.# JOIN
SET enable_nestloop = ON;
SET enable_hashjoin = ON;
SET enable_mergejoin = ON;
EXPLAIN SELECT * FROM users JOIN users_2 ON users.id = users_2.id;
QUERY PLAN
--------------------------------------------------------------------------------------------
Merge Join (cost=1.60..9351.82 rows=100000 width=154)
Merge Cond: (users.id = users_2.id)
-> Index Scan using users_pkey on users (cost=0.29..3941.29 rows=100000 width=77)
-> Index Scan using users_2_pkey on users_2 (cost=0.42..19666.42 rows=500000 width=77)
색인 은 이미 정렬 되 어 있 기 때문에 두 개의 Index Scan 은 색인 을 직접 스 캔 하여 예상 되 는 cost 가 세 가지 상황 중에서 가장 좋 은 것 을 볼 수 있 습 니 다.
성능 최적화
이전 예 에서 PostgreSQL 이 서로 다른 조회 계획 을 강제 적 으로 실행 하도록 제어 하 는 유사 한
merge join
인 자 를 사 용 했 습 니 다. 그렇지 않 으 면 PostgreSQL 의 최적화 기 는 가장 좋 은 상황 을 선택 하여 실 행 했 기 때 문 입 니 다. 예 를 들 어 JOIN 의 예 에서 merge join 을 사용 하기 때 문 입 니 다.일반적인 상황 에서 PostgreSQL 은 조회 계획 을 잘못 실행 하지 않 습 니 다. 가끔 은 데이터베이스 서 비 스 를 시작 하거나 새 표를 사용 할 때 발생 합 니 다. 그 때 는 최적화 기 가 결정 하 는 데 도움 이 되 는 통계 정보 가 충분 하지 않 았 기 때 문 입 니 다.따라서 프로젝트 에서 특별한 상황 이 없 으 면 이러한
enable_mergejoin
인 자 를 수 동 으로 사용 하여 계획 을 강제 집행 할 필요 가 없다.또한 PostgreSQL 은
enable_mergejoin
명령 을 사용 하여 통계 표 의 정 보 를 수집 할 수 있 으 며, 이러한 통계 정 보 는 최적화 기 가 적절 한 실행 계획 을 선택 하 는 데 도움 이 될 것 이다.그러나 일반적으로 PostgreSQL 은 기본적으로 하나의 analyze
프로 세 스 를 실행 하여 vacuum 작업 을 하고 모든 표를 자동 으로 분석 합 니 다. 가끔 우 리 는 이 프로 세 스 를 닫 고 주기 적 이 고 수 동 으로 vacuum 과 analysis (예 를 들 어 한밤중) 를 진행 합 니 다.그러나 특별한 상황 이 없 으 면 수 동 분석 을 할 때 범 하기 쉬 운 인위적인 실 수 를 피 하 는 경향 이 있 습 니 다.더 공부 하고 싶 으 시 면 공식 문 서 를 참고 하 셔 도 됩 니 다. 다음 업데이트 에서 VACUUM 에 대해 구체 적 으로 말씀 드 리 겠 습 니 다.우리 가 느 린 조회 에 직면 한 첫 번 째 일 은 실행 계획 을 분석 하 는 것 이다. 예 를 들 어 자주 하 는 방법 은 Seq Scan 을 Index Scan 으로 바 꾸 거나 Nestloop 을 취소 하 는 것 이다.일반적인 상황 에서 검색 어 를 바 꿀 수 없다 면 실행 계획 을 통 해 구체 적 인 실행 경로 와 원 가 를 얻 은 다음 에 맞 춤 형 최적화 를 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.