Wątek zablokowany 2010-11-15 13:27 przez Adam Boduch.

Jak rozwiązać problem "znajomych" w bazie danych?

0

Witam,

czy macie może jakiś pomysł jak najlepiej stworzyć powiązania między dwoma użytkownikami w serwisach społecznościowych?
Chodzi o optymalne wykorzystanie bazy i php w przypadku kiedy powiązań może być bardzo dużo.

Ma ktoś jakiś pomysł?

0

Jest kilka sposób:

Tabela "Firends" z polami "user_id" i "list".
User nie ma znajomych - nie ma wpisu.
W innym przypadku w "list" dodajesz po przecinku id znajomego.
W phpie używasz potem explode i masz ID wszystkich znajomych.

  1. Podobnie do 1
    Tabela "Firends" z polami "user_id" i "friend_id".
    Zamiast listy trzymasz pojedynczo każde połączenie.

Myślę, że pierwszy sposób będzie mniej obciążał bazę. O ile wykonywałbyś zapytania w postaci SELECT * FROM users WHERE id='1' OR id='2' etc. a nie w pętli wyszukiwał pojedynczego usera.
Chodzi o to, że lepiej czasami stworzyć jedno rozbudowane zapytanie zamiast tworzyć ich 1000 czy więcej.

Tak poza tym do portalu społecznościowego z dużą ilość użytkowników i połączeń miedzy nimi MySQL mija się z celem. Chociaż na początek wystarczy ;)

1

Tabela users:
id
imie
nazwisko
...reszta rzeczy, które potrzebujesz np. miasto, szkoła itd...

Tabela friends:
id_osoby
id_znajomego

Jak użytkownik o id 666 doda do znajomych użytkownika o id 777 to w tabeli pojawia się wpis: 666 777. Jak użytkownik o id 666 chce zobaczyć swoich znajomych robisz:

SELECT id_znajomego FROM friends WHERE id_osoby = 666; 

Aha i oczywiście w przypadku MySQL należy używać mechanizmu InnoDB, który nie lockuje całej tabeli, a jedynie pojedynczy wiersz w przypadku operacji dodwania, usuwania pojedynczego wpisu.

bieniomajster napisał(a)

W innym przypadku w "list" dodajesz po przecinku id znajomego.
W phpie używasz potem explode i masz ID wszystkich znajomych.

Poroniony pomysł...

0

@AdamPL:
Ten sposób jest bardzo dobry, nie zaprzeczam, ale pomyśl, co w przypadku, gdy user będzie miał tą garstkę 1000 znajomych. Wówczas będziesz robił coś w stylu:

$select = mysql_query("SELECT * FROM `friends` WHERE `id_osoby`='".$UserID."');
while($line = mysql_fetch_array($select)){
   // Pobranie danych o uzytkowniku
   $select2 = mysql_query("SELECT * FROM `user_info` WHERE `id_osoby`='".$line['id_znajomego'']."' ");
   // (...)
}

Sposób w którym trzymasz listę id znajomych zaoszczędziłby wielu zapytań.
Chyba, że zamiast wykonywać w pętli kolejnego selecta, będziesz pobierał dane do zmiennej w następujący sposób:

$where = '';
$select = mysql_query("SELECT * FROM `friends` WHERE `id_osoby`='".$UserID."');
while($line = mysql_fetch_array($select)){
   $where .= "`id_osoby`='".$line['id_znajomego']."' OR ";
}

Wówczas na sam koniec tworzysz zapytanie:
mysql_query("SELECT * FROM users WHERE ".substr($where,0,-3));

Dużo lepiej pracować w ten sposób zważywszy na to, że lista (o której pisałem w pierwszym moim poście) ma ograniczoną ilość znaków.

0

A słyszałeś o złączeniach tabel?

SELECT u.* FROM users u INNER JOIN friends f ON f.id_znajomego = u.id WHERE f.id_osoby = 666;

Zamiast robić kursor w PHP i w pętli WHILE odpytywać milion razy o milion userów robisz jednego selecta jw.

0

Już myślałem o takich rozwiązaniach :) Myślałem, że jest jeszcze coś innego. Dziękuję bardzo za odpowiedzi. Przy dużej liczbie osób ze sobą powiązanych tabela może mieć ogromną liczbę rekordów.
Przykładowo:

mamy 100 000 użytkowników z czego każdy ma średnio 30 powiązanych (znajomych)
daje nam to 3 000 000 rekordów.

Myślicie, że lepiej zrobić to aby każde powiązanie (2 osoby) było zrobione w osobnym rekordzie.
('1','444','1') // ('numer_użytkownika_1','numer_użytkownika_2','czy_znajomosc_potwierdzona')

Czy lepiej jest oddzielać powiązania dla danego użytkownika jakimś znakiem ('user1','znajomy1,znajomy2,znajomy3,...,znajomyN') ?
Dzięki temu mamy tyle rekordów 'znajomych' co użytkowników (można nawet korzystać z tabeli z danymi użytkownika) i wystarczy, że znajdziemy tylko naszego usera i mamy jego wszystkich znajomych w stringu. Jednak ciężko wtedy rozwiązać możliwość potwierdzania znajomości. A przy dodawaniu znajomego korzystamy z 2 rekordów.

Wydaje mi się, że rozwiązanie pierwsze będzie dla mnie najodpowiedniejsze.
// przepraszam Pana poniżej za mój błąd językowy ;-)

0

Nie ma czegoś takiego jak "bardziej optymalne", bo optymalny oznacza najlepszy z możliwych, a zwrot "bardziej najlepszy" jest co najmniej głupi.

Ten wątek to ogólnie kompletne jaja jak dla mnie. Zrobić powiązanie dwóch encji ze sobą to przecież powinien być banał. To jasne, że zawsze i wszędzie trzeba zrobić nowy rekord, bo tak będzie wydajniej zarówno dodawać, jak i usuwać, bo można nałożyć klucze obce, dzięki czemu jest zapewnione przechowywanie poprawnych danych, bo można dołożyć dodatkowe kolumny opisujące taki związek dwóch osób.
Obstawiam, że mechanizm przechowujący listę znajomych jako to pokraczne CSV będzie dwa rzędy wielkości wolniejszy niż standardowe rozwiązanie.

0

@lukas_jg: AdamPL ma racje - Twoj pomysl jest do niczego, takich rzeczy w bazach danych sie nie robi.

0

To w jaki sposób w takim razie ? :)

0

Ten, o którym napisał AdamPL w pierwszym swoim poście.

0

To taki sam jaki wpisałem w swoim przykładzie niżej. Więc nie wiem dlaczego napisałeś, że mój pomysł jest "do niczego". W każdym razie wiem jak najlepiej to zrobić. Temat do zamknięcia.

0

Chodzi o fragment, który napisałeś w drugiej części posta tj:

lukas_jg napisał(a)

Czy lepiej jest oddzielać powiązania dla danego użytkownika jakimś znakiem ('user1','znajomy1,znajomy2,znajomy3,...,znajomyN') ?
Dzięki temu mamy tyle rekordów 'znajomych' co użytkowników (można nawet korzystać z tabeli z danymi użytkownika) i wystarczy, że znajdziemy tylko naszego usera i mamy jego wszystkich znajomych w stringu.

To o tym pomyśle pisali koledzy jako tym 'złym'. Poza tym nie bierz tak wszystkiego do siebie i doceń radę od osób, które programują zawodowo. Przykładowo Adam Boduch jest autorem całego serwisu 4programmers, łącznie z forum, więc nie można mu z pewnością odmówić wiedzy i doświadczenia na temat tworzenia tego typu rozwiązań.

0

Nawet MySQL poradzi sobie z tabelą z setkami milionów rekordów, o to się nie bój.

0
lukas_jg napisał(a)

To taki sam jaki wpisałem w swoim przykładzie niżej. Więc nie wiem dlaczego napisałeś, że mój pomysł jest "do niczego".

Ja też nie wiem. Może dlatego nie napisałem? ;)
Ja tylko napisałem czemu lepiej zrobić oddzielną tabelę niż dziwną kolumnę w tabeli użytkownicy.

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