Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Drzewka, cache'owanie wyników a wydajność
Forum PHP.pl > Forum > Bazy danych
spenalzo
Więc po wielu próbach wymyśliliśmy, a raczej scanner wymyslił (i chwała mu za to smile.gif ) jak rozwiązać problem drzewek. Struktura tabeli połączeń wygląda tak:
[sql:1:b8f8130be6]CREATE TABLE `scan_userfriends` (
`userID` bigint(20) unsigned NOT NULL default '0',
`friendID` bigint(20) unsigned NOT NULL default '0'
) TYPE=MyISAM;[/sql:1:b8f8130be6]
natomiast problem wyciągania zapytań został rozwiązany następująco w 3 zapytaniach (tutaj dla uzytkownika nr 1):
Zapytanie nr1
[sql:1:b8f8130be6]SELECT friendID FROM scan_userFriends WHERE userID = 1[/sql:1:b8f8130be6]
po wyciagnieciu ID zapisujemy je do tablicy w php i następnie złączamy do postaci "1,45,85,296,904,10535,..." i wykonujemy kolejne zapytanie
[sql:1:b8f8130be6]SELECT friendID FROM scan_userFriends WHERE userID IN(1,45,85,296,904,10535,...) AND friendID <> 1[/sql:1:b8f8130be6]
Tera zapisujemy do innej tablicy wyniki tego zapytania (np. "3,1056,4352,9024,10466,14935,66043,...")
i wykonujemy zapytanie
[sql:1:b8f8130be6]SELECT friendID FROM scan_userFriends WHERE userID IN (3,1056,4352,9024,10466,14935,66043,...) AND userID NOT IN(1,45,85,296,904,10535,...) AND friendID <> 1[/sql:1:b8f8130be6]
czyli pobieramy ID, których rodzicem są ID z drugiego zapytania i ale nie z pierwszego zapytania.

Działa to bardzo sprawnie nawet na tabelach o bardzo duzej ilości rekordów, ale mimo to pomyślałem o keszowaniu wyników i wymyśliłem taką strukturę tabeli:
[sql:1:b8f8130be6]CREATE TABLE `friends_cached` (
`userid` int(11) NOT NULL default '0',
`friends` text NOT NULL,
`friends_count` int(11) NOT NULL default '0',
`cached` varchar(14) NOT NULL default '',
PRIMARY KEY (`userid`)
) TYPE=MyISAM;[/sql:1:b8f8130be6]
gdzie userid to numer ID użytkownika, friends to numery ID userów rozdzielone przecinkami, friends_count to liczba przyjaciół (żeby za każdym razem nie pobierać z tabeli z powiązaniami), a cached to pole zawierajace czas ostatniego uaktualnienia wyników.

Kiedy będzie tabela uaktualniana:
a) jeżeli od ostatniego keszowania minęło więcej niz 0,5 - 2 h (nie wiem ....)
cool.gif przy dodaniu nowego użytkownika,
c) przy usunięciu użytkownika
d) przy jakiejkolwiek innej zmianie, która wpływa na ilość znajomych

Ponieważ uaktualnianie może mimo wszystko troche zająć to wymyśliłem takie rozwiązanie:
wyświetlamy użytkownikowi strone, że za chwile zostanie przeniesiony na inną, potem przekierowyjemy go przez meta-refresh, a skrypt dalej sie wykonuje i wywołuje funkcję zapisującą dane. Jednocześnie z linkiem przekazujemy czas kiedy nastapiło keszowanie i na następnej stronie sprawdzamy czy w bazie pole "cached" ma taką wartość. Jeżeli tak to OK, jeżeli nie to wyświetlamy, że "prosze czekać" czy cos takiego...

Co Wy na to?? Jake macie sugestie co do optymalizacji pierwszych 3 zapytań i w stosunku do keszowania?
scanner
1. Trzeba przenieść[sql:1:b7637fdc89]AND friendID <> 1[/sql:1:b7637fdc89] do wewnątrz [sql:1:b7637fdc89]NOT IN ()[/sql:1:b7637fdc89]
2. Cacheowanie: pamiętaj, że jeśli u mojego przyjaciela zmieni się lista przyjacól, to trzeba by tez zaktualizować cahce mojej listy.
scanner
Aha. Podaj jeszcze pliki add.php i inidex.php smile.gif
spenalzo
Cytat
2. Cacheowanie: pamiętaj, że jeśli u mojego przyjaciela zmieni się lista przyjacól, to trzeba by tez zaktualizować cahce mojej listy.

No właśnie też o tym pomyślałem i przestaje mi sie podobać takie rozwiązanie... Chyba, że po prostu założy się takie rozwiazanie, że odświeża się przy zmianie dane tylko dla użytkownika zmieniajacego, bo zgodnie z założeniami (które zostały wprowadzone juz w życie smile.gif ) dane są i tak odświeżane co godzine (lub przy następnym zalogowaniu się użytkownika)...

Dodawanie rekordów:
[php:1:ccb685b3cb]<?php
set_time_limit(0);
ignore_user_abort(true);
mt_srand();
$time=time();
$intMaxRepeat = 5000;
$intRepeat = 0;
$arrPairs = array();

do
{
do
{
$intMyID = mt_rand(1, $intMaxRepeat/5);
$intFriendID = mt_rand(1, $intMaxRepeat/5);
$blnPairsDuplicated = FALSE;
if( isset($arrPairs[$intMyID]) && ($arrPairs[$intMyID]==$intFriendID) )
{
$blnPairsDuplicated = TRUE;
}
else
{
$arrPairs[$intMyID]=$intFriendID;
}
}
while ( ($intMyID == $intFriendID) && !$blnPairsDuplicated );
$q=mysql_unbuffered_query("INSERT INTO god_userfriends (userID, friendID) VALUES (".$intMyID.",".$intFriendID.")");# or die(mysql_error());
$intRepeat++;
}
while ( $intRepeat < $intMaxRepeat );

?>[/php:1:ccb685b3cb]

Wyciaganie rekordów:
[php:1:ccb685b3cb]<?php
set_time_limit(0);
function MyImplode( $arrArray )
{
if(sizeof($arrArray) > 1)
{
$strResult = @implode(',', $arrArray);
}
else
{
$strResult = $arrArray[0];
}
return $strResult;
}
function Dump( $mixVar )
{
echo '<pre>';
var_dump( $mixVar );
echo '</pre><hr />';
}

function czas()
{
$t=explode(" ",microtime());
return($t[1]+$t[0]);
}

$myid=1;
$sql="SELECT COUNT(userID) AS ilosc FROM god_userfriends ";
$q=mysql_query($sql) or die(mysql_error());
$ilosc=mysql_result($q,0);
#
$sql="SELECT friendID FROM god_userfriends WHERE userID = ".$myid;
$czas=czas();
$q=mysql_query($sql) or die(mysql_error());
echo sprintf("Czas wykonania pierwszego zapytania: %0.4f sekund<br>",czas()-$czas);
while($t=mysql_fetch_array($q))
{
$arrMyFriends[]=$t["friendID"];
}
$arrMyFriendsSQL=myimplode($arrMyFriends);
if(strlen($arrMyFriendsSQL)>=1)
{
$arrMyFriendsSQL=MyImplode($arrMyFriends);
$sql="SELECT friendID FROM god_userFriends WHERE userID IN(".$arrMyFriendsSQL.") AND friendID <> ".$myid;
$czas=czas();
$q=mysql_query($sql) or die(mysql_error());
echo sprintf("Czas wykonania drugiego zapytania: %0.4f sekund<br>",czas()-$czas);
while($t=@mysql_fetch_array($q))
{
$arrMyFriendsFriends[] = $t["friendID"];
}
$arrMyFriendsFriendsSQL=MyImplode($arrMyFriendsFriends);
if(strlen($arrMyFriendsFriendsSQL)>=1)
{
$sql="SELECT friendID FROM god_userFriends WHERE userID IN (".$arrMyFriendsFriendsSQL.") AND userID NOT IN(".$arrMyFriendsSQL.") AND friendID <> ".$myid;
$czas=czas();
$q=mysql_query($sql) or die(mysql_error());
echo sprintf("Czas wykonania trzeciego zapytania: %0.4f sekund<br>",czas()-$czas);
while($t=@mysql_fetch_array($q))
{
$arrMyFriendsFriendsFriends[] = $t["friendID"];
}
}
}
echo "<br>Przyjaciół na 1 poziomie: ".count($arrMyFriends)."<br>";
echo "Przyjaciół na 2 poziomie: ".count($arrMyFriendsFriends)."<br>";
echo "Przyjaciół na 3 poziomie: ".count($arrMyFriendsFriendsFriends)."<br>";
echo "Razem masz: ";
echo count($arrMyFriends) + count($arrMyFriendsFriends) + count($arrMyFriendsFriendsFriends)." przyjaciół. <br>";
echo "Łączna ilość rekordów: ".$ilosc."<br>";
?>[/php:1:ccb685b3cb]
spenalzo
Hmm co do keszowania to wymysliłem troche inaczej: przy każdej zmianie danych (dodanie, usunięcie użytkownika itd) ustawiam dla jego rodzica czas ostatniej aktualizacji na 0, co spowoduje po zalogowaniu odświeżenie danych. Co o tym sądzicie?
scanner
No to już jepiej, ale powinieens przetrzepać całe drzewko od klienta do ostatniego rodzica rodzica rodzica..
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.