MySQL을 Redis처럼 메모리에서 돌리면 얼마나 빨라질까?
들어가며
“Redis는 RAM 위에서 동작해서 빠른데, MySQL도 메모리에 올리면 얼마나 빨라질까?”
이런 단순한 호기심에서 시작된 실험입니다. Spring Boot 애플리케이션과 MySQL을 조합해 디스크 기반 환경과 메모리 기반 환경의 성능을 비교해 보았습니다.
이번 글에서는 다음 내용을 정리합니다.
- MySQL을 인메모리 환경으로 구성하는 방법
- 디스크 기반 MySQL과 메모리 기반 MySQL의 성능 차이
- 실제 테스트에서 얻은 결과와 해석
- 실무에서 활용할 수 있는 방향과 한계
실험 환경
공통 조건
- 로컬 MacBook 개발환경
- Spring Boot 애플리케이션 (동일한 코드)
- 데이터 건수: 8만건
- K6를 이용한 성능 테스트 (3분간, 최대 50 VU)
테스트 시나리오
- 80% 읽기 작업 (목록 조회 + 상세 조회)
- 20% 쓰기 작업 (데이터 생성)
MySQL을 메모리에서 실행하는 방법
MySQL을 메모리에서 실행하는 방법은 크게 두 가지가 있습니다:
1. MEMORY 스토리지 엔진
1
2
3
4
5
CREATE TABLE items (
id INT PRIMARY KEY,
title VARCHAR(255),
content TEXT
) ENGINE=MEMORY;
위 SQL은 예시이며, 실제 실험 테이블은 이 구조 그대로 사용하지는 않았습니다.
특징:
- 테이블 단위로 RAM에 저장
- MySQL의 모든 기능 사용 가능
- 서버 재시작 시 데이터 소멸
- 특정 테이블만 선택적으로 메모리화
2. datadir를 메모리에 마운트 (tmpfs)
1
2
3
4
5
6
# Docker를 이용한 메모리 마운트
docker run --name mysql-mem \
--tmpfs /var/lib/mysql:rw,size=4g \
-e MYSQL_ROOT_PASSWORD=1234 \
-p 3306:3306 \
-d mysql:8
특징:
- MySQL 전체 DB가 RAM에서 동작
- InnoDB 등 모든 엔진 사용 가능
- Redis처럼 완전한 인메모리 환경
- 서버 재시작 시 모든 데이터 소멸
이번 실험에서는 더 Redis다운 두 번째 방법을 선택했습니다.
성능 테스트 결과
첫 번째 시도: Docker 메모리 제한의 함정
처음에는 이렇게 설정하고 테스트했습니다:
1
2
3
4
5
6
# 잘못된 설정 - 메모리 제한 없음
docker run --name mysql-mem \
--tmpfs /var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=1234 \
-p 3306:3306 \
-d mysql:8
1
2
3
4
5
6
7
8
9
메모리 마운트 케이스:
- 평균 응답시간: 8.83ms
- RPS: 166.2
- 총 요청: 80,029
일반 디스크 케이스:
- 평균 응답시간: 8.11ms
- RPS: 165.5
- 총 요청: 79,762
결과: 거의 동일한 성능
처음에는 “왜 차이가 없지?”라는 의문이 들었습니다. 문제는 Docker의 메모리 설정이었습니다. tmpfs를 설정해도 충분한 메모리가 할당되지 않으면 실질적인 차이가 거의 나지 않을 수 있습니다.
올바른 설정으로 재시도
1
2
3
4
5
6
# 올바른 설정 - 명시적 메모리 크기 지정
docker run --name mysql-mem \
--tmpfs /var/lib/mysql:rw,size=4g \
-e MYSQL_ROOT_PASSWORD=1234 \
-p 3306:3306 \
-d mysql:8
이제야 제대로 된 결과가 나왔습니다!
디스크 기반 MySQL (첫 번째 정식 테스트)
1
2
3
4
5
6
7
8
평균 응답시간: 1.91초 (1,910ms)
RPS: 22.7
총 요청: 4,140
실패율: 0.02%
응답시간 목표 달성률:
- List (< 500ms): 0% 달성
- Create (< 1000ms): 20% 달성
- Detail (< 300ms): 3% 달성
메모리 기반 MySQL (두 번째 정식 테스트)
1
2
3
4
5
6
7
8
평균 응답시간: 216ms
RPS: 100.8
총 요청: 18,265
실패율: 0.00%
응답시간 목표 달성률:
- List (< 500ms): 95% 달성
- Create (< 1000ms): 99% 달성
- Detail (< 300ms): 96% 달성
결과를 한눈에 보면
| 지표 | 디스크 기반 | 메모리 기반 | 개선율 |
|---|---|---|---|
| 평균 응답시간 | 1.91초 | 216ms | 89% 개선 |
| 95%ile 응답시간 | 3.1초 | 438ms | 86% 개선 |
| RPS (처리량) | 22.7 | 100.8 | 4.4배 증가 |
| 총 요청 수 | 4,140 | 18,265 | 4.4배 증가 |
이번 테스트에서는 특히 읽기 중심 워크로드에서 차이가 크게 벌어졌고, 처리량도 눈에 띄게 증가했습니다.
단건 테스트 결과
쓰기 성능 (INSERT)
- 메모리: 13~15ms
- 디스크: 12~15ms
- 차이가 거의 없음
읽기 성능 (SELECT)
- 메모리: 3.60초 (8만건 조회)
- 디스크: 5.15초 (8만건 조회)
- 약 30% 성능 향상
왜 이런 결과가 나왔을까?
첫 번째 실험에서 차이가 거의 없었던 이유
Docker의 tmpfs 설정 시 메모리 크기를 명시하지 않으면 실질적인 인메모리 효과가 제한될 수 있습니다.
1
2
3
4
5
# ❌ 잘못된 설정 - 기본 메모리 제한으로 인해 효과 없음
--tmpfs /var/lib/mysql
# ✅ 올바른 설정 - 충분한 메모리 할당으로 인메모리 환경 구성
--tmpfs /var/lib/mysql:rw,size=4g
이 실험에서 얻은 가장 큰 교훈 중 하나는, Docker 설정의 작은 차이가 결과를 크게 바꿀 수 있다는 점이었습니다.
읽기에서 큰 성능 차이가 난 이유
- 조회 작업은 데이터 접근 속도에 크게 의존
- 메모리는 디스크 I/O 없이 즉각적인 접근 가능
- 디스크는 OS 페이지 캐시가 있어도 메모리보다 느림
쓰기에서 성능 차이가 적었던 이유
- MySQL 내부 오버헤드가 큼: 로그 쓰기, 트랜잭션 관리, 버퍼 플러시 등
- 디스크와 메모리의 차이가 MySQL 자체 처리 시간에 묻힘
- InnoDB 엔진의 복잡한 트랜잭션 처리 구조
실무에서는 어떻게 활용할 수 있을까?
Read Replica를 메모리에 두는 방식
1
2
3
4
5
6
┌─────────────────┐ ┌──────────────────┐
│ Primary DB │───▶│ Read Replica │
│ (디스크 기반) │ │ (메모리 기반) │
│ - 쓰기 담당 │ │ - 읽기 전용 │
│ - 안정성 보장 │ │ - 빠른 조회 │
└─────────────────┘ └──────────────────┘
장점:
- 쓰기는 안정적인 디스크 DB에서 처리
- 읽기는 빠른 메모리 DB에서 처리
- 읽기 위주 워크로드에서 큰 성능 향상 기대
즉, 운영 안정성이 중요한 Primary는 디스크 기반으로 유지하고, 조회 트래픽이 많은 Replica만 메모리 기반으로 구성하는 방식이 가장 현실적인 선택지로 보였습니다.
메모리 기반 MySQL의 한계
1. 데이터 영속성 문제
- 서버 재시작 시 모든 데이터 소멸
- 장애 시 데이터 복구 불가능
- 프로덕션 환경에서는 백업 전략 필수
2. 메모리 사용량
- 전체 DB가 RAM에 상주해야 함
- 대용량 데이터베이스에는 부적합
- 메모리 비용 고려 필요
3. 동시성 처리
- MySQL의 락 메커니즘은 여전히 존재
- Redis만큼의 lock-free 성능은 기대 어려움
마무리
이번 실험의 핵심은 단순히 “메모리에 올리면 빨라진다”가 아니었습니다. 어떤 워크로드에서, 어떤 설정으로, 어느 정도 효과가 나는지를 확인하는 과정에 더 가까웠습니다.
핵심 발견사항
- Docker 설정의 중요성:
tmpfs설정 시 메모리 크기를 명시하지 않으면 기대한 성능 차이가 나오지 않을 수 있음 - 읽기 위주 워크로드에서는 인메모리 MySQL이 큰 성능 향상을 가져다줌 (89% 응답시간 단축)
- 쓰기 성능은 MySQL 자체 구조적 한계로 인해 개선 효과가 제한적
- Read Replica 구성이 실무에서 가장 현실적인 활용 방안이 될 수 있다
실험에서 배운 교훈들
- 환경 설정의 디테일이 결과를 좌우한다 (Docker 메모리 설정)
- 예상과 다른 결과가 나올 때는 설정을 재점검하자
- 동일한 조건에서 여러 번 테스트해야 신뢰할 수 있는 결과를 얻는다
언제 고려해볼만한가?
- 읽기:쓰기 비율이 8:2 이상인 서비스
- 실시간 조회 성능이 중요한 대시보드/분석 서비스
- 캐시로 해결하기 어려운 복잡한 쿼리가 많은 경우
주의할 점
- 데이터 영속성과 안정성을 반드시 고려
- 메모리 비용과 성능 향상의 trade-off 분석 필요
- 단순히 메모리에 올린다고 Redis만큼 빨라지지는 않음
이번 실험을 통해 인메모리 데이터베이스의 특성과 한계를 명확히 이해할 수 있었습니다. 무작정 메모리에 올리는 것보다는 워크로드 특성에 맞는 최적화 전략을 수립하는 것이 더 중요하다는 교훈을 얻었습니다.
다음에는 Redis와 인메모리 MySQL의 직접적인 성능 비교, 그리고 MongoDB를 인메모리 환경으로 구성했을 때도 유의미한 차이가 나는지 추가로 실험해볼 생각입니다.