
개요
이번 콘서트 예약 서비스에서는 "대기열 토큰 발급 및 조회" 기능을 위해 Redis를 사용했습니다. 해당 기능은 실시간 응답성과 빠른 처리
속도가 중요하기 때문에, 기존 DB보다 인메모리 데이터베이스인 Redis가 더 적합하다고 판단했습니다. 이 보고서에서는
Redis와 DB 간의 성능 비교 실험 결과를 상세히 설명하고, 대기열 기능에서 Redis를 선택한 이유를 기재했습니다.
성능 비교 테스트 개요
성능 비교는 k6 부하 테스트 툴을 사용하여 Redis와 DB에서 각각 실행했습니다. 시나리오는 10명의 가상 사용자로 시작해 1분 동안 50명으로 증가시키고, 마지막 30초에 0명으로 감소하는 방식으로 2분간 부하를 유지했습니다. 이는 유저가 대기열 토큰을 발급받고 자신의 순서를 자주 확인해야 하는 실제 상황을 시뮬레이션하며, 대기열 토큰 발급과 조회 API의 응답 시간과 처리 성능을 평가했습니다.
테스트 환경 설정
- 부하 스테이지: 30초 동안 10명에서 50명으로 증가 후, 1분간 50명 유지, 마지막 30초 동안 0명으로 감소
- 검사 기준:
- HTTP 요청의 95퍼센타일 응답 시간 < 500ms
- 요청 실패율 < 1%
- 테스트 스크립트:
- 토큰 발급: 유저 ID를 입력으로 받아 JWT 토큰을 생성
- 대기열 조회: 유저의 토큰을 사용하여 현재 대기열 상태와 위치를 확인
import http from 'k6/http';
import { check, sleep, fail } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 10 },
{ duration: '1m', target: 50 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
const BASE_URL = 'http://localhost:8080/v1/api/queue';
const existingUserIds = [1, 2, 3];
function fetchJwtToken(userId) {
const payload = JSON.stringify({ userId: userId });
const res = http.post(`${BASE_URL}/token`, payload, {
headers: { 'Content-Type': 'application/json' },
});
console.log('Token response:', JSON.stringify(res.json()));
check(res, {
'Token created successfully': (r) => r.status === 200,
});
return res.json().data.queueToken;
}
export default function () {
const userId = existingUserIds[Math.floor(Math.random() * existingUserIds.length)];
const token = fetchJwtToken(userId);
console.log(`Generated JWT token: ${token}`);
const checkQueueRes = http.post(`${BASE_URL}/token/check`, null, {
headers: {
'Content-Type': 'application/json',
'Authorization': token,
},
});
console.log('Check Queue Response:', checkQueueRes.status, checkQueueRes.json());
const checks = check(checkQueueRes, {
'Check token status is 200': (r) => r.status === 200,
'Queue position is defined': (r) => r.json().data.queuePosition !== undefined,
'Queue status is defined': (r) => r.json().data.status !== undefined,
});
if (!checks) {
fail('Failed to get expected response from checkQueue API');
}
sleep(1);
}
성능 비교 결과
성능 비교 결과, Redis가 DB에 비해 모든 주요 지표에서 더 우수한 성능을 보였습니다. 특히, 평균 응답 시간, 95퍼센타일 응답 시간,
최대 응답 시간 등에서 Redis의 성능이 뚜렷하게 나타났습니다.


Redis vs. DB 성능 비교 (요약)
Redis | DB | |
평균 응답 시간 | 23.83 ms | 47.42 ms |
95퍼센타일 응답 시간 | 31.78 ms | 70.54 ms |
최대 응답 시간 | 229.66 ms | 157.27 ms |
요청 실패율 | 0% | 0% |
총 요청 수 | 10,320 | 10,676 |
데이터 전송량 | 1.6 MB (수신) | 1.6 MB (수신) |
성공한 요청 비율 | 100% | 100% |
상세 결과 해석
- 평균 응답 시간: Redis가 DB에 비해 약 50% 더 빠른 응답 시간을 보였습니다.
- 95퍼센타일 응답 시간: Redis에서는 95%의 요청이 31.78ms 이내에 처리된 반면, DB는 70.54ms로 더 느렸습니다.
이는 Redis가 고부하 상황에서 더 안정적인 성능을 제공함을 의미합니다. - 최대 응답 시간: Redis는 최대 응답 시간이 229.66ms로 고부하에서도 일관된 응답 성능을 제공했습니다.
- 요청 실패율: 두 시스템 모두 실패율은 0%였습니다.
이 결과를 통해 대기열 토큰 발급과 조회와 같은 실시간성이 요구되는 기능에서는 Redis가 DB보다 효율적임을 확인할 수 있었습니다.
Redis를 선택한 추가적인 이유
- 고성능과 빠른 응답 속도
- Redis는 인메모리 데이터베이스로, 특히 읽기와 쓰기 속도가 매우 빠릅니다. 대기열 토큰 발급과 조회는 실시간 응답이
필수이기 때문에, Redis의 빠른 응답 속도가 중요하게 작용했습니다.
- Redis는 인메모리 데이터베이스로, 특히 읽기와 쓰기 속도가 매우 빠릅니다. 대기열 토큰 발급과 조회는 실시간 응답이
- TTL 기반 캐싱 전략
- Redis의 TTL(Time-To-Live) 기능을 활용하여 대기열 토큰을 자동으로 만료하고 삭제할 수 있습니다.
이를 통해 데이터 유효 기간을 효과적으로 관리할 수 있으며, DB와 달리 만료된 데이터를 별도로 삭제하는 관리 작업이
필요 없습니다. 이 기능을 통해 만료된 토큰을 자동으로 제거하여 불필요한 스케쥴링 로직을 제외했습니다.
- Redis의 TTL(Time-To-Live) 기능을 활용하여 대기열 토큰을 자동으로 만료하고 삭제할 수 있습니다.
- 데이터 영속성 필요성의 차이
- 대기열 토큰은 영구적으로 보관할 필요가 없는 임시 데이터입니다. 유저가 대기열을 벗어나면 더 이상 유효하지 않기 때문에
영속성이 필요 없습니다. 반면, 좌석 예약이나 결제와 같은 주요 데이터는 영구적 보관과 데이터 일관성이 필요하므로 DB에
저장하여 관리하는 것이 더 적합하다고 판단했습니다.
- 대기열 토큰은 영구적으로 보관할 필요가 없는 임시 데이터입니다. 유저가 대기열을 벗어나면 더 이상 유효하지 않기 때문에
+ 추가 확인건
기존에는 Redis에 객체를 저장할 때 ObjectMapper와 GenericJackson2JsonRedisSerializer를 사용해 직렬화 및 역직렬화를
처리했습니다. 이러한 방식은 직렬화/역직렬화 과정에서 추가적인 메모리와 CPU 리소스를 소모하게 되어 Redis의
빠른 속도를 충분히 활용하지 못하는 우려가 있었습니다. 불필요한 오버헤드를 최소화하여 빠르게 데이터를 처리할 수 있도록
따라서 문자열 기반의 수동 직렬화 방식을 구현하여 최적화 하였습니다.
리팩토링 과정
- 기존 방식: ObjectMapper와 GenericJackson2JsonRedisSerializer로 직렬화 및 역직렬화
- 리팩토링 후: 문자열 기반의 수동 직렬화 방식을 통해 Redis의 처리 속도 향상
성능 테스트 결과
Redis(리팩토링 전) | Redis(리팩토링 후) | DB | |
평균 응답 시간 | 23.83 ms | 23.55 ms | 47.42 ms |
95퍼센타일 응답 시간 | 31.78 ms | 31.62 ms | 70.54 ms |
최대 응답 시간 | 229.66 ms | 228.34 ms | 157.27 ms |
요청 실패율 | 0% | 0% | 0% |
총 요청 수 | 10,320 | 10,320 | 10,676 |
리팩토링 후 Redis는 더 안정적이고 빠른 응답 속도를 보였으나, 기대했던 큰 성능 향상은 나타나지 않았습니다.
이는 성능 테스트 결과, 리팩토링 전과 후의 응답 시간에서 차이가 크지 않음을 확인한 결과로, 기존 방식이 Redis의
성능을 저하시키는 요소가 아니었기 때문으로 보입니다.
'DB' 카테고리의 다른 글
콘서트 예약 시스템 인덱스 성능 최적화 분석 보고서 (4) | 2024.11.14 |
---|---|
인덱스 설계와 쿼리 튜닝 (14) | 2024.11.12 |
동시성? 낙관적락? 비관적락? 도대체 무슨말일까 (5) | 2024.10.30 |
깨굴딱지의 코드연못입니다
올챙이가 개구리로 거듭나듯, 끊임없는 노력으로 진화하는 개발자의 길을 걷습니다. 🐸