Wyświetlanie użytkowników online. Jak można to zrobić lepiej?

0

Obecnie sesje zapisywane są w Redis. W sesji zapisany jest też URL pod którym aktualnie przebywa użytkownik.

Problemem było wyświetlanie listy użytkowników dostępnych na danych podstronach. Na produkcji, przy kilku tysiącach sesji działało to wolno. Wyciąganie kilku tysięcy rekordów z redis, następnie wyszukiwanie czy dany użytkownik znajduje się na danej podstronie, nie sprawdzało się.

Postanowiłem więc w cronie, co 1 min. zapisywać sesje z Redis do tabeli sessions. Tutaj już łatwo można odszukać użytkowników przebywających na danej podstronie: SELECT user_id FROM sessions WHERE path LIKE '/forum/off-topic/%'. Co 1 minute wykonywane jest jednak zapytanie DELETE na tabeli, a następnie INSERT. Może macie pomysł jak można by to lepiej rozwiązać?

1

A gdyby odwrócić schemat danych w redisie - URL jako klucz, a lista użytkowników przebywających na ten stronie jako wartość?
Nie wiem w jaki sposób decydujesz kiedy użytkownik "opuszcza" daną stronę, ale strzelam, że po jakimś ustalonym czasie od wejścia usuwasz użytkownika z listy przeglądających. W redisie nie można ustawić TTL na elementy listy, ale można to obejść w taki sposób - https://quickleft.com/blog/how-to-create-and-expire-list-items-in-redis/. Nie wiem jak z wydajnością.

0

IMO sorted sets pasują tutaj jak ulał - niech kluczem będzie pewna znormalizowana postać zasobu (np. stats.posts.25 / stats.categories.38), a wartością posortowany zbiór, którego z kolei kluczem będzie id użytkownika (co gwarantuje nam unikalność), a wartością unix timestamp.

Przykład:

# id postu: 10, id użytkownika: 50

# Usuwamy sesje starsze niż minuta:
ZREMRANGEBYSCORE stats.posts.10 0 1564574027

# Dodajemy aktualną sesję:
ZADD stats.posts.10 1564574087 "50"

# Zliczamy liczbę aktualnych użytkowników:
ZCOUNT stats.posts.10

W tym wypadku również trzeba ręcznie usuwać stare sesje, lecz już nie cronem - no i złożoność jest logarytmiczna! :-)

Edit: widzę, że @iksde podesłał coś podobnego;

0

Załóżmy, że mamy użytkownika A oraz B. A przebywa na podstronie /Forum/Off-topic, a B na /Forum/Kariera.

Będąc na stronie głównej wyświetlamy wszystkich użytkowników online. Na /Forum wyświetlamy, że użytkownik A i B są online. Na podstronie /Forum/Off-topic wyświetlamy że online jest użytkownik A. Czyli, usera A należałoby dodać do dwóch zbiorów:

SADD forum "A"
SADD forum.off-topic "B"

Liczba zbiorów w tym przypadku zależy od poziomów zagłębienia URL. Nie wiem czy nie próbowałem już kiedyś czegoś podobnego ;) Ale spróbuje jeszcze raz i podzielę się rezultatem.

0

Sesje trzymane są w hashach redisa gdzie kluczem jest id sesji, a wartością serializowana tablica z informacjami z sesji.

Już pamiętam, że o wiele łatwiej było to zrobić (tj. wyświetlanie statystyk userów online) za pomocą bazy danych niż redisa. Na bazie wykonuje takie zapytanie select "user_id", "robot", COUNT(*) from "sessions" group by "user_id", "robot" i mam już policzoną liczbę użytkowników niezalogowanych online oraz botów. Trwa to 2 ms.

W redisie wyciągnięcie wszystkich sesji, tj. $redis->hgetall('sessions'); trwa 10 ms. Do tego należy doliczyć grupowanie userów.

0

Póki co zarzuciłem to zadanie. Zacząłem pisać, ale problem nie jest tak trywialny. Ustawiłem za to tabelę sessions na UNLOGGED i czas zapisu jest zadowalający.

Niemniej jednak gdyby ktoś miał ochotę zrealizować to zadanie, to proszę pisać :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1