Какие типы данных поддерживает Redis?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы данных в Redis
Redis (Remote Dictionary Server) — это in-memory хранилище ключ-значение, которое поддерживает различные структуры данных. Это отличает его от обычных кэшей и делает мощным инструментом для различных задач.
1. String (Строка)
Самый простой тип — бинарные строки длиной до 512 MB.
import redis
red = redis.Redis(host='localhost', port=6379, db=0)
# Установка
red.set('user:1:name', 'Alice')
# Получение
name = red.get('user:1:name') # b'Alice'
name_str = red.get('user:1:name').decode() # 'Alice'
# Работа с числами
red.set('counter', 0)
red.incr('counter') # 1
red.incr('counter') # 2
red.incrby('counter', 5) # 7
red.decr('counter') # 6
# Работа со строками
red.append('message', ' World') # добавить в конец
red.strlen('message') # длина строки
red.getrange('message', 0, 4) # подстрока
red.setrange('message', 6, 'Redis') # заменить часть
# Bitwise операции (для флагов)
red.setbit('flags', 0, 1) # установить бит 0
red.getbit('flags', 0) # получить бит
red.bitcount('flags') # количество установленных битов
2. List (Список)
Отсортированная коллекция строк. Может использоваться как очередь или стек.
# Добавление элементов
red.rpush('tasks', 'task1', 'task2', 'task3') # в конец
red.lpush('tasks', 'task0') # в начало
# Получение элементов
red.lrange('tasks', 0, -1) # все элементы [b'task0', b'task1'...]
red.llen('tasks') # 4
red.lindex('tasks', 0) # b'task0' (первый элемент)
# Удаление элементов
red.lpop('tasks') # удалить первый → b'task0'
red.rpop('tasks') # удалить последний → b'task3'
red.lrem('tasks', 1, 'task1') # удалить 1 вхождение
# Использование как очередь (FIFO)
def enqueue(queue_name, item):
red.rpush(queue_name, item)
def dequeue(queue_name):
return red.lpop(queue_name)
enqueue('job_queue', 'job1')
val = dequeue('job_queue') # 'job1'
# Использование как стек (LIFO)
def push(stack_name, item):
red.lpush(stack_name, item)
def pop(stack_name):
return red.lpop(stack_name)
push('undo_stack', 'action1')
val = pop('undo_stack') # 'action1'
# Блокирующие операции (для очереди)
def wait_for_job(queue_name, timeout=0):
# Ждет элемент с timeout (0 = бесконечно)
return red.blpop(queue_name, timeout)
item = wait_for_job('job_queue', timeout=5)
if item:
queue_name, value = item
print(f"Got job: {value}")
3. Set (Множество)
Неупорядоченная коллекция уникальных строк. Поддерживает операции множеств.
# Добавление элементов
red.sadd('users:online', 'alice', 'bob', 'charlie')
red.scard('users:online') # 3 (размер)
red.smembers('users:online') # {b'alice', b'bob', b'charlie'}
# Проверка принадлежности
red.sismember('users:online', 'alice') # True
red.sismember('users:online', 'dave') # False
# Удаление
red.srem('users:online', 'bob') # удалить элемент
red.spop('users:online') # удалить случайный элемент
# Операции множеств
red.sadd('users:premium', 'alice', 'eve')
red.sadd('users:free', 'bob', 'charlie')
# Пересечение
red.sinter('users:online', 'users:premium') # {b'alice'}
# Объединение
red.sunion('users:premium', 'users:free') # все пользователи
# Разность
red.sdiff('users:online', 'users:premium') # {b'bob', b'charlie'}
# Хранение результата
red.sinterstore('result', 'users:online', 'users:premium')
# Использование: отслеживание онлайна
def user_came_online(user_id):
red.sadd('users:online', user_id)
def user_went_offline(user_id):
red.srem('users:online', user_id)
def get_online_users():
return red.smembers('users:online')
4. Hash (Хеш)
Мапа поля-значение. Идеален для хранения объектов.
# Установка полей
red.hset('user:1', mapping={
'name': 'Alice',
'email': 'alice@example.com',
'age': '30'
})
# Получение
red.hget('user:1', 'name') # b'Alice'
red.hgetall('user:1') # {b'name': b'Alice', ...}
red.hmget('user:1', 'name', 'email') # [b'Alice', b'alice@...']
# Проверка
red.hexists('user:1', 'name') # True
red.hlen('user:1') # 3 полей
# Получение ключей и значений
red.hkeys('user:1') # [b'name', b'email', b'age']
red.hvals('user:1') # [b'Alice', b'alice@...', b'30']
# Удаление
red.hdel('user:1', 'age')
# Инкремент числового поля
red.hincrbyfloat('user:1', 'rating', 0.5)
# Использование: кэш пользователя
class UserCache:
def save_user(self, user_id, user_data):
red.hset(f'user:{user_id}', mapping=user_data)
def get_user(self, user_id):
user_data = red.hgetall(f'user:{user_id}')
return {k.decode(): v.decode() for k, v in user_data.items()}
def update_field(self, user_id, field, value):
red.hset(f'user:{user_id}', field, value)
cache = UserCache()
cache.save_user(1, {'name': 'Alice', 'email': 'alice@example.com'})
user = cache.get_user(1) # {'name': 'Alice', 'email': 'alice@example.com'}
5. Sorted Set (Отсортированное множество)
Множество с оценкой (score) для каждого элемента. Сортируется по score.
# Добавление с score
red.zadd('leaderboard', {'alice': 100, 'bob': 85, 'charlie': 95})
# Получение
red.zscore('leaderboard', 'alice') # 100.0
red.zrank('leaderboard', 'alice') # позиция (0-based)
red.zrevrank('leaderboard', 'alice') # позиция в обратном порядке
# Диапазоны
red.zrange('leaderboard', 0, -1) # все, отсортированные
red.zrange('leaderboard', 0, -1, withscores=True) # с очками
red.zrevrange('leaderboard', 0, -1, withscores=True) # в обратном порядке
# По score
red.zrangebyscore('leaderboard', 85, 100) # элементы с score от 85 до 100
red.zcount('leaderboard', 85, 100) # количество
# Инкремент score
red.zincrby('leaderboard', 5, 'bob') # bob теперь 90
# Удаление
red.zrem('leaderboard', 'charlie')
red.zremrangebyrank('leaderboard', 0, 0) # удалить первого
# Использование: лидерборд
def add_score(user_id, score):
red.zadd('leaderboard', {user_id: score})
def increment_score(user_id, points):
red.zincrby('leaderboard', points, user_id)
def get_top_10():
return red.zrevrange('leaderboard', 0, 9, withscores=True)
def get_user_rank(user_id):
rank = red.zrevrank('leaderboard', user_id)
return rank + 1 if rank is not None else None
add_score('alice', 100)
add_score('bob', 85)
increment_score('bob', 10) # bob = 95
print(get_top_10()) # [(b'alice', 100), (b'bob', 95)]
print(get_user_rank('alice')) # 1
6. Stream (Поток)
Последовательность событий. Как лог с автоматическими ID.
# Добавление события
red.xadd('events', {'user_id': '1', 'action': 'login'})
red.xadd('events', {'user_id': '2', 'action': 'upload'})
# Получение события
red.xlen('events') # количество
red.xrange('events') # все события
# Групповое чтение (для обработки очереди)
red.xgroup_create('events', 'event_processors', id='0')
event = red.xreadgroup({'events': '>'}, 'event_processors', count=1)
# Использование: логирование событий
def log_event(event_type, data):
red.xadd('event_log', {'type': event_type, **data})
def get_recent_events(count=100):
events = red.xrevrange('event_log', count=count)
return [(e_id, {k.decode(): v.decode() for k, v in data.items()})
for e_id, data in events]
log_event('user_signup', {'user_id': '1', 'email': 'alice@example.com'})
log_event('order_created', {'order_id': '123', 'amount': '99.99'})
7. HyperLogLog
Структура для подсчета уникальных элементов с минимальной памятью (вероятностный).
# Добавление элементов
red.pfadd('unique_visitors', 'user1', 'user2', 'user3')
red.pfadd('unique_visitors', 'user1', 'user4') # user1 уже был
# Подсчет (приблизительный)
red.pfcount('unique_visitors') # ≈ 4
# Объединение HyperLogLogs
red.pfadd('visitors_today', 'alice', 'bob')
red.pfadd('visitors_yesterday', 'bob', 'charlie')
red.pfmerge('all_visitors', 'visitors_today', 'visitors_yesterday')
red.pfcount('all_visitors') # ≈ 3
# Использование: уникальные пользователи
def track_visitor(page, user_id):
red.pfadd(f'unique_visitors:{page}', user_id)
def get_unique_count(page):
return red.pfcount(f'unique_visitors:{page}')
8. Bitmap (через String)
Эффективное хранение флагов в битах.
# Установка и получение битов
red.setbit('user:online', 0, 1) # пользователь 0 онлайн
red.setbit('user:online', 1, 0) # пользователь 1 оффлайн
red.getbit('user:online', 0) # 1
# Подсчет установленных битов
red.bitcount('user:online') # 1 пользователь онлайн
# Использование: отслеживание дней активности
def mark_user_active(user_id, day):
red.setbit(f'user:{user_id}:active_days', day, 1)
def get_active_days(user_id, start, end):
# Подсчет активных дней
return red.bitcount(f'user:{user_id}:active_days', start, end)
Сравнение и выбор типа
| Тип | Использование | Пример |
|---|---|---|
| String | Простой кэш, счетчики | user:1:name, views:article:1 |
| List | Очередь, стек, история | job_queue, undo_stack |
| Set | Теги, уникальные элементы | users:online, article:tags |
| Hash | Объект, кэш с полями | user:1, product:123 |
| Sorted Set | Рейтинг, лидерборд | leaderboard, trending:posts |
| Stream | События, логи | event_log, notifications |
| HyperLogLog | Уникальные подсчеты | unique_visitors |
| Bitmap | Флаги, битовые операции | user:online, active_days |
Практические паттерны
# Session хранилище
class SessionStore:
def save_session(self, session_id, data, ttl=3600):
red.hset(f'session:{session_id}', mapping=data)
red.expire(f'session:{session_id}', ttl)
def get_session(self, session_id):
return red.hgetall(f'session:{session_id}')
# Очередь задач
class JobQueue:
def enqueue(self, job_type, payload):
red.rpush('job_queue', json.dumps({'type': job_type, 'payload': payload}))
def dequeue(self):
job = red.lpop('job_queue')
return json.loads(job) if job else None
# Рейтинг
class RatingService:
def add_vote(self, item_id, user_id, vote):
# Добавить голос
red.zadd('rating', {item_id: vote})
# Отследить кто голосовал
red.sadd(f'voters:{item_id}', user_id)
def get_rating(self, item_id):
return red.zscore('rating', item_id) or 0
Выводы
- String: для простого кэша и счетчиков
- List: для очередей и истории
- Set: для уникальных элементов и членства
- Hash: для объектов с несколькими полями
- Sorted Set: для рейтингов и лидербордов
- Stream: для логирования событий
- HyperLogLog: для больших наборов уникальных элементов
- Bitmap: для флагов и битовых операций