Computer >> 컴퓨터 >  >> 프로그래밍 >> 데이터베이스

Redis를 제대로 활용하기 위한 10가지 핵심 팁

Redis를 제대로 활용하기 위한 10가지 핵심 팁

Redis는 현재 기술 업계에서 가장 주목받는 인메모리 데이터 저장소입니다. Antirez의 작은 개인 프로젝트에서 시작해 이제는 업계 표준으로 자리 잡았습니다. 그만큼 Redis를 올바르게 사용하기 위한 검증된 모범 사례들도 함께 발전해 왔는데요. 이 글에서는 Redis를 효율적으로 활용할 수 있는 10가지 핵심 팁을 소개합니다.

1. KEYS * 사용을 당장 멈추세요

글의 시작부터 외치는 것은 좋지 않지만, 이 부분만큼은 반드시 강조하고 싶습니다. Redis 인스턴스를 점검하다 보면 commandstats에서 눈에 띄는 KEYS 명령어를 종종 발견하게 됩니다. 프로그래밍 관점에서는 다음과 같은 의사 코드가 자연스러워 보일 수 있습니다.

for key in 'keys *':
  doAllTheThings()

하지만 키가 1,300만 개에 달한다면 이야기가 달라집니다. KEYS 명령어는 O(n) 복잡도를 가지며, n은 반환되는 키의 개수입니다. 즉, dbsize에 따라 성능이 좌우되고, 해당 작업이 실행되는 동안에는 인스턴스에서 다른 어떤 명령도 처리할 수 없습니다.

대안으로 SCAN 명령어를 사용하세요. SCAN은 데이터베이스를 증분 방식으로 순회할 수 있게 해주며, 커서 기반 반복자로 동작하기 때문에 필요할 때마다 멈추거나 재개할 수 있습니다.

2. Redis를 느리게 만드는 범인 찾기

Redis는 로그가 그리 상세하지 않아 인스턴스 내부에서 무슨 일이 일어나는지 추적하기 어려울 때가 많습니다. 다행히 Redis는 commandstats 유틸리티를 통해 이를 확인할 수 있게 해줍니다.

127.0.0.1:6379> INFO commandstats
# Commandstats
cmdstat_get:calls=78,usec=608,usec_per_call=7.79
cmdstat_setex:calls=5,usec=71,usec_per_call=14.20
cmdstat_keys:calls=2,usec=42,usec_per_call=21.00
cmdstat_info:calls=10,usec=1931,usec_per_call=193.10

이 출력 결과를 통해 각 명령어별 실행 횟수와 실행에 걸린 마이크로초(총합 및 호출당 평균)를 확인할 수 있습니다. 통계를 초기화하고 싶다면 CONFIG RESETSTAT 명령어를 실행하면 깨끗한 새 출발점을 얻을 수 있습니다.

3. Redis-Benchmark는 기준선일 뿐, 절대적인 진실이 아니다

Redis 창시자 살바토레(Salvatore)는 이렇게 말했습니다. "GET/SET 테스트만으로 Redis를 평가하는 것은, 비 오는 날 페라리가 사이드미러를 얼마나 잘 닦는지 시험하는 것과 같다." 많은 개발자들이 Redis-Benchmark 결과가 기대에 못 미친다고 질문해오곤 하는데, 결과를 해석할 때는 다음과 같은 요소들을 고려해야 합니다.

  • 클라이언트 측에 병목이 있었는가?
  • 버전 차이는 없었는가?
  • 수행된 테스트가 실제 애플리케이션의 워크로드와 관련성이 있는가?

Redis-Benchmark는 redis-server가 비정상적으로 동작하지 않는지 확인하는 훌륭한 기준선이 되어주지만, 결코 진정한 의미의 "부하 테스트"로 받아들여져서는 안 됩니다. 부하 테스트는 실제 애플리케이션의 동작 방식을 반영해야 하며, 가능한 한 프로덕션 환경과 유사한 조건에서 수행해야 합니다.

4. 해시(Hash)가 최고의 친구입니다

해시를 적극적으로 활용해 보세요. 해시에게 기회를 준다면 그 편리함에 놀라게 될 것입니다. 다음과 같은 키 구조는 너무나 흔하게 볼 수 있습니다.

foo:first_name
foo:last_name
foo:address

위 예제에서 foo는 사용자 이름이라고 가정할 때, 각 속성이 별도의 키로 저장됩니다. 이런 구조는 오류 가능성을 높이고 불필요한 키를 계속 늘립니다. 대신 해시를 사용하면 단 하나의 키로 모든 정보를 관리할 수 있습니다.

127.0.0.1:6379> HSET foo first_name "Joe"
(integer) 1
127.0.0.1:6379> HSET foo last_name "Engel"
(integer) 1
127.0.0.1:6379> HSET foo address "1 Fanatical Pl"
(integer) 1
127.0.0.1:6379> HGETALL foo
1) "first_name"
2) "Joe"
3) "last_name"
4) "Engel"
5) "address"
6) "1 Fanatical Pl"
127.0.0.1:6379> HGET foo first_name
"Joe"

5. TTL 설정을 생활화하세요

가능한 한 만료 시간(TTL)이 있는 키를 활용하세요. 대표적인 예가 임시 인증 키 저장입니다. OAUTH를 예로 들면, 인증 키를 발급받을 때 대개 만료 시간이 함께 제공됩니다. 키를 저장할 때 동일한 만료 시간을 설정하면 Redis가 알아서 정리해 줍니다. 더 이상 KEYS *로 키를 일일이 순회하며 삭제할 필요가 없겠죠?

6. 적절한 축출(Eviction) 정책 선택하기

키 정리와 관련해서 축출 정책도 짚고 넘어가야 합니다. Redis 인스턴스가 가득 차면 Redis는 키를 축출하려 시도합니다. 사용 사례에 따라 다르지만, 만료 키가 있다면 volatile-lru를 권장합니다. 캐시 용도로 사용 중이면서 만료 설정이 없다면 allkeys-lru를 고려해볼 수 있습니다. 각 옵션의 특징을 미리 확인해두는 것이 좋습니다.

7. 중요한 데이터라면 try/except를 활용하세요

데이터가 반드시 Redis에 저장되어야 한다면 try/except 처리를 강력히 권장합니다. 대부분의 Redis 클라이언트는 "fire-and-forget" 방식으로 설정되어 있기 때문에, 키가 실제로 데이터베이스에 저장되지 못하는 경우를 항상 염두에 두어야 합니다. 이 경우 추가되는 코드 복잡성은 거의 없으면서도, 중요한 데이터가 제자리에 안전하게 도달하는 것을 보장할 수 있습니다.

8. 하나의 인스턴스에 몰아주지 마세요

가능하면 여러 Redis 인스턴스에 워크로드를 분산하세요. 버전 3.0.0부터 Redis Cluster를 사용할 수 있습니다. Redis Cluster는 키 범위를 기준으로 지정된 마스터/슬레이브 그룹에 키를 분배할 수 있게 해줍니다. 클러스터링이 어려운 상황이라면 네임스페이싱(namespace)을 통해 여러 인스턴스에 키를 분산하는 방법도 고려해보세요. redis.io 공식 사이트에서 데이터 파티셔닝에 대한 훌륭한 문서를 참고할 수 있습니다.

9. 코어가 많으면 무조건 좋다? 아니다!

Redis는 싱글 스레드 프로세스이며, 영속성(persistence)이 활성화된 경우에도 최대 두 개의 코어만 사용합니다. 같은 호스트에서 여러 인스턴스를 실행할 계획이 아니라면—그런 경우는 개발·테스트 환경 정도겠죠!—Redis 인스턴스에 두 개 이상의 코어는 필요 없습니다.

10. 고가용성(HA) 구성은 필수!

Redis Sentinel은 이제 충분히 검증되었으며, 많은 사용자가 프로덕션 환경에서 운영 중입니다(ObjectRocket도 포함!). 애플리케이션이 Redis에 크게 의존하고 있다면 서비스 중단을 방지하기 위한 HA(고가용성) 솔루션 도입을 반드시 고려해야 합니다. 물론 직접 관리하고 싶지 않다면 ObjectRocket처럼 24x7 지원을 제공하는 HA 플랫폼을 이용해보는 것도 좋은 선택입니다.