질문을 정확히 바꿉니다

SELECT *는 느린가’가 아니라 ‘애플리케이션이 id, email만 쓰는데 모든 컬럼을 가져오면 어느 계층의 비용이 늘어나는가’를 묻습니다. raw result shape가 다른 두 SQL은 애플리케이션이 실제로 소비하는 값과 최종 동작을 비교 계약으로 삼습니다.

DB 안에서 확인하는 순서

일반 EXPLAIN으로 예상 plan·rows·cost를 보고, EXPLAIN (ANALYZE, BUFFERS)로 actual rows·time·loops와 buffer 접근을 확인합니다. PostgreSQL 17 이상에서는 SERIALIZE로 출력 변환 비용을 더 볼 수 있지만 network 전송 시간은 포함하지 않습니다. Index Only Scan이 다른 scan으로 바뀌면 Heap Fetches와 read를 함께 확인하고, warm-cache와 cold-cache 표본은 섞지 않습니다.

DB와 client를 나눠 봅니다

DB에는 plan, rows, width, buffer·I/O와 serialization을, client에는 end-to-end latency, 수신 byte와 decode 시간을 둡니다. lock 대기는 EXPLAIN의 buffer 항목이 아니므로 pg_stat_activitypg_locks 등으로 별도 확인합니다. 한 번의 실행 시간이나 application RSS만으로 원인을 확정하지 않습니다.

공식 문서