Redis EXPIRE 및 TTL 명령어 사용 모범 사례
캐시, 세션, 속도 제한, 잠금 및 메모리 정리를 위해 Redis EXPIRE와 TTL을 안전하게 사용하세요.
Redis EXPIRE 및 TTL 명령어 사용 모범 사례
Redis는 강력한 인메모리 데이터 구조 저장소로, 종종 캐시, 메시지 브로커 및 데이터베이스로 활용됩니다. Redis 내에서 데이터 수명을 효과적으로 관리하는 것은 성능 최적화, 메모리 고갈 방지 및 강력한 캐싱 전략 구현에 매우 중요합니다. EXPIRE 및 TTL(그리고 밀리초 단위 대응 명령어인 PEXPIRE 및 PTTL) 명령어는 이러한 데이터 만료 제어를 달성하기 위한 기본 도구입니다.
임시 데이터에 만료를 설정하지 않으면 Redis는 오래된 캐시 항목, 오래된 세션 및 버려진 잠금 키로 메모리를 조용히 채울 수 있습니다. 만료를 의도적으로 사용하고, 키가 생성될 때 원자적으로 설정하세요.
Redis 만료 명령어 이해하기
Redis는 키에 TTL(Time To Live)을 설정하는 명령어를 제공하며, TTL이 지나면 키가 자동으로 삭제됩니다. 이 자동 삭제는 특히 캐싱 시나리오에서 메모리 관리와 데이터 신선도 유지에 필수적입니다.
EXPIRE 및 PEXPIRE 명령어
이 명령어들은 키에 타임아웃을 설정합니다. 타임아웃에 도달하면 키가 자동으로 삭제됩니다. 주요 차이점은 시간 단위에 있습니다:
EXPIRE key seconds: 만료 시간을 초 단위로 설정합니다.PEXPIRE key milliseconds: 만료 시간을 밀리초 단위로 설정합니다.
PEXPIRE를 사용하면 더 세밀한 제어가 가능하며, 시간에 민감한 캐싱이나 매우 짧은 특정 간격 내에 데이터를 만료시켜야 할 때 유용합니다.
예시:
# 'mykey' 키를 60초 후에 만료되도록 설정
redis-cli> EXPIRE mykey 60
(integer) 1
# 'anotherkey' 키를 500밀리초 후에 만료되도록 설정
redis-cli> PEXPIRE anotherkey 500
(integer) 1
반환 값:
1: 타임아웃이 성공적으로 설정되었습니다.0: 키가 존재하지 않습니다.
EXPIREAT 및 PEXPIREAT 명령어
이 명령어들은 EXPIRE 및 PEXPIRE와 유사하지만, 기간을 설정하는 대신 키가 만료되어야 하는 특정 절대 시간을 설정합니다.
EXPIREAT key timestamp: 특정 Unix 타임스탬프(에포크 이후 초)로 만료를 설정합니다.PEXPIREAT key millitimestamp: 특정 Unix 타임스탬프(밀리초) 로 만료를 설정합니다.
이는 설정된 시간과 관계없이 특정 벽시계 시간에 항목이 만료되도록 하려는 경우 유용합니다.
예시:
# 'session:123' 키를 Unix 타임스탬프 1678886400(2023년 3월 15일 12:00:00 UTC)에 만료되도록 설정
redis-cli> EXPIREAT session:123 1678886400
(integer) 1
TTL 및 PTTL 명령어
이 명령어들은 키의 남은 수명을 반환합니다. 이는 키의 만료를 모니터링하고 남은 시간에 의존하는 로직을 구현하는 데 중요합니다.
TTL key: 키의 남은 수명을 초 단위로 반환합니다.PTTL key: 키의 남은 수명을 밀리초 단위로 반환합니다.
반환 값:
- 양의 정수:
TTL의 경우 초,PTTL의 경우 밀리초 단위의 남은 수명입니다. -1: 키가 존재하지만 연결된 만료 시간이 없습니다.-2: 키가 존재하지 않습니다.
예시:
redis-cli> TTL mykey
(integer) 55
redis-cli> PTTL anotherkey
(integer) 480
redis-cli> TTL non_existent_key
(integer) -2
redis-cli> SET permanent_key "some value"
OK
redis-cli> TTL permanent_key
(integer) -1
EXPIRE 및 TTL 사용 모범 사례
이 명령어들을 효과적으로 활용하려면 캐싱 및 데이터 관리에 대한 전략적 접근 방식이 필요합니다. 다음은 주요 모범 사례입니다:
1. 캐시에 대해 적극적으로 만료 설정하기
캐시 역할을 하는 데이터의 경우 거의 항상 만료 시간을 설정하는 것이 좋습니다. 이렇게 하면 오래된 데이터가 무기한 유지되지 않습니다. 핵심은 캐시 적중률과 데이터 신선도의 균형을 맞추는 만료 시간을 선택하는 것입니다.
- 캐시 무효화: 만료는 자동 캐시 무효화의 한 형태로 작동합니다. 캐시 항목이 만료되면 애플리케이션이 기본 소스에서 새 데이터를 다시 가져와 캐시를 업데이트할 수 있습니다.
- 메모리 관리: 캐시가 무한정 커지는 것을 방지하여 메모리 고갈 및 성능 저하를 예방합니다.
예시: 사용자 프로필을 5분 동안 캐싱합니다.
import redis
import time
r = redis.Redis(decode_responses=True)
def get_user_profile(user_id):
cache_key = f"user_profile:{user_id}"
profile_data = r.get(cache_key)
if profile_data:
print(f"사용자 {user_id}에 대한 캐시 적중")
return profile_data
else:
print(f"사용자 {user_id}에 대한 캐시 미스. DB에서 가져오는 중...")
# 데이터베이스에서 가져오는 것을 시뮬레이션
user_profile = {"name": "Alice", "email": "[email protected]"}
# 5분(300초) 만료 시간으로 Redis에 저장
r.set(cache_key, str(user_profile), ex=300)
return user_profile
# 첫 번째 호출 (캐시 미스)
print(get_user_profile(123))
# 두 번째 호출 (캐시 적중)
print(get_user_profile(123))
# 잠시 기다리지만 만료 시간보다는 짧게
time.sleep(10)
print(f"캐시 키의 TTL: {r.ttl(cache_key)} 초")
# 만료 시간까지 기다림
time.sleep(300) # 나머지 5분을 시뮬레이션
print(f"만료 후 TTL: {r.ttl(cache_key)} 초")
2. 고빈도/단기 데이터에 PEXPIRE 사용하기
속도 제한, 매우 짧은 유효 기간의 세션 토큰 또는 임시 잠금과 같은 시나리오에서는 밀리초 단위의 정밀도가 중요할 수 있습니다. PEXPIRE는 훨씬 더 세밀한 제어를 가능하게 합니다.
예시: 간단한 속도 제한기 구현.
import redis
import time
r = redis.Redis(decode_responses=True)
def check_rate_limit(user_id, limit=5, period_ms=60000): # 분당 5회 요청
key = f"rate_limit:{user_id}"
current_requests = r.get(key)
if current_requests is None:
# 이 기간의 첫 번째 요청
r.set(key, 1, px=period_ms)
return True
else:
current_requests = int(current_requests)
if current_requests < limit:
# INCR은 기존 TTL을 유지합니다.
r.incr(key)
return True
else:
# 제한 초과
return False
# 사용자에 대한 요청 시뮬레이션
user = "user:abc"
for i in range(7):
if check_rate_limit(user):
print(f"요청 {i+1}: 허용됨. 남은 TTL: {r.pttl(f'rate_limit:{user}')}ms")
else:
print(f"요청 {i+1}: 속도 제한 초과.")
time.sleep(0.1) # 요청 사이에 약간의 시간 시뮬레이션
3. 시간 기반 이벤트에 EXPIREAT 사용하기
특정 달력 시간(예: 프로모션 종료, 로그인 시간에 고정 기간을 더한 세션 만료)에 데이터가 만료되어야 하는 경우, 기간을 계산하는 것보다 EXPIREAT가 더 적합합니다.
예시: 특별 혜택을 고정 시간에 만료시키기.
import redis
import datetime
r = redis.Redis(decode_responses=True)
offer_id = "SUMMER2026"
end_time = datetime.datetime(2023, 8, 31, 23, 59, 59)
# Unix 타임스탬프로 변환
end_timestamp = int(end_time.timestamp())
# Redis에 혜택 세부 정보 저장 및 만료 설정
r.set(f"offer:{offer_id}", "모든 품목 20% 할인!")
r.expireat(f"offer:{offer_id}", end_timestamp)
print(f"혜택 '{offer_id}'이(가) {end_time}(타임스탬프: {end_timestamp})에 만료되도록 설정됨")
print(f"혜택의 현재 TTL: {r.ttl(f'offer:{offer_id}')} 초")
4. 만료 시간이 없는 키에 주의하기
명시적인 EXPIRE, PEXPIRE, EXPIREAT 또는 PEXPIREAT 명령어 없이 설정된 키는 명시적으로 삭제되거나 Redis 서버가 다시 시작될 때까지(지속성이 구성되지 않은 경우) 무기한 유지됩니다. 이는 메모리 문제로 이어질 수 있습니다.
- 영구 데이터: 데이터를 영구적으로 유지하려는 경우 실수로 만료 시간이 할당되지 않았는지 확인하십시오. 반대로, 데이터가 만료되어야 하지만 만료 설정을 잊은 경우 계속 유지됩니다.
- 모니터링: Redis 메모리 사용량과 키 개수를 정기적으로 모니터링하십시오.
INFO memory및redis-cli --stat명령어나 Redis Enterprise의 UI와 같은 도구를 사용하여 만료 시간 없이 과도한 메모리를 소비할 수 있는 키를 식별하십시오.
5. PERSIST를 사용하여 만료 제거하기
키에 만료 시간을 설정했지만 나중에 영구적으로 유지하기로 결정한 경우 PERSIST 명령어를 사용하십시오.
예시:
redis-cli> SET temp_key "data"
OK
redis-cli> EXPIRE temp_key 300
(integer) 1
redis-cli> TTL temp_key
(integer) 295
redis-cli> PERSIST temp_key
(integer) 1
redis-cli> TTL temp_key
(integer) -1
6. 원자성: SET과 만료 결합하기
만료 시간이 있어야 하는 새 키를 설정할 때는 SET 명령어 다음에 EXPIRE를 실행하는 것보다 SET 명령어에 EX 또는 PX 옵션을 사용하는 것이 더 효율적이고 원자적입니다.
SET key value EX seconds: 키의 값과 초 단위 만료 시간을 설정합니다.SET key value PX milliseconds: 키의 값과 밀리초 단위 만료 시간을 설정합니다.
이는 원자적이므로 작업이 완전히 성공하거나 완전히 실패하여 SET과 EXPIRE 명령어 사이에 EXPIRE 명령어가 실패하거나 누락될 수 있는 경쟁 조건을 방지합니다.
예시:
# 대신:
# redis-cli> SET mycache "some value"
# redis-cli> EXPIRE mycache 3600
# 사용:
redis-cli> SET mycache "some value" EX 3600
OK
# 또는 밀리초의 경우:
redis-cli> SET anothercache "other value" PX 500
OK
7. 만료와 함께 SETNX 고려하기
분산 잠금 또는 키가 이미 존재하지 않는 경우에만 값을 설정하기 위해 SETNX(Set if Not Exists)를 사용하는 경우, 교착 상태를 방지하기 위해 만료와 결합해야 합니다.
SET key value NX EX seconds:key가 존재하지 않는 경우에만key를value로 설정하고 지정된 만료 시간(초)을 설정합니다.SET key value NX PX milliseconds: 유사하지만 밀리초 단위입니다.
이는 분산 잠금을 구현하는 일반적인 패턴입니다.
예시: 분산 잠금 획득.
# 리소스 'resource_X'에 대한 잠금을 10초 동안 획득 시도
redis-cli> SET lock:resource_X "process_abc" NX EX 10
OK
# 위 명령어가 'OK'를 반환하면 잠금을 획득한 것입니다.
# 'nil'(또는 일부 클라이언트에서는 빈 문자열)을 반환하면 잠금이 이미 유지되고 있는 것입니다.
# 잠금 해제 (주의해서, 일반적으로 삭제 전에 값을 확인하는 Lua 스크립트 사용)
# redis-cli> DEL lock:resource_X
8. 만료 이벤트 모니터링하기
Redis에는 만료된 키를 정리하는 메커니즘인 지연 만료와 능동 만료가 있습니다.
- 지연 만료: 키는 명령어(예:
GET,TTL)로 액세스될 때만 만료 여부를 확인합니다. 이것은 가장 일반적이고 리소스 효율적인 방법입니다. - 능동 만료: Redis는 주기적으로 TTL이 있는 키를 샘플링하고 액세스되지 않더라도 만료된 키를 삭제합니다. 이는 메모리를 더 적극적으로 회수하는 데 도움이 됩니다.
서버 구성 없이 능동 만료 빈도를 직접 제어할 수는 없지만, TTL 및 PTTL을 사용하여 키의 상태를 확인하고 애플리케이션 로직이 만료된 데이터를 올바르게 처리하는지 확인할 수 있습니다.
핵심 요약
임시 키를 생성할 때는 SET ... EX 또는 SET ... PX를 사용하고, 디버깅할 때는 TTL을 확인하며, TTL = -1인 키는 의도적으로 영구적인 경우가 아니라면 정리 대상으로 간주하세요.