← 전체 글로 돌아가기

서버 운영

페이지네이션 쿼리가 서버에서만 느릴 때

서버 환경에서 페이지네이션 조회가 느려질 때 원인을 찾는 방법을 정리했다.

로컬에서는 빠르지만 서버에서 페이지네이션 조회가 느려지는 경우가 있다. 데이터량이 많고, 인덱스가 없거나, 쿼리가 비효율적일 때 주로 발생한다.

실제 쿼리가 뭔지 확인

# 서버에 접속
ssh user@server

# 데이터베이스에서 느린 쿼리를 본다
POSTGRES의 경우:
sudo tail -100 /var/log/postgresql/postgresql.log | grep duration

# MySQL의 경우:
mysql -u user -p -e "SELECT * FROM mysql.slow_log LIMIT 10\G"

로그에서 확인할 것:

  • 실제 쿼리가 뭔지
  • 얼마나 오래 걸렸는지
  • 얼마나 많은 row를 검사했는지

EXPLAIN으로 쿼리 분석

-- 느린 쿼리를 분석한다
EXPLAIN ANALYZE
SELECT * FROM posts
WHERE user_id = 123
ORDER BY created_at DESC
LIMIT 10 OFFSET 100;

EXPLAIN 결과에서 보기:

  • Seq Scan: 전체 테이블을 훑었다 → 인덱스 필요
  • Index Scan: 인덱스를 사용했다 → 좋음
  • Filter rows: 1000000 rows → 10 rows: 매우 많은 행을 검사

인덱스 추가

-- 페이지네이션 쿼리에 자주 쓰이는 컬럼에 인덱스 추가
CREATE INDEX idx_posts_user_created
ON posts(user_id, created_at DESC);

인덱스를 추가한 후 다시 테스트:

# 시간을 재본다
time curl 'https://api.example.com/users/123/posts?page=1'

COVERING INDEX를 만들면 더 빠르다:

CREATE INDEX idx_posts_user_created_covering
ON posts(user_id, created_at DESC)
INCLUDE (id, title, content);

OFFSET의 문제

OFFSET을 쓰면 뒤로 갈수록 느려진다:

-- page 1 (offset 0): 빠름
SELECT * FROM posts LIMIT 10 OFFSET 0;

-- page 1000 (offset 9990): 매우 느림 (10,000개 행을 건너뛰어야 함)
SELECT * FROM posts LIMIT 10 OFFSET 9990;

Keyset pagination (또는 Cursor-based pagination)을 쓰면 훨씬 빠르다:

-- 마지막 게시물의 ID를 받아서
SELECT * FROM posts
WHERE id < :last_id  -- 또는 created_at < :last_created_at
ORDER BY id DESC
LIMIT 10;

데이터베이스 연결 풀 확인

# 활성 연결이 많은지 확인
POSTGRES:
psql -c "SELECT count(*) FROM pg_stat_activity;"

# MySQL:
mysql -e "SHOW PROCESSLIST;"

연결 풀이 제대로 설정되지 않으면 쿼리가 대기한다. 앱 설정을 확인:

# 환경 변수 확인
echo $DATABASE_POOL_SIZE
echo $DATABASE_POOL_TIMEOUT

캐싱 추가

자주 요청되는 페이지는 캐시한다:

// Redis를 쓴다면
const cacheKey = `posts:user:${userId}:page:${page}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);

const posts = await db.posts.findMany({
  where: { userId },
  skip: (page - 1) * 10,
  take: 10,
});

await redis.setex(cacheKey, 3600, JSON.stringify(posts));

최종 확인 단계

  1. 실제 쿼리 성능을 본다 (EXPLAIN ANALYZE)
  2. 필요한 인덱스를 추가한다
  3. OFFSET 대신 Keyset pagination을 고려한다
  4. 자주 요청되는 페이지는 캐시한다

These steps should significantly improve pagination performance on the server.