비교할 계약

예제 애플리케이션은 id, email만 사용한다고 가정합니다. SELECT *와 명시적 컬럼 query의 SQL 결과 컬럼 수는 다르지만 최종 애플리케이션 결과가 같다는 조건에서 불필요한 컬럼을 가져오는 비용을 비교합니다.

재현하고 반복하는 방법

CREATE TABLE users (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email text NOT NULL, display_name text NOT NULL,
  profile jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX users_email_cover_idx ON users (email) INCLUDE (id);

EXPLAIN (ANALYZE, BUFFERS, SERIALIZE TEXT, FORMAT JSON)
SELECT id, email FROM users WHERE email = '[email protected]';

EXPLAIN (ANALYZE, BUFFERS, SERIALIZE TEXT, FORMAT JSON)
SELECT * FROM users WHERE email = '[email protected]';

첫 query가 Index Only Scan 후보가 되는지와 SELECT *에서 heap 접근이 생기는지를 확인합니다. row 수, email 선택도, profile 크기·분포와 통계 상태를 고정하고, SERIALIZE는 PostgreSQL 17 이상에서 사용합니다. A-B와 B-A 순서로 반복하되 warm cache와 cold cache는 별도 실험군으로 두고 원시 표본, median과 p95를 남깁니다.

기록하고 해석할 값

계층 기록할 값
plan scan 종류, estimated/actual rows, width, loops
storage Heap Fetches, shared hit/read, temp read/write
DB 출력 execution time, serialization time
client end-to-end time, 수신 byte, 실제 사용 컬럼

EXPLAIN ANALYZE는 결과를 client로 전송하지 않습니다. network와 driver decode 비용은 실제 driver가 모든 row를 읽는 별도 측정으로 확인합니다.

공식 문서