DEMO blogZ 데모 · 매일 새벽 4시 초기화 demo 계정으로 로그인하면 블로그 주인이 되어 글쓰기 · 관리 화면을 써 볼 수 있습니다

작은 서비스를 만들고 운영하며 배운 것들

백엔드PHP 성능 튜닝 노트 · 2편

N+1 쿼리를 한 번에 잡기

PHP 성능 튜닝 노트 2편

윤서 2분 읽기

지난 편에서 찾은 범인은 이런 코드였다. 주문 목록을 가져온 뒤, 주문마다 상품을 다시 조회한다.

foreach ($orders as $order) {
    // 주문마다 쿼리 한 번씩: 주문 200개면 쿼리 200번
    $order->items = $db->query('SELECT * FROM order_items WHERE order_id = ?', [$order->id])->fetchAll();
}

IN 으로 한 번에 가져오기

$ids = array_column($orders, 'id');
$marks = implode(',', array_fill(0, count($ids), '?'));
$rows = $db->query("SELECT * FROM order_items WHERE order_id IN ($marks)", $ids)->fetchAll();

$byOrder = [];
foreach ($rows as $row) {
    $byOrder[$row->order_id][] = $row;
}
foreach ($orders as $order) {
    $order->items = $byOrder[$order->id] ?? [];
}

결과

고치기 전고친 뒤
쿼리 수 (주문 200개)201회2회
평균 응답1,840ms210ms
최대 응답5,210ms380ms

IN 목록이 수천 개가 넘으면 오히려 느려질 수 있습니다. 페이지 단위로 나눠 가져오세요.

코드 열 줄 남짓 바꿨을 뿐인데 체감이 완전히 달라졌다. 메일을 보내 준 사용자에게 답장을 쓰면서 가장 뿌듯했던 순간.