2017-02-19 10 views
3

На моем веб-сайте пользователям разрешено хранить одни и те же имена пользователей. Более того, в любой момент времени пользователь входит в систему, я временно сохраняю свое имя пользователя в ключе redis с ttl 10 минут.Поиск коллизий значений из изменяющейся коллекции ключей Redis

Вопрос: Есть ли способ - используя Redis - найти все интернет-пользователи за последние 10 минут, воспользовавшись тем же именем пользователя?

В настоящее время я извлекаю все значения ключей и нахожу коллизии в Python - что действительно не помогает, так как мне нужно делать это несколько раз во время выполнения (и есть много пользовательского трафика).

Я предполагаю, что я мог бы создать наборы с уникальным именем пользователя в качестве ключа и сохранить все идентификаторы пользователя в наборе, чтобы дать мне O (1) поиск пользователей, использующих одни и те же имена пользователей. Но тогда мне придется пожертвовать условием 10 минут ttl (которое мне нужно для каждого имени пользователя отдельно).

Btw Redis/Lua начинающий здесь, отсюда вопрос noob (если есть).

ответ

1

Где есть воля, есть способ ... :)

Начните хранить логины в отсортированном наборе. Если предположить, что идентификатор пользователя 123 вошел в систему во время 456 с именем пользователя «Foo», то можно представить, что, как:

ZADD logins 456 123:foo 

Примечание: Вы также должны удалить старые элементы из этого отсортированного набора, так что Безразлично «Просто выходите из-под контроля.

Далее, вы хотите найти пользователей за последние 10 минут, поэтому для этого вы должны использовать ZRANGEBYSCORE. Вместо того, чтобы отправить все свое дело своему клиенту, используйте Lua для его обработки и проверки на наличие конфликтов.

Следующий пример сценария обертывания вместе все вышеперечисленное:

-- Keys: 1) The logins Sorted Set 
-- Args: 1) The epoch value of 'now' 
--  2) The logged in user id 
--  3) The logged in user name   

-- Get logins from the last 10 minutes 
local l = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1]-600, '+inf') 

-- "Evict" old logins 
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', '(' .. ARGV[1]-600) 

-- Store the new login 
redis.call('ZADD', KEYS[1], ARGV[1], ARGV[2] .. ':' .. ARGV[3]) 

local c = {} -- detected name collision 
for _, v in pairs(l) do 
    local p = v:find(':') -- no string.split in Lua 
    local i = v:sub(1,p-1) -- id 
    local n = v:sub(p+1) -- name 
    if n == ARGV[3] then 
    c[#c+1] = i 
    end 
end 

return c 
+0

Круто! Это выглядит хорошо и, наконец, заставит меня написать свой первый правильный сценарий Lua. Я думал, что я буду хранить 'usernames.lua' локально, а затем называть его в python. Должен ли я использовать https://labix.org/lunatic-python, или я могу просто импортировать файл lua в качестве модуля, такого как обычные файлы py? –

+0

Это еще один вопрос Hassan :) Следуйте этому примеру - https://pypi.python.org/pypi/redis#lua-scripting –

+1

Gotcha! Как всегда, спасибо за то, что вы показали дорогу. –