순차 스캔 삽입
31575 단어 databaseheuristicspgpostgres
EXPLAIN
출력에 "순차적 스캔"이 표시됩니까? 이 게시물은 로컬psql
에서 실행할 수 있는 SQL을 보여 주는 빠른 둘러보기를 목표로 하는 한 가지 이유에 대해 설명합니다.이 블로그 게시물의 좋은 후보자라면
ANALYZE
가 PostgreSQL의 중요한 기능이라는 것을 알 수 있지만 이유를 말할 수 없거나 중요한 경우에 대해 확신이 서지 않을 수 있습니다.이에 대해 단계적으로 살펴보겠습니다.
pg_stats
참조1. 설정
이것은 테이블 스키마를 변경하는 유일한 섹션입니다. 이것은 중요합니다. 플래너는 스키마 변경이 아니라 우리의 하루를 망칠 것입니다.
create table dogs_seen (id serial, num_seen bigint);
create index dogs_idx on dogs_seen (num_seen);
-- inserts 1mil rows with num_seen values
-- that are weghted towards low, positive integers
insert into dogs_seen (num_seen) select (1/random())
from generate_series(1,1000000);
-- update pg_stats to reflect our table state
analyze dogs_seen;
2. 초기 테스트
우리가 집중할 테스트 쿼리는 다음과 같습니다.
select count(*) from dogs_seen where num_seen = 1;
-- this should be around 300k-400k
해당 쿼리는 설정 중에 생성한 인덱스를 사용할 것으로 예상하며 다음과 같아야 합니다.
explain analyze select count(*) from dogs_seen where num_seen = 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=6320.28..6320.29 rows=1 width=8) (actual time=25.344..26.535 rows=1 loops=1)
-> Gather (cost=6320.07..6320.28 rows=2 width=8) (actual time=25.296..26.527 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=5320.07..5320.08 rows=1 width=8) (actual time=19.418..19.418 rows=1 loops=3)
-> Parallel Index Only Scan using dogs_idx on dogs_seen (cost=0.42..4973.54 rows=138611 width=0) (actual time=0.062..11.770 rows=111153 loops=3)
Index Cond: (num_seen = 1)
Heap Fetches: 0
Planning Time: 0.292 ms
Execution Time: 26.595 ms
(10 rows)
예,
Index
스캐닝이 표시됩니다. 여기서 주목해야 할 한 가지는 이 쿼리의 성능이 상대적으로 매우 낮다는 것입니다. 설정하는 동안 1에 가중치를 둔 값num_seen
을 생성했다고 언급했으므로 조건과 일치하는 행이 이보다 훨씬 더 많습니다.explain analyze select count(*) from dogs_seen
where num_seen = 6;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=650.42..650.43 rows=1 width=8) (actual time=5.709..5.710 rows=1 loops=1)
-> Index Only Scan using dogs_idx on dogs_seen (cost=0.42..580.67 rows=27900 width=0) (actual time=0.040..3.502 rows=28235 loops=1)
Index Cond: (num_seen = 6)
Heap Fetches: 0
Planning Time: 0.233 ms
Execution Time: 5.754 ms
(6 rows)
힌트로 여기에 비교를 그립니다. 이 쿼리는 플래너에게 얻을 수 있는 최악의 쿼리입니다. 스캔하는 데이터의 양을 줄이기 위해 인덱스를 사용하려고 하지만 수십만 개의 항목을 스캔하도록 만들었습니다.
플래너에서 상황을 더 나쁘게 만들면 어떻게 될까요? 이 테이블의 더 큰 청크가 쿼리와 일치하도록 만들면 어떻게 될까요?
3. 둔화 만들기
다시 말하지만, 순전히 더 많은 행을 삽입하여 속도 저하를 만들 예정이지만 플래너에서 상황을 악화시키고 싶으므로 이러한 행에 다른 가중치를 부여합니다.
-- insert 1mil more rows, even more weight on low values
-- (0.5 instead of 1, is the difference)
insert into dogs_seen (num_seen) select (0.5/random()) from generate_series(1,1000000);
-- don't forget to analyze
analyze dogs_seen;
이제 원래 25ms SQL에 대해 설명합니다.
explain analyze select count(*) from dogs_seen where num_seen = 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=23271.91..23271.92 rows=1 width=8) (actual time=76.779..77.476 rows=1 loops=1)
-> Gather (cost=23271.69..23271.90 rows=2 width=8) (actual time=76.730..77.471 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=22271.69..22271.70 rows=1 width=8) (actual time=70.811..70.811 rows=1 loops=3)
-> Parallel Seq Scan on dogs_seen (cost=0.00..21227.67 rows=417611 width=0) (actual time=0.024..57.560 rows=333558 loops=3)
Filter: (num_seen = 1)
Rows Removed by Filter: 333109
Planning Time: 0.599 ms
Execution Time: 77.524 ms
(10 rows)
동일한 SQL이 순차 스캔으로 변경되었습니다! 내 머릿속에는 PG 기획자가 다음과 같이 말하는 것을 상상하는 대화가 있습니다.
그러나 postgres는 위의 사항을 어떻게 결정합니까?
4. pg_stats
위에서
ANALYZE dogs_seen
를 실행했을 때 postgresql은 이 테이블에 대한 pg_stats
행을 업데이트했습니다. 이러한 열은 모두 알고 있으면 유용하며 부담 없이 사용할 수 있습니다read up on them . 여기서 우리가 관심을 갖는 것은 most_common_vals
와 그 동료most_common_freqs
입니다.select left(most_common_vals::text, 10) as most_common_vals,
left(most_common_freqs::text, 50) as most_common_freqs
from pg_stats
where tablename = 'dogs_seen' and attname = 'num_seen';
most_common_vals | most_common_freqs
------------------+----------------------------------------------------
{1,2,3,4,5 | {0.5011333,0.19793333,0.08653333,0.047066666,0.029
(1 row)
이것은 다음과 같이 읽습니다.
1
이 샘플링된 행2
이 샘플링된 행동일한 값을 가진 많은 행을 삽입함으로써
ANALYZE
가 목록을 채울 때 이 목록의 항목에 영향을 미쳤습니다. 그리고 플래너는 인덱스를 사용하는 대신 값1
을 순차적으로 스캔하도록 조정했습니다.이 집으로 운전하기 위해 많은
1
를 삭제하고 다시 살펴보겠습니다.-- delete 500k rows, make "1" great again
delete from dogs_seen where id in (select id from dogs_seen where num_seen = 1 limit 500000);
-- re-analyze
analyze dogs_seen;
-- check out the new stats
select left(most_common_vals::text, 10) as most_common_vals, left(most_common_freqs::text, 50) as most_common_freqs from pg_stats where tablename = 'dogs_seen' and attname = 'num_seen';
most_common_vals | most_common_freqs
------------------+----------------------------------------------------
{1,2,3,4,5 | {0.32923332,0.27103335,0.11643333,0.0607,0.0405666
(1 row)
-- did we switch it back onto the index?
explain analyze select count(*) from dogs_seen where num_seen = 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=9520.65..9520.66 rows=1 width=8) (actual time=33.478..34.535 rows=1 loops=1)
-> Gather (cost=9520.44..9520.65 rows=2 width=8) (actual time=33.384..34.527 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=8520.44..8520.45 rows=1 width=8) (actual time=28.603..28.603 rows=1 loops=3)
-> Parallel Index Only Scan using dogs_idx on dogs_seen (cost=0.43..8006.01 rows=205771 width=0) (actual time=0.265..17.126 rows=166891 loops=3)
Index Cond: (num_seen = 1)
Heap Fetches: 0
Planning Time: 0.417 ms
Execution Time: 34.570 ms
(10 rows)
Index
스캔으로 돌아왔습니다! 당신은 그것을 가지고 있습니다. ANALYZE
를 실행하여 행을 삽입하고 쿼리 계획을 변경했습니다. 당신을 괴롭히는 순차 스캔이 있다면 아마도 이것 때문일 것입니다!
Reference
이 문제에 관하여(순차 스캔 삽입), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/jakswa/inserting-sequential-scans-2m25텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)