Python 멀티프로세싱으로 배치 작업 성능 개선하기
문제 상황
매일 새벽 실행되는 배치 작업이 2시간 넘게 걸리면서 다음 작업 스케줄에 영향을 주기 시작했다. 약 50만 건의 레코드를 조회해 각각 API 호출과 데이터 변환을 수행하는 구조였는데, 단일 프로세스로 순차 처리하다 보니 병목이 발생했다.
해결 방법
multiprocessing.Pool을 사용해 CPU 코어를 최대한 활용하도록 변경했다.
from multiprocessing import Pool, cpu_count
import os
def process_record(record):
# API 호출 및 데이터 변환 로직
result = transform_data(record)
return result
if __name__ == '__main__':
records = fetch_records() # 50만 건
with Pool(processes=cpu_count() - 1) as pool:
results = pool.map(process_record, records, chunksize=1000)
save_results(results)
주의사항
직렬화 이슈
처음에는 클래스 메서드를 worker 함수로 사용했다가 pickle 직렬화 오류가 발생했다. 최상위 레벨 함수로 분리하거나 __getstate__, __setstate__를 구현해야 했다.
메모리 관리
pool.map은 모든 결과를 메모리에 적재하므로, 결과 데이터가 큰 경우 imap_unordered를 사용해 순차 처리하는 것이 안전했다.
for result in pool.imap_unordered(process_record, records, chunksize=1000):
save_result(result) # 즉시 저장
데이터베이스 연결
각 프로세스가 독립적인 DB 커넥션을 가져야 한다. 부모 프로세스의 연결을 공유하면 경쟁 상태가 발생할 수 있다.
결과
8코어 서버 기준으로 처리 시간이 약 30분으로 단축됐다. chunksize 튜닝으로 오버헤드를 줄이는 것이 중요했고, 1000 정도가 적절했다.