Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [SQL][PHP]Update online - konstrukcja
Forum PHP.pl > Forum > Przedszkole
fr33d0m
Posiadam na praktycznie wszystkich podstronach sporego portalu, który tworzę od pół roku taki oto skrypt:

  1. if($ZAPISUJ===1){
  2. mysql_query("UPDATE `online` SET `gdzie` = '1', `czas` = '5' WHERE `sesions_id` = '$id'");
  3. }

Powyższy skrypt zapisuje na jakiej podstronie znajduje się user oraz ustawia kolumnę z czasem sesji na 5 minut.
Mam też plik, który wykonuje cron co 5min w takiej postaci:
  1. mysql_query("UPDATE `online` SET `czas` = (`czas` - '5') WHERE `czas` > 4");

Ma on za zadanie wyeliminowanie nie aktywnych użytkowników, którzy byli zalogowani.

Odnośnie skryptu crona nie mam zastrzeżeń. Natomiast skrypt, który ustawia 5min "życia" sesji przy zaledwie 3rekordach w bazie o unikalnym ID każdy, wydłuża czas generowania strony , o 0.04000 sekundy! na pierwszy rzut oka to malutko, ale przewidywuje w tabeli `online` trzymać około 10k rekordów, więc czas ten mocno się wydłuży...
Mam założone indexy na kolumny, ale problem chyba leży w logice mojego skryptu wywalającego sesje, aby nadać danemu userowi status "offline", gdy ten nie korzysta z portalu przez 5min. Jeśli ktoś ma inną idee, szybszą to będę wdzięczny za wskazówki...
Rysh
Twoje rozwiązanie jest do bani. Więc nie dziw się, że tyle to trwa.
fr33d0m
Tak, a Twój post to czysty i perfidny spam. Gdyby moje rozwiązanie było by idealne, to bym to uwzględnił w opisie. Baaa! nawet tego tematu by nie było.
Rysh
To nawet nie jest dobre rozwiązanie, wręcz powiedziałbym złe. Po pierwsze cron nie jest Ci do niczego potrzebny, pomyśl jak wykonać to samo bez jego użycia.
fr33d0m
to powiedz mi jak wywalić zapisaną sesje w bazie (zapisaną!), gdy użytkownik po prostu zamknie przeglądarkę bez wykonania skryptu "wyloguj". Bez użycia AJAXu. Nie da się... trzeba wykonać UPDATE. Jeśli masz na myśli DEL przy usuwaniu, to wolałbym tego uniknąć bo będę musiał przerobić większy kawałek kodu. Co masz na myśli?
Rysh
Jak to się nie da? A w czym problem zapisać w bazie do kiedy sesja ma być ważna?
fr33d0m
No tak... ale to załatwi tylko 1/2 problemu. Chcę wiedzieć gdzie zalogowany user się znajduje i w tym wypadku muszę wykonać update na każdej podstronie....
Rysh
Update i tak będziesz musiał robić, ponieważ będziesz musiał przedłużać sesję o kolejne 6 min, więc w czym problem dać:
  1. UPDATE user SET session_live = NOW()+300, last_seen = 'Strona główna' WHERE user_id = 1
fr33d0m
hm... czy aby przypadkiem nie wychodzi na to samo? a nawet gorzej? bo w Twoim kodzie traci minimalne zasoby na liczenie czasu - w moim ma twardo podaną liczbę bez liczenia. Poza tym, INT chyba szybciej się zapisuje od stringa, a nawet jeśli nie, to na pewno lepiej jest odczytywać później po INTcie niż po STRINGU... Taki mądry do zjeżdżania mojej błędnej(?) logiki, a sam się nie popisałeś. Ehh
Rysh
Cytat(fr33d0m @ 2.07.2012, 20:09:05 ) *
hm... czy aby przypadkiem nie wychodzi na to samo? a nawet gorzej? bo w Twoim kodzie traci minimalne zasoby na liczenie czasu - w moim ma twardo podaną liczbę bez liczenia. Poza tym, INT chyba szybciej się zapisuje od stringa, a nawet jeśli nie, to na pewno lepiej jest odczytywać później po INTcie niż po STRINGU... Taki mądry do zjeżdżania mojej błędnej(?) logiki, a sam się nie popisałeś. Ehh

Przetestuj, a później podyskutujemy ;-)

Poza tym, kto Ci każe trzymać czas życia sesji w stringu? Czas podajesz jako unixtime w integerze, przecież to logiczne jak dwa plus dwa.
fr33d0m
To głupie, ale specjalnie dla Ciebie zmieniłem kolumnę o nazwie `gdzie` na char'a, aby przetestować Twoje wątpliwe rozwiązanie. Efekt taki sam.
`last_seen` u Ciebie to STRING - ja miałem INT'a w kolumnie `gdzie`, później, po stronie PHP obrabiałem sobie numerki pod konkretne nazwy typu 'Strona Główna' itp. To jest logiczne, bardziej niż Twoje słabe wywyższanie się od pierwszej wypowiedzi. wink.gif

EDIT: wszystkie pola mam w INT'cie - widać to w pierwszym poście z opisem problemu.
klocu
Ja mimo wszystko popieram rozwiązanie Rysh'a.

Zaprzęgania crona do eliminowania martwych dusz - trochę na wyrost.
Tak to jak zapiszesz sobie timestamp + ilość sekund naprzód to przy kolejnym odświeżeniu sprawdzisz tylko warunek:
*) timestamp aktualny < timestamp ubicia sesji => update
*) przeciwnie => ubij sesję i kieruj klienta do logowania

Cytat
bo w Twoim kodzie traci minimalne zasoby na liczenie czasu - w moim ma twardo podaną liczbę bez liczenia

tak tylko, że ty i tak musisz to potem obrobić poprzez phpa w sensie sumowanie dat, konwersja typów.
a tak to robisz sprawdzenie jednego warunku i wsio, zresztą liczenie czyli sumowanie dwóch wartości i tak spada na bazę.

Twoje rozwiązanie to trochę wyskoczenie z armatą na muchę.
fr33d0m
Panowie, nie kumam was. Rozwiązanie Rysh'a jest tak samo czasochłonne co moje - sprawdzałem. Dodanie czasu na przód dla sesji w bazie poprzez timestamp i później tylko sprawdzanie warunkiem czy sesja jeszcze żyje, jest lepsza od crona - to prawda, ale to załatwia tylko 1/2 problemu. Muszę wykonać update na każdej podstronie, aby wiedzieć gdzie znajduje się dany user i w tym tkwi największy problem... W sumie temat jest bez sensu, bo nie da się zrobić tego bez UPDATE, a z UPDATE ostro muli.

//edit:
z update: Czas generowania 0.08065s
Bez update: Czas generowania 0.02974s
Rysh
Poparz kolego:
  1. <?php
  2. $check = mysql_query("SELECT * FROM user WHERE session_life > NOW() AND user_id = "'. $_SESSION['user_id'] .'" LIMIT 1") or die ("Błąd: ". mysql_error());
  3.  
  4. if(mysql_num_rows($check) == 0) {
  5. //sesja wygasła lub coś tam coś, przekierowanie do logowania
  6. header('Location login.php');
  7. }
  8.  
  9. //sesja istnieje dalszy ciąg strony
  10. //ze zmiennej $check możesz wyciągnąć sobie elegancko dane o użytkowniku
  11.  
  12.  
  13. //na zakończenie strony update, czyli co tylko chcesz zmienić
  14. mysql_query("UPDATE user SET session_life = NOW() + 360, last_seen = 'Jakaś strona' WHERE user_id = "'. $_SESSION['user_id'] .'");
  15. ?>

I jestem pewien, że rozwiązanie takie będzie o wiele wydajniejsze.
fr33d0m
Tak, zgadzam się. Tylko, że UPDATE Twój czy mój, wykonuje się tak samo mozolnie... i w tym tkwi mój problem.


Edit:
W zasadzie 1/2 problemu rozwiązana - zastąpię crona, czasem dodatnim w DB i będę sprawdzał.
2/2 problemu są jak widać moim widzi-misie nie realnym do skorygowania.

PKT. dla Ciebie za pomoc.

Poza tematem, dobrze będzie wykorzystać crona, aby odjąć ważność ogłoszenia?
Zapisuje ważność ogłoszenie w postaci liczby i raz dziennie, chwilę przed północą odejmuję poprzez crona od ważności -1
  1. mysql_query("UPDATE `ogloszenia` SET `waznosc` = (`waznosc` - '1') WHERE `waznosc` > 0");

Tu cron się nadaje?
peter13135
Może problem załatwi założenie indeksów na pola które używasz w zapytaniach po where ? smile.gif

Poza tym, ja zrobiłbym w ten sposób, że przy odwiedzeniu strony zapisuesz do bazy aktualny uniksowy znacznik czasu (time()/now())
a cron odpalasz do wyrzucania tych wierszy, które które mają ten znacznik uniksowy mniejszy niż time()-300

Co do zapisywania time()+300 czy samego time() to nie ma większego znaczenia. Dodawanie nie zajmuje zbyt wiele czasu.
Rysh
Widzę że na siłę chcesz używać CRON'a. Niepotrzebnie, praktycznie wszystko można osiągnąć bez jego użycia.

Wyobraź sobie, że serwer jest offline w chwili kiedy powinna wykonać się aktualizacja bazy. Oczywiście update niezostanie wykonany i niektóre ogłoszenia mimo że się zakończyły dalej będą widoczne. Do tego używaj DATE i po prostu pobieraj rekordy aktualne.
fr33d0m
@peter12125, też myślałem, aby używać crona wyłącznie do delete, ze względu na szybkość wykonywania, ale dalej pozostaje ten zasrany UPDATE, który jest mulący wbrew pozorom (mam indexy). Nie pomyślałem też (co słusznie zauważył Rysh) o tym, że będzie burdel w bazie gdy serwer zaliczy offline np. poprzez padnięcie dysku, które na OVH są częstym zjawiskiem przy dedykach.
PtasiorZz
Ja osobiście zrobiłbym to tak:

- Nie zapisywałbym czasu, który pozostał do zakończenia sesji w bazie danych(czy expire date)....no bo po co to komu? Co najwyżej zastosował klucze sesji.
- Użyłbym samego mechanizmu sesji do zapamiętania session key + zapisu czasu pozostałego do końca sesji(lub expire date)
- Aktualną podstronę na, której znajduje się użytkownik zrobił na tej zasadzie:
  1. if( SESJA[last_seen] != AKTUALNA_PODSTRONA ) {
  2. UPDATE.
  3. } else {
  4. strona została tylko odświeżona, także na kij.
  5. }


Oczywiście, to swojego rodzaju pseudokod. A co do samej podstrony....to też zależy do czego Ci to potrzebne, jeśli chcesz wyświetlać to na profilu użytk. no to fakt, musisz wklepać do bazy.

//Edit.

Tak samo jeśli chodzi o to czy do identyfikacji podstron używać int'a czy string'a, wydaje mi się iż dużo wygodniej wybrać int, a gdzieś w kodzie po prostu stworzyć enum(tzn. array) z listą wszystkich podstron i ich id.
fr33d0m
Hmm... Zdecydowanie bardziej ufam ustawieniu czasu poprzez SQL.
Pomysł z if( SESJA[last_seen] != AKTUALNA_PODSTRONA ) jest dobry i przez chwile sam go używałem, ale co w przypadku gdy sesja dobiega końca za 5min, a użytkownik będzie na tej samej podstronie aktywny przez czas dłuższy? wyloguje go, a przy kolejnym takim wypadku zdenerwuje - tego chyba każdy z nas wolałby uniknąć.
Zgadzam się, że stosowanie INT'a do identyfikacji stron jest zdecydowanie lepszy w porównaniu do STRINGA. O ile zapis INT/STRING jest drugo-planowy, to odczyt wszystkich zalogowanych poprzez INT'a na danej podstronie, będzie znacznie szybszy.
PtasiorZz
Odświeżenie sesji a zapisanie ostatniej aktywności to nie to samo @fr33d0m. A ustawianie czasu poprzez sql a mechanizm sesji nie ma nic do zaufania. Co do int/string. String jest implementowany jako zbiór typu char, który z kolei jest rozkładany na byte, a Int rozkładamy tylko na byte. Tak to wygląda, dlatego najlepszym rozwiązaniem jest Integer jako key oraz String jako value.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.