서버 운영
페이지네이션 쿼리가 서버에서만 느릴 때
서버 환경에서 페이지네이션 조회가 느려질 때 원인을 찾는 방법을 정리했다.
로컬에서는 빠르지만 서버에서 페이지네이션 조회가 느려지는 경우가 있다. 데이터량이 많고, 인덱스가 없거나, 쿼리가 비효율적일 때 주로 발생한다.
실제 쿼리가 뭔지 확인
# 서버에 접속
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));
최종 확인 단계
- 실제 쿼리 성능을 본다 (EXPLAIN ANALYZE)
- 필요한 인덱스를 추가한다
- OFFSET 대신 Keyset pagination을 고려한다
- 자주 요청되는 페이지는 캐시한다
These steps should significantly improve pagination performance on the server.