본문 바로가기
데이터베이스

DragonflyDB — 멀티스레드 인메모리 DB, Redis 25배 처리량

by forward error correction Circle 2026. 7. 9.
반응형

Ⅰ. DragonflyDB 기술이란?

ⅰ. 왜 필요한가 — Redis 모델이 깨지는 지점

Redis는 단일 스레드 + 이벤트 루프(epoll/io_uring) 구조입니다. 이 단순함이 강점이자 한계입니다. 운영을 오래 해 본 사람이라면 다음 네 가지 벽에 부딪힌 경험이 있을 겁니다.

실수에서 마주치는 Redis 한계
· 코어 활용 불가 — 64코어 서버에서도 사실상 코어 1개만 사용, 나머지는 IO 스레드(7.0+)와 백그라운드만 약간 사용
· BGSAVE 메모리 2배 폭증 — fork(2) + Copy-on-Write로 쓰기가 많을 때 RSS가 순간 2배가 되어 OOM
· 큰 키 하나가 전체 멈춤 — HGETALL on a 5GB hash → 다른 모든 명령 대기
· 수직 확장이 어려움 — 결국 Cluster로 가야 하는데 멀티키 트랜잭션, Lua, MULTI/EXEC가 깨짐

 

ⅱ. DragonflyDB 정의

DragonflyDB는 Redis와 Memcached 와이어 프로토콜을 동시 지원하는 BSL 라이선스(2025년부터 일부 변경) 의 인메모리 데이터스토어입니다. 핵심은 세 가지입니다.

아키텍처 3대 키워드
· Shard-per-Thread — CPU 코어마다 독립된 샤드. 코어 간 락 없음 (Shared-Nothing)
· Dashtable — 일반 해시테이블 대신 Dashing Hash 사용. 리해시 시 멈춤이 없음
· VLL (Very Lazy Logging) — 트랜잭션 직렬화를 위한 새로운 락 프리 알고리즘

Ⅱ. DragonflyDB 특징

❶ 수직 확장 1순위
단일 인스턴스로 1M QPS, 1TB 데이터셋까지 처리. Redis Cluster 6노드를 단일 Dragonfly 노드로 대체한 사례가 다수입니다.
❷ 점진 스냅샷
fork() 기반 BGSAVE 가 아닙니다. 점진 스냅샷 알고리즘으로 RSS 2배 폭증이 없습니다. 32GB 데이터셋 스냅샷 시 RSS 증가가 100MB 수준.
❸ Redis 7.x API 호환
String, List, Hash, Set, ZSet, Streams, JSON, Bitmap, HyperLogLog, Geo 모두 지원. Lua, MULTI/EXEC, Pub/Sub, Cluster, Sentinel 호환.
❹ 메모리 효율
Dashtable 의 캐시 친화적 구조로 동일 데이터셋이 Redis 대비 30~40% 적은 메모리를 사용합니다.
❺ Memcached 프로토콜도 지원
같은 인스턴스가 Redis(6379)와 Memcached(11211) 두 프로토콜을 동시에 서비스합니다. 이 부분은 의외로 강력합니다.

Ⅲ. DragonflyDB 동작 방식

ⅰ. 구성 요소

DragonflyDB 프로세스 내부는 다음과 같이 구성됩니다.

Process Layout
  └─ ProactorPool (Helio framework, io_uring/epoll)
    └─ Thread 0 — Shard 0 + ConnectionPool + Transaction Coord
    └─ Thread 1 — Shard 1 + ConnectionPool
    └─ Thread 2 — Shard 2 + ConnectionPool
    └─ ... up to N (default = nproc)
  └─ Shared : Replication log, Metrics, Auth, Cluster routing

각 스레드는 자기 샤드만 만집니다. 키는 XXHash(key) % N 으로 샤드가 결정됩니다.

 

ⅱ. 데이터 흐름 — 멀티키 트랜잭션이 핵심

여기가 DragonflyDB 의 가장 똑똑한 부분입니다. 일반적인 shared-nothing DB 는 멀티키 트랜잭션이 어렵습니다. DragonflyDB는 VLL(Very Lazy Locking) 알고리즘으로 이를 풉니다.

VLL 동작 순서 (멀티키 MGET 예시)
① 클라이언트가 MGET user:1 user:2 user:3 요청
② 코디네이터 스레드가 키 → 샤드 매핑 후 각 샤드의 "예약 큐"에 등록
③ 각 샤드가 자기 차례의 모든 키에 대해 락 없이 작업 수행
④ 결과를 코디네이터로 회신, 응답 조립 후 클라이언트 송신
⑤ 이 과정에서 스레드 간 락이 발생하지 않음

Ⅳ. DragonflyDB 구성 및 흐름도

ⅰ. 구성도

ⅱ. 단계별 설명

SET 명령 처리 흐름 (single-key)
1. 클라이언트가 RESP 프로토콜로 SET foo bar EX 60 전송
2. 워커 스레드가 파싱, key="foo" → XXHash → Shard 5
3. Shard 5 스레드의 큐에 작업 등록 (lock-free SPSC queue)
4. Shard 5 스레드가 Dashtable에 INSERT, TTL 메타데이터 기록
5. 응답을 원 워커 스레드로 회신 후 +OK 송신
6. 비동기로 Replication Log 에 추가 (replica 가 붙어있다면)
스냅샷 흐름 — Redis 와 가장 다른 부분
1. SAVE/BGSAVE 시 fork() 안 함
2. 각 샤드 스레드가 자기 Dashtable 의 세그먼트를 순회
3. 세그먼트 단위로 락(매우 짧음, 마이크로초) → snapshot 파일에 직렬화
4. 진행 중 변경은 modification log 에 동시 기록
5. 모든 세그먼트 완료 시 modification log 를 snapshot 끝에 머지
6. 결과: RSS 증가 거의 없음, write traffic 영향 미미

Ⅴ. DragonflyDB 설치 방법

ⅰ. Docker (가장 빠른 시작)

# 최소 옵션
docker run -p 6379:6379 --ulimit memlock=-1 \
  docker.dragonflydb.io/dragonflydb/dragonfly

# 프로덕션 권장 옵션
docker run -d --name dragonfly \
  -p 6379:6379 \
  --ulimit memlock=-1 \
  -v /data/dragonfly:/data \
  docker.dragonflydb.io/dragonflydb/dragonfly \
  --maxmemory=12gb \
  --proactor_threads=4 \
  --dir=/data \
  --dbfilename=dump \
  --snapshot_cron="0 */1 * * *"
트러블슈팅 - 설치 시
· "Cannot allocate memory"  --ulimit memlock=-1 옵션 빠뜨림. 반드시 추가해야 io_uring 이 동작합니다.
· io_uring not supported — 커널 5.10 미만이면 io_uring 미지원. --force_epoll 옵션으로 epoll 모드 사용 가능.
· 컨테이너 환경에서 nproc 가 호스트값으로 잡힘 — cgroup 인식이 안 됨. 명시적으로 --proactor_threads=N 지정 필수.

 

ⅱ. Kubernetes (Operator)

helm repo add dragonfly https://dragonflydb.github.io/dragonfly-operator/helm
helm install dragonfly-operator dragonfly/dragonfly-operator -n dragonfly --create-namespace

# CRD 로 인스턴스 정의
cat <<EOF | kubectl apply -f -
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
  name: cache
spec:
  replicas: 3
  resources:
    requests:
      memory: 16Gi
      cpu: 8
    limits:
      memory: 16Gi
      cpu: 8
  args:
    - --maxmemory=14gb
    - --proactor_threads=8
EOF

 

ⅲ. 바이너리(Ubuntu 24.04)

wget https://github.com/dragonflydb/dragonfly/releases/latest/download/dragonfly-x86_64.tar.gz
tar -xzf dragonfly-x86_64.tar.gz
sudo mv dragonfly-x86_64 /usr/local/bin/dragonfly
sudo setcap 'cap_ipc_lock=+ep' /usr/local/bin/dragonfly
dragonfly --version

Ⅵ. DragonflyDB 사용 방법

ⅰ. 클라이언트 코드 — 기존 Redis 클라이언트 그대로

# Python - redis-py 그대로 사용
import redis
r = redis.Redis(host='dragonfly-host', port=6379, decode_responses=True)
r.set('user:1001', 'Kim', ex=3600)
print(r.get('user:1001'))

# 파이프라인도 동일
pipe = r.pipeline()
for i in range(10000):
    pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()
# Node.js - ioredis
const Redis = require('ioredis');
const r = new Redis({ host: 'dragonfly-host', port: 6379 });

# Java - Lettuce
RedisClient client = RedisClient.create("redis://dragonfly-host:6379");
StatefulRedisConnection<String, String> connection = client.connect();

 

ⅱ. 운영 시 고려사항 

트러블슈팅 케이스 1 - 메모리 limit 가 maxmemory 보다 낮음
컨테이너 limit 16Gi 인데 --maxmemory=16gb 으로 설정 → OOMKilled. 해결: maxmemory 는 컨테이너 limit 의 80~85% 로. 16Gi 컨테이너면 --maxmemory=13gb. 스냅샷이 점진식이라 fork 폭증은 없지만 connection buffer, replication buffer 가 별도로 메모리를 씁니다.
트러블슈팅 케이스 2 - 큰 키 마이그레이션 후 P99 튐
Redis 에서 10MB 짜리 Hash 를 DUMP/RESTORE 로 옮긴 후 HGETALL 시 P99 가 50ms 까지 치솟음. 원인: 단일 키는 단일 샤드에만 존재 → 해당 코어가 포화. 해결: 큰 Hash 는 키 패턴을 분리 (hash:user:1001:profile, hash:user:1001:preferences) 하거나 압축. 또는 HSCAN 으로 청크 처리.
트러블슈팅 케이스 3 - replication lag 가 비정상적으로 큼
마스터 1대 + 레플리카 3대 구성에서 일부 레플리카만 lag. 원인: 레플리카 측 proactor_threads 가 마스터보다 적음 → 적용 속도 부족. 해결: 레플리카 = 마스터와 동일한 CPU/코어 수. DragonflyDB는 멀티스레드라 레플리카도 CPU 자원을 동일하게 줘야 합니다.
트러블슈팅 케이스 4 - Lua 스크립트가 Redis 보다 느림
일부 Lua 스크립트가 멀티키를 만지면 코디네이터 오버헤드 발생 → 단일 스레드 Redis 가 더 빠를 수 있음. 해결: 스크립트가 키 1개만 만지도록 분해, 또는 EVAL 대신 파이프라인으로 변환. DragonflyDB의 강점은 다수 클라이언트 동시 요청 이지 단일 클라이언트 Lua 가 아닙니다.
트러블슈팅 케이스 5 - KEYS * 사용 시 메모리 폭증
운영 중 실수로 KEYS * 호출 → 5천만 키 응답 직렬화 중 OOM. 해결: ACL 로 -@dangerous 적용해서 KEYS, FLUSHALL 차단. SCAN 만 허용. (Redis 도 같지만 DragonflyDB 가 데이터셋이 커서 영향이 더 큼)

Ⅶ. DragonflyDB 자주 쓰는 명령어

ⅰ. 관리자 명령

# 상태 확인 (Redis 와 동일하지만 추가 섹션이 있음)
redis-cli -h dragonfly-host INFO

# DragonflyDB 전용 - 샤드별 통계
redis-cli DEBUG OBJECT mykey         # 어느 샤드에 있는지 확인
redis-cli DFLY VERSION               # 빌드 버전
redis-cli DFLY MEMORY                # 샤드별 메모리 상세

# 점진 스냅샷 강제 실행
redis-cli SAVE
redis-cli BGSAVE                     # 비동기, 점진식

# 레플리케이션 셋업
redis-cli REPLICAOF master-host 6379
redis-cli REPLICAOF NO ONE

 

ⅱ. 실전 사례 - 캐시 워밍업

# 1000만 키 적재 - Redis 와 DragonflyDB 비교
# Redis: ~180초, CPU 1코어 100%
# DragonflyDB (8코어): ~12초, CPU 8코어 80%

redis-cli --pipe < warmup.cmds

# 또는 memtier_benchmark
memtier_benchmark -s dragonfly-host -p 6379 \
  --ratio=1:0 \
  --data-size=256 \
  --key-pattern=P:P \
  --key-maximum=10000000 \
  -t 16 -c 50 --pipeline=20

 

ⅲ. 실전 사례 - Redis 에서 마이그레이션

# 방법 1: DragonflyDB 를 Redis 의 replica 로 붙임
# Dragonfly 측에서
redis-cli REPLICAOF redis-master 6379

# 동기화 완료 후 promote
redis-cli REPLICAOF NO ONE

# 방법 2: RDB 파일 직접 import
# Redis 에서 BGSAVE 후 dump.rdb 를 Dragonfly --dir 로 복사
docker run -p 6379:6379 \
  -v $(pwd)/dump.rdb:/data/dump.rdb \
  docker.dragonflydb.io/dragonflydb/dragonfly --dir=/data
마이그레이션 함정
· Redis 모듈(RedisJSON, RedisBloom, RediSearch)은 호환 안 됨 → DragonflyDB 자체 구현(JSON.SET 등) 만 가능, 일부 명령은 미지원
· Cluster slot 은 호환되지만 슬롯 마이그레이션 도구는 별도 (Dragonfly Operator 의 cluster mode 사용)
· AOF 미지원 — DragonflyDB 는 RDB 스타일 점진 스냅샷만 지원. AOF 의존도가 높다면 검토 필요
· CLIENT KILL TYPE master 등 일부 디버깅 명령 미지원

Ⅷ. DragonflyDB 활용 방안

ⅰ. 대안 기술 비교

· 강점: 멀티스레드, 수직 확장, 점진 스냅샷, Memcached 동시 지원
· 약점: Redis 모듈 미지원, AOF 미지원, Pub/Sub 패턴 매칭 일부 차이
· 언제: 단일 노드 처리량이 부족할 때, 16GB+ 데이터셋, 캐시/세션 스토어
 
· 강점: 가장 성숙한 생태계, 모든 클라이언트/툴 완벽 호환, RedisJSON/Search 모듈
· 약점: 단일 스레드 (IO 스레드는 있음), fork 기반 스냅샷, 수평 확장만 가능
· 언제: 작은~중간 데이터셋, 모듈 기반 기능 사용, 안정성 최우선
 
· 강점: Redis 코드베이스에서 멀티스레드 패치, 호환성 매우 높음
· 약점: 개발 정체 (Snap 인수 후), shared-everything 모델로 락 경합 존재
· 언제: Redis 호환성이 critical 한데 코어를 좀 더 쓰고 싶을 때 (단, 개발 활성도 확인 필수)
 
· 강점: .NET 기반 멀티스레드, Tsavorite 스토리지로 메모리 + 디스크 티어링
· 약점: 생태계 초기, 운영 경험 적음, .NET 의존
· 언제: Windows/.NET 스택, 메모리보다 큰 데이터셋
Memcached
· 강점: 극단적으로 단순, 멀티스레드, LRU 캐시에 특화
· 약점: 자료구조 없음, 영속성 없음, Pub/Sub 없음
· 언제: 순수 KV 캐시만 필요. DragonflyDB가 같은 프로토콜을 지원하므로 통합 가능

 

ⅱ. 언제 쓰면 안 되는가

DragonflyDB 가 적합하지 않은 경우
· RedisJSON / RediSearch / RedisBloom 등 모듈에 의존하는 시스템 — DragonflyDB 의 자체 구현은 호환 부분만 있고 고급 기능은 미지원입니다.
· AOF 기반 강한 내구성 요구 — 금융 거래 원장 등 1초도 잃을 수 없는 데이터. (애초에 Redis도 부적합하지만, DragonflyDB는 AOF 가 없어 더 명확히 부적합)
· 데이터셋이 1GB 미만 — 멀티스레드 이점이 거의 없음. Redis 가 더 단순하고 빠를 수 있음.
· 이미 Redis Cluster 가 잘 동작 중이고 운영 성숙도가 높을 때 — 굳이 마이그레이션 리스크를 질 이유가 없습니다.
· 매우 큰 단일 키 위주의 워크로드 — 단일 키는 단일 샤드에 갇혀 멀티스레드 이점을 못 봅니다.

 

ⅲ. 최적 활용 시나리오

DragonflyDB가 가장 빛나는 곳
· 대규모 세션 스토어 — 수천만 동시 세션, 키 분포 균등
· API 응답 캐시 — CDN 백엔드, GraphQL 캐시 등 다수 클라이언트 동시 요청
· 실시간 리더보드 — Sorted Set 으로 수억 사용자 랭킹
· 피처 스토어 (ML) — 추론 시점에 수백만 피처를 ms 단위로 조회
· Rate Limiting — 초당 수십만 요청 카운팅
· Redis Cluster 단순화 — 6노드 Cluster 를 단일 노드로 통합 후 운영 부담 감소
반응형