데이터 작업 구현
Azure Managed Redis는 데이터 작업에 대한 표준 Redis 명령을 지원합니다. 이 단원에서는 redis Python 라이브러리를 사용하여 데이터를 저장 및 검색하고, 만료 시간을 설정하고, 캐시 무효화를 관리하는 방법을 설명합니다.
Redis는 간단한 문자열 이외의 여러 데이터 형식을 지원하며 각각 다른 사용 사례에 최적화되어 있습니다. 이러한 데이터 구조를 이해하면 애플리케이션 데이터를 저장하고 검색하기 위한 올바른 방법을 선택할 수 있습니다.
| 데이터 형식 | Operations |
|---|---|
| 문자열 작업 | 문자열 작업(SET, GET, MSET) MGET은 텍스트 또는 직렬화된 데이터에서 작동합니다. 연결에서 사용하는 decode_responses=True 경우 Redis는 바이트 응답을 문자열로 자동으로 변환하므로 텍스트 데이터를 더 쉽게 사용할 수 있습니다. 이진 데이터인 이미지 또는 피클된 Python 객체의 경우, decode_responses=False를 원시 바이트로 처리하도록 설정합니다. |
| 해시 연산 | Redis Hashes는 개체를 단일 키 내에 필드-값 쌍으로 저장하며 사용자 프로필 또는 제품 세부 정보와 같은 구조화된 데이터를 나타내는 데 적합합니다. 필드를 저장하고, HSET 개별 필드를 검색하고HGET, 모든 필드를 한 번에 가져오는 데 사용합니다HGETALL. 해시는 각 필드를 별도의 키로 저장하는 것보다 메모리 효율적입니다. |
| 작업 나열 | Redis 목록은 큐, 활동 피드 또는 최근 항목에 적합한 정렬된 문자열 컬렉션입니다. 양쪽 끝에 항목을 추가하고,LPUSH/RPUSH항목을 제거 및 반환하고LPOP, 범위를 검색하는 데 사용합니다./RPOPLRANGE 목록은 스택(LIFO) 및 FIFO(큐) 패턴을 모두 지원합니다. |
| 숫자 연산 | Redis는 숫자 문자열 값에서 작동하는 원자성 증가 및 감소 작업(INCR, DECR, INCRBY, DECRBY)을 제공합니다. 이러한 작업은 원자성이므로 경합 조건이 없는 카운터, 속도 제한 및 분산 계산 시나리오에 이상적입니다. 값이 문자열로 저장되더라도 Redis는 이러한 작업에 대한 정수로 처리합니다. |
redis-py 라이브러리를 사용하여 Azure Managed Redis에 연결
데이터 작업을 수행하기 전에 Azure Managed Redis 인스턴스에 대한 연결을 설정합니다. 라이브러리는 redis-py 동기 및 비동기 작업을 모두 지원합니다.
Azure Cache for Redis와 Azure Managed Redis는 모두 애플리케이션 개발에 동일한 라이브러리를 사용합니다. 그러나 기본 연결 포트는 서로 다릅니다.
- Azure Managed Redis 및 Enterprise 캐시용 포트: 10000
- Azure Cache for Redis 인스턴스에 대한 포트: 6380
비고
Azure Managed Redis 인스턴스에 대해 선택한 클러스터링 정책은 연결 방법에 영향을 줍니다. 다음 예제는 엔터프라이즈 클러스터링 및 비클러스터형 구성에서 작동합니다. OSS 클러스터링 정책을 사용하는 인스턴스에서 연결 시 redis.Redis 대신 redis.cluster.RedisCluster를 사용해야 합니다.
import redis
# Create a Redis client
r = redis.Redis(
host='your-redis-instance',
port=10000,
ssl=True,
decode_responses=True, # Convert byte responses to strings automatically
password='your-access-key'
)
프로덕션 환경의 경우 보안을 강화하기 위해 redis-entraid 패키지와 함께 Microsoft Entra ID 인증을 사용하는 것이 좋습니다.
import redis
from azure.identity import DefaultAzureCredential
from redis_entraid.cred_provider import create_from_default_azure_credential
# Create credential provider using DefaultAzureCredential
credential_provider = create_from_default_azure_credential(
("https://redis.azure.com/.default",),
)
# Create a Redis client with Azure Entra ID authentication
r = redis.Redis(
host='your-redis-instance',
port=10000,
ssl=True,
decode_responses=True, # Convert byte responses to strings automatically
credential_provider=credential_provider
)
비고
이 단원의 나머지 코드 예제는 모두 Redis 연결이 제자리에 있다고 가정합니다. 예를 들어 r.set('user:1001:name', 'Alice Smith')에서 "r"은 연결을 나타냅니다.
기본 데이터 작업
Redis는 키-값 데이터를 저장, 검색 및 관리하기 위한 간단하면서도 강력한 명령을 제공합니다.
작업 설정 및 가져오기
Redis에서 가장 기본적인 작업은 SET 및 GET 명령을 사용하여 문자열 값을 저장하고 검색하는 것입니다.
# Store a value
result = r.set('user:1001:name', 'Alice Smith')
print(f"SET operation successful: {result}") # Returns True
# Retrieve the value
name = r.get('user:1001:name')
print(f"Retrieved name: {name}") # Output: Alice Smith
해시는 단일 키 내에 여러 필드 값 쌍을 저장하므로 사용자 프로필 또는 캐시된 모델 결과와 같은 구조화된 데이터에 이상적입니다.
# Store multiple fields in a hash
r.hset('user:1001', mapping={
'name': 'Alice Smith',
'email': 'alice@example.com',
'age': '28'
})
# Retrieve a single field
name = r.hget('user:1001', 'name')
print(f"Name: {name}") # Output: Alice Smith
# Retrieve all fields
user_data = r.hgetall('user:1001')
print(f"User data: {user_data}") # Returns dict with all fields
여러 키 연산
여러 문자열 키를 동시에 작업할 때, 더 나은 성능을 위해 MSET와 MGET을 사용하세요.
# Set multiple keys at once
r.mset({
'user:1001:name': 'Alice Smith',
'user:1001:email': 'alice@example.com',
'user:1001:age': '28'
})
# Get multiple values at once
values = r.mget('user:1001:name', 'user:1001:email', 'user:1001:age')
print(f"User data: {values}") # Returns list of values
해시 작업의 경우 여러 키 작업에는 여러 해시 키(한 해시 내의 여러 필드가 아님)로 작업하는 작업이 포함됩니다.
# Using pipeline for multiple hash operations (efficient)
pipe = r.pipeline()
pipe.hgetall('user:1001')
pipe.hgetall('user:1002')
results = pipe.execute()
# Returns list of dicts, one for each hash
# Or retrieve specific fields from multiple hashes
pipe = r.pipeline()
pipe.hget('user:1001', 'name')
pipe.hget('user:1002', 'name')
names = pipe.execute()
# Returns list of names: ['Alice Smith', 'Bob Jones', 'Carol White']
키 존재 확인
데이터를 검색하거나 업데이트하기 전에 키가 있는지 확인합니다. 이 EXISTS 명령은 포함된 데이터 형식이 아니라 키의 존재를 확인하므로 모든 Redis 데이터 형식에서 작동합니다.
# Check if a key exists
if r.exists('user:1001:name'):
print("Key exists")
# Check multiple keys
count = r.exists('user:1001:name', 'user:1001:email', 'user:9999:name')
print(f"Number of existing keys: {count}") # Returns 2
삭제 작업
데이터가 더 이상 필요하지 않거나 부실해지면 캐시에서 키를 제거합니다. 이 DELETE 명령은 모든 Redis 데이터 형식에서 작동하여 전체 키와 연결된 데이터를 제거합니다.
# Delete a single key
result = r.delete('user:1001:name')
print(f"Keys deleted: {result}") # Returns count of deleted keys
# Delete multiple keys
result = r.delete('user:1001:email', 'user:1001:age')
print(f"Keys deleted: {result}")
만료 작업
키에 만료 시간을 설정하는 것은 자동 캐시 무효화 및 메모리 관리에 매우 중요합니다. 만료 명령(EXPIRE, SETEX등 TTL)은 값이 아닌 키에서 작동하므로 모든 Redis 데이터 형식에서 작동합니다.
만료를 사용하여 값 설정
단일 원자성 작업에서 문자열 값 및 만료 시간을 설정하는 데 사용합니다 SETEX . 해시 또는 목록과 같은 다른 데이터 형식의 경우 먼저 데이터를 설정한 다음 만료를 추가하는 데 사용합니다 EXPIRE .
# Set a key with 60-second expiration
r.setex('session:abc123', 60, 'user_data')
# Alternative: Set value with expiration in milliseconds
r.psetex('temp:data', 5000, 'temporary_value') # Expires in 5000ms
기존 키에 만료 추가
캐시에 이미 있는 키에 만료를 적용합니다. 이러한 명령(EXPIRE, PEXPIRE, EXPIREAT)은 모든 Redis 데이터 형식에서 작동합니다.
# Set a key without expiration
r.set('user:1002:preferences', 'dark_mode')
# Add 3600-second (1 hour) expiration
r.expire('user:1002:preferences', 3600)
# Set expiration in milliseconds
r.pexpire('user:1002:preferences', 3600000)
# Set expiration at specific Unix timestamp
import time
expire_at = int(time.time()) + 7200 # 2 hours from now
r.expireat('user:1002:preferences', expire_at)
TTL 확인 및 관리
키의 TTL(Time-to-Live)을 모니터링하여 만료 시기를 파악합니다. 이러한 명령(TTL, PTTL, PERSIST)은 키 수준 만료 설정을 쿼리하거나 수정하기 때문에 모든 Redis 데이터 형식에서 작동합니다.
# Get TTL in seconds
ttl = r.ttl('user:1002:preferences')
if ttl == -1:
print("Key exists but has no expiration")
elif ttl == -2:
print("Key does not exist")
else:
print(f"Key expires in {ttl} seconds")
# Get TTL in milliseconds
ttl_ms = r.pttl('user:1002:preferences')
print(f"Key expires in {ttl_ms} milliseconds")
# Remove expiration (persist the key)
r.persist('user:1002:preferences')
캐시 무효화 패턴
효과적인 캐시 무효화는 애플리케이션이 최적의 성능을 유지하면서 새 데이터를 제공합니다. 이러한 패턴은 모든 Redis 데이터 형식에서 작동합니다.
시간 기반 무효화
가장 간단한 방법은 TTL을 사용하여 설정된 기간 후에 캐시된 데이터를 자동으로 만료합니다.
def cache_database_query(query_key, query_function, ttl=300):
"""
Cache database query results with automatic expiration.
Args:
query_key: Cache key for the query
query_function: Function that executes the database query
ttl: Time to live in seconds (default: 5 minutes)
"""
# Check if result is cached
cached_result = r.get(query_key)
if cached_result:
print("Cache hit")
return cached_result
# Execute query and cache result
print("Cache miss - executing query")
result = query_function()
r.setex(query_key, ttl, result)
return result
# Usage example
def get_product_details():
# Simulate database query
return "Product: Laptop, Price: $999"
product_data = cache_database_query('product:12345', get_product_details, ttl=600)
업데이트에 대한 수동 무효화
데이터가 변경되면 일관성을 유지하기 위해 캐시된 버전을 명시적으로 제거하거나 업데이트합니다.
def update_user_profile(user_id, new_data):
"""
Update user profile and invalidate related cache entries.
"""
# Update database (simulated)
save_to_database(user_id, new_data)
# Invalidate all related cache keys
cache_keys = [
f'user:{user_id}:profile',
f'user:{user_id}:summary',
f'dashboard:user:{user_id}'
]
deleted = r.delete(*cache_keys)
print(f"Invalidated {deleted} cache entries")
def save_to_database(user_id, data):
# Placeholder for database operation
pass
만료가 있는 캐시 어사이드 패턴
캐시 사용을 최적화하기 위해 자동 만료를 사용하여 캐시 배제(지연 로드) 패턴을 구현합니다.
def get_user_data(user_id, ttl=3600):
"""
Retrieve user data using cache-aside pattern.
Args:
user_id: User identifier
ttl: Cache expiration in seconds (default: 1 hour)
"""
cache_key = f'user:{user_id}:data'
# Try to get from cache
cached_data = r.get(cache_key)
if cached_data:
return cached_data
# Cache miss - fetch from database
user_data = fetch_from_database(user_id)
# Store in cache with expiration
if user_data:
r.setex(cache_key, ttl, user_data)
return user_data
def fetch_from_database(user_id):
# Placeholder for database query
return f"User data for {user_id}"
패턴 기반 무효화
명령과 SCAN 일치하는 패턴을 사용하여 여러 관련 키를 삭제합니다(서버를 차단하는 것이 아니라 KEYS).
def invalidate_user_cache(user_id):
"""
Invalidate all cache entries for a specific user.
Uses SCAN for non-blocking pattern matching.
"""
pattern = f'user:{user_id}:*'
cursor = 0
deleted_count = 0
while True:
cursor, keys = r.scan(cursor, match=pattern, count=100)
if keys:
deleted_count += r.delete(*keys)
if cursor == 0:
break
print(f"Invalidated {deleted_count} keys for user {user_id}")
# Usage
invalidate_user_cache(1001)
데이터 작업에 대한 모범 사례
이러한 사례를 따르면 Redis 구현이 강력하고 성능이 우수하며 유지 관리가 가능합니다.
-
연결 풀링 사용: 라이브러리는
redis자동으로 연결 풀링을 사용하지만 최적의 성능을 위해 구성할 수 있습니다. 자세한 내용은 연결 풀 및 멀티플렉싱을 참조하세요. - 오류를 정상적으로 처리합니다. 애플리케이션 복원력을 보장하기 위해 항상 Redis 작업에 대한 오류 처리를 구현합니다.
-
적절한 TTL 값을 선택합니다. 데이터의 특성 및 새로 고침 요구 사항에 따라 만료 시간을 선택합니다.
- 자주 변경된 데이터: 1-5분
- 보통 변경률: 15-60분
- 비교적 안정적인 데이터: 1-24시간
- 정적 참조 데이터: 24시간 이상
TTL 값을 결정할 때 데이터 새로 고침 및 캐시 적중률에 대한 애플리케이션의 요구 사항을 고려합니다.