2015-01-29 4 views
1

Например, у меня 10 000 пользователей, которые играют в игру. Каждый пользователь может выиграть или потерять очки во время игры несколькими (может быть 100 или 1000) раз в час. Мне нужно показать 10 лучших пользователей по очкам за последние 1 час. Список должен обновляться каждую минуту.Как реализовать пользователей на Redis с zsets

Так что мне нужно сохранить и обновить 60 (минут в час) zsets за каждую победу или проиграть. Старые zsets будут автоматически удалены по истечении срока действия.

Другой способ - сохранить пользовательские точки за минуту в hset (только один hincrby за каждую победу или проиграть) и пересчитать значения для zset, используя эти данные каждую минуту. В этом случае я должен принимать по 10 000 клавиш в минуту, удалять старые данные (старше одного часа) в каждом ключе, суммировать другие данные и создавать новые zset для отображения.

Оба случая не нравятся мне, потому что количество пользователей может увеличиться в несколько раз или другие вершины могут быть добавлены в будущем.

Может ли это реализовать другой способ в Редисе?

+1

Чтобы уточнить, пытаетесь ли вы отображать десятку пользователей, отсортированных по сетевым точкам, полученным за последний час, или совокупный общий балл (если это применимо)? – rchang

+0

Да, я пытаюсь показать пользователям отсортированные по сетевым точкам в течение последнего часа. – dkop

ответ

3

Улов, как вы определяете «последний час». Гораздо проще сделать это с «часовым часом» в отличие от «последних 60 минут». Я объясню, как сделать тактовый час, учитывая его простоту.

Вы можете использовать HINCRBY с отрицательными номерами. Итак, если я правильно вас понимаю, вы должны иметь один хэш для каждого часа, с истечением срока для автоматического удаления старых часов.

Когда пользователь закончил свою игру вы делаете:

HINCR «Лидеры: hournumber» Идентификатор_пользователь

Это даст вам очки, полученные или потерянные в том час. Например, чтобы получить 10 лучших, вам нужно перейти по маршруту HGETALL, чтобы потянуть все обратно и выполнить сортировку на стороне клиента.

Чтобы использовать аспект кэширования, вы можете сохранить полученные «верхним X» значениями пользователя/точек в ключе (например, хранить его как JSON), который истекает каждые N минут. При этом процесс, который отображает рейтинг, будет тянуть этот ключ и отображать, если он найден, в противном случае генерирует/сохраняет/отображает результат. В качестве альтернативы или в дополнение к вышесказанному вы можете иметь запланированное задание, которое вычисляет и сохраняет результаты для отображения каждую минуту.

Потому что это возможно (даже если редка) для пользователей иметь такое же изменение точки сетки. Я бы не пошел с отсортированным набором, где итоговое значение точки - это оценка.

Чтобы сделать это с помощью прокатного окна, вы могли бы что-то сделать по линии минутного хэша, как указано выше, вместо часа (возможно, в таблице лидеров: minutenumber), а на стороне расчета выяснить, находятся прямо сейчас и делают HGETALL на предыдущих 60-минутных хэшах в конвейере. Конечно, установите минутные хэши истечения после 60, чтобы сохранить использование ниже.

Выполнение этого способа означает, что вы вычисляете ключи, а не запрашиваете их.

Я подозреваю, что вы также можете сделать итоговый аспект с помощью сценария Lua, но по мере роста вашей клиентской стороны и обращения к ней увеличение будет более горизонтально масштабируемой для выполнения этих вычислений на клиенте.

+1

Как насчет ZINCRBY вместо HINCRBY, так что вам не нужно делать сортировку на стороне клиента? Я бы взял ваш подход в отношении количества часов в час, что является хорошей идеей по нескольким причинам, включая простой доступ, срок действия, ... – antirez

+1

@antirez Я изначально начал работу (я - присоска для отсортированных множеств, я полагаю,), но отступил от него, потому что я чувствовал, что сложность перевода этого маршрута на «прокручивающийся 60-метровый» маршрут привела бы к большому количеству вычислений для Redis, который я не уверен, должен быть там и вообще стараться избегать в горизонтально масштабируемом клиенте модель. Для этого и для обработки связей вы либо принимаете обратный курс лексиса, либо сворачиваете свой собственный. Выполняя все это на стороне клиента, вы можете выбрать, какой именно. В противном случае отсортированные наборы будут работать нормально. –

+0

О да, если требуется скользящее окно, это определенно проблема, если дискретное решение приемлемо, подход zset, скорее всего, лучший. Одна вещь, которая может быть приемлемой из POV многих продуктов, - всегда показывать последнее окно часа, заполняя это окно часа. Однако в случае игры, возможно, требуется больше «реального времени». – antirez

2

Здесь я пытаюсь предоставить другое решение по сравнению с тем, которое предусмотрено Биллом, с недостатками и преимуществами в то же время, чтобы предоставить альтернативу.С помощью этого решения вы получаете:

  1. Вверх N без сортировки на стороне клиента.
  2. Подвижные окна.

Однако это немного дороже от POV памяти и вычислительно.

Вот как это работает:

  1. У вас есть topn ключ, который заполняется ZINCRBY с данными в реальном масштабе времени. Если мы представим себе это сделать навсегда, что произойдет, так это то, что у вас нет катящегося окна, но «все время сверху N», поэтому нам нужно исправить это.
  2. Вы также берете дополнительный сортированный набор, по одному на каждую минуту последних ~ 2 часов (при условии, что у него более 1 часа должно быть хорошо). Таким образом, каждый раз, когда пользовательский счет обновляется, вы делаете ZINCRBY до topn, а также до topn_<minute>.
  3. У вас есть дополнительный процесс, который делает следующее: для каждой записи topn_<minute>, которая больше не находится в текущих часах, она вычитает свои оценки из topn. Это должно быть возможно с помощью одного вызова ZUNIONSTORE, используя AGGREGATE «SUM». Важно удалить его (в транзакции) одновременно, поэтому мы уверены, что удалим его один раз.

Поскольку у нас есть только СУММ, здесь есть трюк. На самом деле в шаге «2» вы должны заполнить topn_<minute>перевернутыми значениями. Позитив для отрицательных оценок, отрицательный для положительных баллов.

ОК, это ночь, и я не уверен, что все детали правильно, но общая идея должна работать, иметь один главный ключ и другие клавиши, чтобы вычесть то, что больше не находится в текущем часе.