Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Redis: типы данных и использование
Redis — это in-memory хранилище данных. Использую его почти в каждом проекте для кэширования, очередей и sessions.
Основные типы значений в Redis
1. String (Строка)
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Простая строка
r.set('user:1:name', 'John Doe')
name = r.get('user:1:name')
# 'John Doe'
# С expiration (важно!)
r.setex('session:abc123', 3600, 'user_token') # Истечёт через час
# Числовые операции
r.set('page_views', 0)
r.incr('page_views') # +1
r.incrby('page_views', 5) # +5
r.get('page_views')
# '6'
# Битовые операции
r.setbit('users_online', 0, 1) # User 0 online
r.getbit('users_online', 0)
# 1
2. List (Список)
Ордерированный список элементов. Эффективен для очередей.
# Добавление
r.rpush('tasks', 'task1', 'task2', 'task3')
# [0, 2, 4, 6, 8]
# FIFO очередь
r.lpop('tasks') # 'task1' - берём слева
r.lpush('tasks', 'task0') # Добавляем слева
# LIFO (Stack)
r.rpush('stack', 'a', 'b', 'c')
r.rpop('stack') # 'c' - берём справа (последний)
# Получить диапазон
r.lrange('tasks', 0, -1) # Все элементы
r.lrange('tasks', 0, 2) # Первые 3
# Длина
r.llen('tasks')
# Практический пример: job queue
def add_job(job_type, data):
job = json.dumps({"type": job_type, "data": data})
r.rpush('job_queue', job)
def process_job():
job_json = r.blpop('job_queue', timeout=0) # Блокирующее, ждёт
if job_json:
job = json.loads(job_json[1])
# Обработать
3. Hash (Хеш)
Меп ключ-значение. Идеален для объектов.
# Сохранить объект
r.hset('user:1', mapping={
'name': 'John',
'email': 'john@example.com',
'age': '30'
})
# Получить конкретное поле
r.hget('user:1', 'name') # 'John'
# Получить все
r.hgetall('user:1')
# {'name': 'John', 'email': 'john@example.com', 'age': '30'}
# Инкремент
r.hincrbyfloat('user:1:stats', 'rating', 0.5)
# Проверить существование поля
r.hexists('user:1', 'name') # True
# Удалить поле
r.hdel('user:1', 'age')
# Практический пример: кэш сессии
def save_session(session_id, user_data):
r.hset(f'session:{session_id}', mapping=user_data)
r.expire(f'session:{session_id}', 3600) # 1 час
def get_session(session_id):
return r.hgetall(f'session:{session_id}')
4. Set (Множество)
Неупорядоченное множество уникальных элементов.
# Добавить элементы
r.sadd('tags:python', 'django', 'fastapi', 'celery')
r.sadd('tags:javascript', 'react', 'vue', 'angular')
# Получить все элементы
r.smembers('tags:python')
# {'django', 'fastapi', 'celery'}
# Проверить принадлежность
r.sismember('tags:python', 'django') # True
# Размер
r.scard('tags:python') # 3
# Операции над множествами
r.sinter('tags:python', 'tags:javascript') # Пересечение
r.sunion('tags:python', 'tags:javascript') # Объединение
r.sdiff('tags:python', 'tags:javascript') # Разность
# Практический пример: followers
def follow_user(user_id, follower_id):
r.sadd(f'user:{user_id}:followers', follower_id)
def get_followers(user_id):
return r.smembers(f'user:{user_id}:followers')
def common_followers(user1_id, user2_id):
return r.sinter(
f'user:{user1_id}:followers',
f'user:{user2_id}:followers'
)
5. Sorted Set (Упорядоченное множество)
Множество с score для сортировки. Самое мощное!
# Добавить элементы со score
r.zadd('leaderboard', {
'player1': 100,
'player2': 150,
'player3': 120
})
# Получить по рангу (от лучших)
r.zrevrange('leaderboard', 0, 2) # Top 3
# ['player2', 'player1', 'player3']
# С scores
r.zrevrange('leaderboard', 0, 2, withscores=True)
# [('player2', 150), ('player1', 100), ('player3', 120)]
# Ранг игрока
r.zrevrank('leaderboard', 'player1') # 1 (второе место, 0-indexed)
# Обновить score
r.zincrby('leaderboard', 50, 'player1') # +50
# Количество элементов в диапазоне score
r.zcount('leaderboard', 100, 150) # Элементы с score от 100 до 150
# Практический пример: рейтинг
def record_event(event_name, value=1):
today = datetime.now().strftime('%Y-%m-%d')
r.zincrby(f'events:{event_name}:{today}', value, event_name)
def get_top_events(event_name, top=10):
today = datetime.now().strftime('%Y-%m-%d')
return r.zrevrange(
f'events:{event_name}:{today}',
0, top-1,
withscores=True
)
Когда использовать какой тип
use_cases = {
"String": [
"Кэш значений (user profile)",
"Счётчики (page views, likes)",
"Sessions (с expiration)",
"Rate limiting (с INCR)"
],
"List": [
"Очереди работ (job queue)",
"Timeline событий (newest first)",
"Stack для undo/redo",
"Message queue между сервисами"
],
"Hash": [
"Кэш объектов (user data, product info)",
"Сессии (всё в одном ключе)",
"Tracking данные (metrics per user)",
"Конфигурация (key-value pairs)"
],
"Set": [
"Теги (unique values)",
"Followers/Following",
"Membership (is user in group?)",
"Уникальные значения (unique IPs, user IDs)"
],
"Sorted Set": [
"Leaderboards и рейтинги",
"Time-based data (с timestamp как score)",
"Priority queues",
"Range queries (между min и max)"
]
}
Best Practices
1. Правильное именование ключей
# Плохо
r.set('u1', 'john')
r.set('pd', 'secret')
# Хорошо: namespace:object:id:field
r.set('user:1:name', 'john')
r.set('user:1:password_hash', 'secret')
r.hset('user:1', mapping={'name': 'john', 'email': 'j@e.com'})
2. Expiration для избежания утечки памяти
# ВСЕГДА для кэша и sessions
r.setex('cache:user:1', 3600, user_data) # 1 час
r.expire('session:abc', 86400) # 1 день
# Проверить TTL
r.ttl('session:abc') # Секунд до expiration (-1 = никогда)
3. Pipeline для множественных операций
# Вместо 100 запросов
pipe = r.pipeline()
for i in range(100):
pipe.incr('counter')
pipe.execute() # Один network roundtrip!
4. Transactions при необходимости
pipe = r.pipeline(transaction=True)
pipe.watch('balance:user:1')
balance = r.get('balance:user:1')
if int(balance) >= 100:
pipe.multi()
pipe.decrby('balance:user:1', 100)
pipe.incrby('balance:user:2', 100)
pipe.execute()
Сложность операций
time_complexity = {
"String": "O(1) get/set",
"List": "O(1) push/pop, O(n) range",
"Hash": "O(1) get/set field, O(n) hgetall",
"Set": "O(1) add/remove, O(n) operations",
"Sorted Set": "O(log n) add/remove, O(log n + m) range"
}
Частые ошибки
# ❌ Забыли expiration
r.set('cache:key', data) # Никогда не удалится!
# ❌ Сохранили сложный объект как string
r.set('user:data', user_obj) # Сериализируется как repr()
# ✅ Используй JSON
import json
r.set('user:data', json.dumps(user_obj))
# ✅ Или Hash
r.hset('user:1', mapping=user_dict)
Итог
Redis — это не БД, это кэш и message queue. Правила:
- String для простых значений и счётчиков
- Hash для объектов
- List для очередей
- Set для уникальных значений и членства
- Sorted Set для рейтингов и временных рядов
- Всегда используй expiration кроме постоянных данных