FastAPI에서 비동기 DB 쿼리 성능 개선

문제 상황

FastAPI로 구축한 API 서버에서 PostgreSQL 조회 성능이 예상보다 낮았다. 초당 200건 정도의 요청을 처리해야 하는데, 100건 정도에서 응답 시간이 급격히 증가했다.

기존에는 SQLAlchemy 1.4의 동기 세션을 사용하고 있었다.

# 기존 코드
from sqlalchemy.orm import Session

def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(User).offset(skip).limit(limit).all()

해결 과정

FastAPI의 비동기 특성을 제대로 활용하지 못하고 있었다. SQLAlchemy의 async_session을 적용하기로 했다.

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker

engine = create_async_engine(
    "postgresql+asyncpg://user:pass@localhost/db",
    pool_size=20,
    max_overflow=10
)

async_session = sessionmaker(
    engine, class_=AsyncSession, expire_on_commit=False
)

async def get_users(db: AsyncSession, skip: int = 0, limit: int = 100):
    result = await db.execute(
        select(User).offset(skip).limit(limit)
    )
    return result.scalars().all()

핵심은 asyncpg 드라이버 사용과 적절한 커넥션 풀 설정이었다.

결과

부하 테스트 결과 초당 300건 이상의 요청을 안정적으로 처리할 수 있게 되었다. 특히 I/O 대기 시간이 긴 복잡한 쿼리에서 효과가 컸다.

다만 트랜잭션 관리와 에러 핸들링이 동기 방식보다 복잡해져서, 팀 내 학습 비용이 있었다. 그래도 성능 개선 효과를 고려하면 충분히 가치 있는 마이그레이션이었다.