Ruby on Rails 애플리케이션의 PostgreSQL 성능 최적화

Easy to Overlook PostgreSQL Performance Issues in Rails Apps

작성자
jeff
발행일
2024년 08월 27일

핵심 요약

  • 1 Rails 앱에서 PostgreSQL의 'random_page_cost'를 조정하여 인덱스 활용도를 높이고 순차 스캔을 줄여 성능을 개선할 수 있습니다.
  • 2 활용되지 않거나 NULL 값이 많은 인덱스를 제거하거나 최적화하여 쓰기 작업 성능을 향상시키고 데이터베이스 오버헤드를 감소시킬 수 있습니다.
  • 3 'work_mem' 설정을 적절히 조정하여 쿼리 정렬 및 그룹화 작업이 디스크 대신 메모리에서 효율적으로 수행되도록 함으로써 전반적인 응답 시간을 단축할 수 있습니다.

도입

PostgreSQL 데이터베이스 계층에서 발생하는 성능 문제는 애플리케이션의 규모가 커질수록 더욱 명확하게 드러납니다. 본 글은 여러 Rails 프로젝트에서 흔히 발생하는 데이터베이스 설정 문제와 일반적인 성능 병목 현상에 대한 실질적인 해결책을 제시합니다. 특히, `random_page_cost` 튜닝, 불필요한 인덱스 제거, 그리고 `work_mem` 설정 조정에 중점을 두어 Rails 애플리케이션의 데이터베이스 성능을 최적화하는 방법을 다룹니다. 이러한 최적화는 애플리케이션의 응답성과 확장성을 크게 향상시키는 데 기여할 것입니다.

random_page_cost 튜닝을 통한 인덱스 활용도 증대

PostgreSQL의 random_page_cost는 디스크에 저장된 인덱스를 통해 데이터를 가져오는 비용을 추정하는 값입니다. 기본값인 4는 과거 HDD 기반 시스템에서 유래한 것으로, 현대의 SSD 기반 데이터베이스에서는 종종 비효율적입니다. 이 값이 높게 설정되어 있으면 쿼리 플래너는 인덱스 스캔 대신 CPU와 메모리 집약적인 순차 스캔을 선호하게 됩니다. 이는 결국 애플리케이션의 성능 저하로 이어질 수 있습니다. random_page_cost를 1.1과 같이 낮게 설정하면 쿼리 플래너가 인덱스 스캔을 더 자주 선택하도록 유도하여 성능을 향상시킬 수 있습니다. EXPLAIN 명령을 통해 쿼리 계획의 변화를 확인할 수 있으며, RailsPgExtras.unused_indexes 메서드를 사용하여 변경 전후의 미사용 인덱스 수를 비교함으로써 실제 효과를 측정할 수 있습니다. 이 설정은 현재 세션에만 영향을 미치므로 프로덕션 환경에서도 안전하게 테스트하고 적용할 수 있는 장점이 있습니다.

인덱스 및 순차 스캔 제어 이해

enable_indexscan, enable_bitmapscan, enable_seqscan과 같은 설정을 조정함으로써 쿼리 플래너의 동작 방식을 심층적으로 이해할 수 있습니다. 예를 들어, 인덱스 스캔을 비활성화하면 쿼리 플래너는 다른 대안이 없는 경우 순차 스캔으로 대체됩니다. 이는 random_page_cost를 극단적으로 높이는 것과 유사한 효과를 냅니다. 특정 스캔 유형을 강제로 비활성화하더라도, 쿼리 플래너는 가능한 최선의 방법을 찾으려 하며, 다른 대안이 없다면 비활성화된 스캔을 여전히 사용할 수 있음을 EXPLAIN 결과를 통해 확인할 수 있습니다. 이러한 테스트는 쿼리 플래너의 복잡한 로직을 파악하는 데 유용합니다.

NULL 인덱스 최적화

데이터베이스 인덱스는 쓰기 작업에 오버헤드를 추가하고, autovacuum 시간 및 잠금 경합을 증가시킬 수 있습니다. 특히 대부분 NULL 값을 포함하는 인덱스, 이른바 ‘NULL 인덱스’는 비효율성을 야기합니다. RailsPgExtras.null_indexes 메서드를 사용하여 이러한 인덱스를 쉽게 식별할 수 있습니다. 예를 들어, 선택적 관계의 외래 키에 대한 인덱스가 NULL 인덱스의 전형적인 예시입니다. 99%가 NULL 값으로 채워진 50GB 이상의 인덱스도 발견될 수 있습니다. 이러한 인덱스를 WHERE IS NOT NULL 조건을 사용하여 재인덱싱함으로써 인덱스 크기를 대폭 줄이고 쓰기 작업 성능을 현저히 개선할 수 있습니다. 이는 사실상 사용되지 않는 인덱스를 완전히 제거하는 것과 유사한 긍정적인 효과를 가져옵니다.

work_mem PostgreSQL 설정 조정

work_mem 설정은 ORDER BY, DISTINCT, IN과 같은 쿼리 작업이 사용할 수 있는 최대 메모리 양을 정의합니다. PostgreSQL의 기본 work_mem 값인 4MB는 특정 쿼리에서 메모리가 부족하여 디스크에 임시 파일을 생성하고 정렬 작업을 수행하게 만들 수 있습니다. 디스크 기반 정렬은 인메모리 정렬보다 훨씬 느려 전반적인 쿼리 응답 시간을 지연시킵니다. AWS RDS 사용자의 경우, Performance Insights에서 ‘Temp bytes’ 및 ‘Temp files’ 지표를 모니터링하여 애플리케이션이 디스크 기반 임시 파일을 사용하고 있는지 확인할 수 있습니다. PGTune과 같은 도구는 데이터베이스에 적합한 work_mem 값을 결정하는 데 도움을 줄 수 있습니다. 이 값을 점진적으로 2배씩 증가시키면서 데이터베이스의 메모리 사용량을 주의 깊게 모니터링하는 것이 중요합니다. work_mem은 동적으로 변경 가능한 설정이므로 데이터베이스를 재시작할 필요 없이 조정할 수 있습니다. RailsPgExtras.outlierspg_stat_statements_reset을 활용하여 work_mem 변경 전후의 쿼리 성능 변화를 측정하고, 영향을 받은 쿼리의 실행 시간이 유의미하게 감소하는지 확인할 수 있습니다.

결론

이 글에서 설명한 PostgreSQL 설정 최적화 기법들을 Rails 애플리케이션에 적용함으로써, 특히 대규모 데이터셋을 다루는 환경에서 측정 가능한 성능 개선을 이룰 수 있습니다. `random_page_cost` 조정, NULL 인덱스 최적화, 그리고 `work_mem` 설정 변경은 데이터베이스의 효율성을 극대화하고 애플리케이션의 응답성을 향상시키는 핵심적인 방법들입니다. 또한, 각 변경 사항의 영향을 모니터링하는 구체적인 방법들을 제시함으로써, 개발자와 운영자가 더욱 확신을 가지고 이러한 튜닝을 프로젝트에 적용할 수 있도록 돕습니다. 적절한 데이터베이스 튜닝은 Rails 애플리케이션의 안정성과 사용자 경험을 크게 향상시키는 데 필수적인 요소입니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!