쿼리 중에 디스크에서 무엇을 검색합니까?


13

상당히 간단한 질문인데 아마도 어딘가에 대답했지만 Google에 대한 올바른 검색 질문을 구성 할 수없는 것 같습니다 ...

특정 테이블의 열 수가 해당 테이블의 서브 세트에서 쿼리 할 때 쿼리 성능에 영향을 줍니까?

예를 들어 테이블 Foo에 20 개의 열이 있지만 쿼리에서 해당 열 중 5 개만 선택하는 경우 20 (열 대 10) 열이 쿼리 성능에 영향을 줍니까? WHERE 절의 모든 항목이이 5 개의 열에 포함되어 있다는 것을 간단하게 가정합니다.

운영 체제의 디스크 캐시 외에도 Postgres의 버퍼 캐시 사용에 대해 우려하고 있습니다. Postgres의 물리적 스토리지 설계에 대한 이해가 매우 부족합니다. 테이블은 여러 페이지에 걸쳐 저장되지만 (페이지 당 크기는 8k) 기본적으로 튜플이 어떻게 배열되는지 이해하지 못합니다. PG는 5 개의 열을 구성하는 데이터 만 디스크에서 가져올 수있을 정도로 똑똑합니까?


당신은 50 바이트를 가져 오는 것에 대해 이야기하고 있지만 나머지 150 개는 아닙니다. 디스크는 아마도 그보다 더 큰 단위로 읽을 것입니다!
Andomar

그 번호는 어디서 구합니까?
Jmoney38

답변:


14

행의 실제 스토리지는 데이터베이스 페이지 레이아웃 문서에 설명되어 있습니다. 동일한 행의 열 내용은 모두 동일한 디스크 페이지에 저장되며 TOAST 의 내용 (페이지에 너무 커서 너무 큰 내용) 은 예외입니다 . 다음과 같이 내용이 각 행 내에서 순차적으로 추출됩니다.

데이터를 읽으려면 각 속성을 차례로 검사해야합니다. 먼저 널 비트 맵에 따라 필드가 NULL인지 확인하십시오. 그렇다면 다음으로 이동하십시오. 그런 다음 올바른 정렬 상태인지 확인하십시오. 필드가 고정 너비 필드이면 모든 바이트가 단순히 배치됩니다.

가장 간단한 경우 (TOAST의 열 없음) postgres는 열이 거의 없어도 전체 행을 가져옵니다. 따라서이 경우 대답은 예입니다. 특히 열 수가 많을수록 낭비 버퍼 버퍼 캐시에 부정적인 영향을 미칠 수 있습니다. 특히 열 내용이 여전히 TOAST 임계 값 미만인 경우 열 내용이 많습니다.

이제 TOAST 사례 : 개별 필드가 ~ 2kB를 초과하면 엔진은 필드 내용을 별도의 물리적 테이블에 저장합니다. 전체 행이 페이지에 맞지 않을 때도 작동합니다 (기본적으로 8kB). 일부 필드는 TOAST 저장소로 이동됩니다. 의사는 말합니다 :

가변 길이 필드 인 경우 (attlen = -1) 조금 더 복잡합니다. 모든 가변 길이 데이터 유형은 공통 헤더 구조 struct varlena를 공유합니다. 여기에는 저장된 값의 전체 길이와 일부 플래그 비트가 포함됩니다. 플래그에 따라 데이터는 인라인 또는 TOAST 테이블에있을 수 있습니다. 압축되었을 수도 있습니다

TOAST의 컨텐츠는 명시 적으로 필요하지 않은 경우 페치되지 않으므로 페치 할 총 페이지 수에 대한 영향은 적습니다 (열당 몇 바이트). 이것은 @dezso의 답변 결과를 설명합니다.

쓰기와 관련하여 모든 열이있는 각 행은 변경된 열에 관계없이 각 UPDATE에서 완전히 다시 작성됩니다. 따라서 더 많은 열이 있으면 쓰기 비용이 더 많이 듭니다.


그것은 킥-답입니다. 정확히 내가 찾고있는 것. 감사합니다.
Jmoney38

1
행 구조 (페이지 검사 및 일부 샘플 사용법)와 관련하여 내가 찾은 좋은 리소스 here .
Jmoney38

9

Daniel의 답변 은 개별 행을 읽는 비용에 중점을 둡니다. 이 맥락에서 : 고정 크기 넣기NOT NULL 테이블에서 열을 먼저 하면 약간 도움이됩니다. 관련 열을 먼저 조회하면 도움이됩니다. 열로 정렬 테트리스재생 하여 패딩을 최소화하면 (데이터 정렬로 인해) 약간 도움이 될 수 있습니다. 그러나 가장 중요한 효과는 아직 언급되지 않았으며 특히 큰 테이블에 대해서는 언급되지 않았습니다.

추가 열은 분명히 한 행이 더 많은 디스크 공간을 차지하므로 한 데이터 페이지에 적은 수의 행 (기본적으로 8kB)이 적합합니다. 개별 행이 더 많은 페이지에 분산되어 있습니다. 데이터베이스 엔진은 일반적으로 개별 행이 아닌 전체 페이지 가져와야합니다 . 동일한 수의 페이지를 읽어야하는 한 개별 행이 다소 작거나 큰지 여부는 중요하지 않습니다.

쿼리가 큰 테이블의 (상대적으로) 작은 부분을 가져 와서 행이 인덱스에 의해 지원되는 전체 테이블에 무작위로 퍼지거나 거의 퍼지지 않으면 거의 동일한 수의 페이지 읽기가 발생합니다. 행 크기로. 관련이없는 열은 이러한 (희귀 한) 경우에 크게 느려지지 않습니다.

일반적으로 순서 나 근접성으로 입력되고 데이터 페이지를 공유 한 패치 또는 행 클러스터를 가져옵니다. 이러한 행은 혼란으로 인해 확산되므로 쿼리를 충족시키기 위해 더 많은 디스크 페이지를 읽어야합니다. 더 많은 페이지를 읽어야하는 것이 일반적으로 쿼리 속도가 느려지 는 가장 중요한 이유 입니다. 그리고 이것이 관련없는 열이 쿼리 속도를 늦추는 가장 중요한 요소입니다.

큰 데이터베이스의 경우 일반적으로 모든 RAM을 캐시 메모리에 유지하기에 충분한 RAM이 없습니다. 더 큰 행은 더 많은 캐시, 더 많은 경합, 더 적은 캐시 적중, 더 많은 디스크 I / O를 차지합니다. 디스크 읽기는 일반적으로 훨씬 비쌉니다. SSD의 경우에는 그렇지 않지만 상당한 차이가 남아 있습니다. 이것은 페이지 읽기에 대한 위의 요점을 추가합니다.

그것은 수도 있고하지 않을 수도 관련성이없는 열이 TOAST-ED 경우 문제. 관련 컬럼도 TOAST-ED 처리되어 동일한 효과를 많이 가져올 수 있습니다.


1

작은 시험 :

CREATE TABLE test2 (
    id serial PRIMARY KEY,
    num integer,
    short_text varchar(32),
    longer_text varchar(1000),
    long_long_text text
);

INSERT INTO test2 (num, short_text, longer_text, long_long_text)
SELECT i, lpad('', 32, 'abcdefeghji'), lpad('', 1000, 'abcdefeghji'), lpad('', (random() * 10000)::integer, 'abcdefeghji')
FROM generate_series(1, 10000) a(i);

ANALYZE test2;

SELECT * FROM test2;
[...]
Time: 1091.331 ms

SELECT num FROM test2;
[...]
Time: 21.310 ms

쿼리를 처음 250 개 행 ( WHERE num <= 250)으로 제한하면 각각 34.539ms 및 8.343ms가됩니다. long_long_text이 제한된 세트를 제외한 모든 것을 선택하면 18.432ms가됩니다. 이것은 당신의 용어로 PG가 충분히 똑똑하다는 것을 보여줍니다.


글쎄, 나는 입력에 감사드립니다. 그러나 나는이 테스트 시나리오가 내가 처음 제안한 것을 증명한다고 확신 할 수 없다. 몇 가지 문제가 있습니다. 우선, "SELECT * FROM test2"를 처음 실행하면 공유 버퍼 캐시가 채워져 있어야합니다. 이 쿼리는 디스크에서 검색하는 데 훨씬 오래 걸렸습니다. 따라서 두 번째 쿼리는 SB 캐시에서 가져 오기 때문에 이론적으로 훨씬 빠릅니다. 그러나 나는 PG가 나중에 테스트 / 비교를 기반으로 필요한 행만 가져 오는 것이 '추천'한다는 데 동의합니다.
Jmoney38

맞습니다.이 테스트 (단순함)에는 결함이 있습니다. 시간이 충분하면 이것들도 다룰 것입니다.
dezso
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.