피드로 돌아가기
Dev.toDatabase
원문 읽기
메모리 내 50만 건 로드 방지로 OOM 장애 해결 및 DB 연산 최적화
You Wanted a Number, but Loaded 500,000 Rows Into Memory
AI 요약
Context
Ruby on Rails 애플리케이션에서 ActiveRecord의 Lazy Loading 특성을 오해하여 대량의 데이터를 Application Memory로 로드한 설계 결함 발견. 특히 Ruby Array 메서드와 ActiveRecord 메서드의 유사한 명칭으로 인해 DB 수준의 필터링이 아닌 메모리 내 전수 조사가 수행되어 서버 OOM(Out Of Memory) 장애 유발.
Technical Solution
- Ruby Array의
.select대신 ActiveRecord의.where를 사용하여 SQL 수준에서 필터링을 수행하는 구조로 변경 - 데이터 존재 여부 확인 시 전체 로우를 가져오는
.present?대신LIMIT 1쿼리를 생성하는.exists?를 채택하여 네트워크 I/O 및 메모리 점유 최소화 - 단순 수치 계산 시
.length를 배제하고 상황에 따라.count(항상 DB 쿼리) 또는.size(로딩 상태에 따른 최적 경로 선택)를 적용하는 전략 수립 - 대량 데이터 처리 시
.each를 통한 일괄 로드 방식에서.find_each를 이용한 Batch Processing(기본 1,000건 단위)으로 전환하여 메모리 사용량 상한선 설정 - 집계 연산을 Application Layer에서 루프로 처리하던 로직을 DB 엔진의
SUM,AVG,GROUP BY함수로 위임하여 데이터 전송량 최적화
실천 포인트
- 단일 수치/불리언 값만 필요한 경우 `.count`, `.exists?`, `.sum(:column)` 등 DB 전용 메서드 사용 여부 확인 - 컬렉션의 크기를 구할 때 이미 로드된 데이터인지 확인 후 `.size`를 우선 고려 - 1만 건 이상의 레코드를 처리하는 루프 설계 시 `.each` 대신 `.find_each` 또는 `.in_batches` 적용 검토 - Ruby 블록을 사용하는 `.select { ... }`나 `.sum { ... }`이 SQL이 아닌 메모리 내 연산임을 인지하고 사용