HighLoad.org – блог о высоких нагрузках
HighLoad.org > Redis на практике: кто онлайн?

Redis на практике: кто онлайн?
2010-03-07 06:21 my_fess  
Redis — одно из самых интересных NoSQL решений. Redis намного больше, чем простое key-value хранилище, здесь значениями могут быть не только простые строки, но и структуры данных. Redis сейчас поддерживает списки, множества и отсортированные множества. Этот пост предоставляет пример использования Redis-кого типа данных множества, который я реализовал для weplay (наша социальная сеть о спорте).

Окончательный результат

Участники weplay сказали нам, что они хотели иметь возможность видеть, кто из их друзей онлайн, поэтому мы решили добавить эту фичу. Давайте посмотрим, как Redis участвует в предоставление этой функциональности.

Свойства множества

Сначала небольшой обзор Redis-кой структуры данных — множества. Множество в Redis обладает теми же свойствами, как и одноименная абстрактная структура данных:
  • от 0 до N элементов
  • неупорядоченность
  • нет повторяющихся элементов
На практике это очень удобно добавлять значение в множество без проверки есть ли уже такое значение там.

Операции над множеством в Redis

Вы можете посмотреть полный список операций над множествами поддерживаемые Redis. А вот какие из них мы использовали:
require 'redis'

# SADD key, member
# Добавляет member в множество key.

redis = Redis.new
redis.sadd 'my_set', 'foo' # => true
redis.sadd 'my_set', 'bar' # => true
redis.sadd 'my_set', 'bar' # => false
redis.smembers 'my_set'    # => ["foo", "bar"]

# SUNION key1 key2 ... keyN
# Возвращзает элементы объединения всех множдеств

# SUNIONSTORE dstkey  key1 key2 ... keyN
# Работает как SUNION, но вместо возвращения результирующего
# множества ,записывает его в dstkey

redis = Redis.new
redis.sadd 'set_a', 'foo'
redis.sadd 'set_a', 'bar'
redis.sadd 'set_b', 'bar'
redis.sadd 'set_b', 'baz'
redis.sunion 'set_a', 'set_b'                # => ["foo", "baz", "bar"]

redis.sunionstore 'set_ab', 'set_a', 'set_b'
redis.smembers 'set_ab'                      # => ["foo", "baz", "bar"]

# SINTER key1 key2 ... keyN
# Возвращает пересечение всех перечисленных множества

redis = Redis.new
redis.sadd 'set_a', 'foo'
redis.sadd 'set_a', 'bar'
redis.sadd 'set_b', 'bar'
redis.sadd 'set_b', 'baz'
redis.sinter 'set_a', 'set_b'                # => ["bar"]

Подход

Суть состоит в том, чтобы каждую минуту иметь одно активное множество. Как только от залогиненого пользователя приходит запрос, мы добавляем ID этого пользователя в активное множество. Когда нам необходимо узнать ID-шники онлайн пользователей, мы можем объединить последние 5 множеств, чтобы получить множество ID пользователей сделавших запрос в течение последних 5 минут. Теперь, когда у нас есть множество ID пользователей, которые являются друзьями некоторого пользователя, мы можем получить пересечение этого множества с множеством онлайн пользователей, и мы получим наш список ID-шников друзей онлайн.

Код

Вот соответствующий код. (Замечание: мы используем redis-rb библиотеку, написанную Ezra Zygmuntowicz (@ezmobius, в качестве нашего Redis клиента).
# Defining the keys

def current_key
    key(Time.now.strftime("%M"))
end

def keys_in_last_5_minutes
    now = Time.now
    times = (0..5).collect {|n| now - n.minutes }
    times.collect{ |t| key(t.strftime("%M")) }
end

def key(minute)
    "online_users_minute_#{minute}"
end

# Учитывание активного пользователя

def track_user_id(id)
    key = current_key
    redis.sadd(key, id)
end

# Кто онлайн

def online_user_ids
    redis.sunion(*keys_in_last_5_minutes)
end

def online_friend_ids(interested_user_id)
    redis.sunionstore("online_users", *keys_in_last_5_minutes)
    redis.sinter("online_users", "user:#{interested_user_id}:friend_ids")
end

Мне нравится, что код краткий, почти как диаграммы. С таким подходом, вам необходимо очищать или удалять старые множества, чтобы через час все учитывать по новому. Мы используем задачи cron для этого. Я не собираюсь показывать это здесь, но со мной легко связаться если у вас будут вопросы об этом. Надеюсь вы заметили элегантную простоту и мощность Redis. Приведенный код проще, чем мог быть, если бы использовалась реляционная СУБД или memcached или оба в совокупности. Если вы новичок в Redis, я рекомендую вам зафоловить ее создателя и разработчика, Salvatore Sanfilippo (@antirez). Он очень содержательный и понятный лидер открытых исходников, и он быстро и постоянно улучшает Redis.

Автор статьи: Luke Melia Дата: 17 января 2010 Оригинал статьи


комментарии [0]  | комментировать

  © 2010-2018 HIGHLOAD