Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

2 Stron V   1 2 >  
Reply to this topicStart new topic
> [PHP][MYSQL] LOSOWY REKORD, szukam sposobu bardziej wydajnego niż RAND()
lysy2005
post 18.06.2011, 12:48:03
Post #1





Grupa: Zarejestrowani
Postów: 56
Pomógł: 0
Dołączył: 18.01.2009

Ostrzeżenie: (0%)
-----


Witam,
mam bazę 4 GB i chce pobrać losowy rekord spełniające moje kryteria.

Teraz mam zastosowanie:
Select pole FROM tabela WHERE pole='1' ORDER BY RAND(); LIMIT 1

Ale wykorzystując RAND() zapytanie trwa bardzo długo,
przy dużych bazach nie spełnia roli.
W jaki sposób wy wyciągacie losowe rekordy w takich dużych bazach?
Z góry dziękuje za pomoc
Go to the top of the page
+Quote Post
matino
post 18.06.2011, 12:56:11
Post #2





Grupa: Zarejestrowani
Postów: 175
Pomógł: 30
Dołączył: 9.08.2007

Ostrzeżenie: (0%)
-----


Tutaj masz bardzo przydatny link: http://net.tutsplus.com/tutorials/other/to...best-practices/
Patrz punkt 6 odnośnie swojego pytania.
Go to the top of the page
+Quote Post
lysy2005
post 18.06.2011, 13:11:06
Post #3





Grupa: Zarejestrowani
Postów: 56
Pomógł: 0
Dołączył: 18.01.2009

Ostrzeżenie: (0%)
-----


Cytat(matino @ 18.06.2011, 13:56:11 ) *
Tutaj masz bardzo przydatny link: http://net.tutsplus.com/tutorials/other/to...best-practices/
Patrz punkt 6 odnośnie swojego pytania.

Hm... no jest to jakiś pomysł,
ale jak dodam do pierwszego zapytanie Where, to całe zapytanie trwa 10 sekund troszkę dużo.. Wiadomo lepiej niż RAND, ale i ka długo...
Może jeszcze jakieś pomysły>

10 sekund napisałem... Zapytanie z tego przykładu dostosowane do moich potrzeb trwa 100seknd
Go to the top of the page
+Quote Post
Hpsi
post 18.06.2011, 13:17:25
Post #4





Grupa: Zarejestrowani
Postów: 483
Pomógł: 50
Dołączył: 15.03.2005
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Aż mi sie nie chce wierzyć że:

  1. SELECT count(`id`) AS `count` FROM tabela

wynik counta jak dasz w mt_rand() i pobierasz potem 1 rekord, daje ci 100 sekund geenrowania

pracowałem na podobnej bazie wielkosciowo nie jeden raz i nigdy nie osiagnelem takiej "wartosci" zapytan

Ten post edytował Hpsi+ 18.06.2011, 13:18:03


--------------------
Daiquiri: T1 = (dx/dt * s)^hpsi
Daiquiri: gdzie T1 - Twój czas przybycia na miejsce, dx/dt - prędkość, s droga
Daiquiri: brb trzeba by to poprawić T1 - Czas jaki Ci to zajmie
Daiquiri: Zatem T - czas dotarcia p T1 + T2 gdzie T2 = aktualny czas

===
po prostu kocham ją :D haha
Go to the top of the page
+Quote Post
lysy2005
post 18.06.2011, 13:43:52
Post #5





Grupa: Zarejestrowani
Postów: 56
Pomógł: 0
Dołączył: 18.01.2009

Ostrzeżenie: (0%)
-----


Cytat(Hpsi+ @ 18.06.2011, 14:17:25 ) *
Aż mi sie nie chce wierzyć że:

  1. SELECT count(`id`) AS `count` FROM tabela

wynik counta jak dasz w mt_rand() i pobierasz potem 1 rekord, daje ci 100 sekund geenrowania

pracowałem na podobnej bazie wielkosciowo nie jeden raz i nigdy nie osiagnelem takiej "wartosci" zapytan


Tak, ale zobacz ze nie dodajesz przedrostka WHERE, a ja jeszcze dodatkowo daje regule do wyszukiwania.
Baza ma 4 GB, i cały czas rośnie bo są tam dodawane rekordy ok 100MB na godzinę także wieczorem będzie 5 GB
Go to the top of the page
+Quote Post
webdice
post 18.06.2011, 14:32:39
Post #6


Developer


Grupa: Moderatorzy
Postów: 3 045
Pomógł: 290
Dołączył: 20.01.2007




Cytat(Hpsi+ @ 18.06.2011, 14:17:25 ) *
Aż mi sie nie chce wierzyć że:

  1. SELECT count(`id`) AS `count` FROM tabela

wynik counta jak dasz w mt_rand() i pobierasz potem 1 rekord, daje ci 100 sekund geenrowania

pracowałem na podobnej bazie wielkosciowo nie jeden raz i nigdy nie osiagnelem takiej "wartosci" zapytan


Count zwróci Ci ilość rekordów. Wszystko będzie działać dobrze, o ile rekordy będą ponumerowane w odpowiedniej kolejności. Dla przykładu masz w bazie pięć rekordów (1, 2, 3, 5, 6). W takiej sytuacji mt_rand może wylosować Ci liczbę 4, co z wiadomej przyczyny nie zadziała.
Go to the top of the page
+Quote Post
Speedy
post 18.06.2011, 14:54:24
Post #7





Grupa: Zarejestrowani
Postów: 651
Pomógł: 28
Dołączył: 4.12.2004

Ostrzeżenie: (0%)
-----


Możesz zrobić tak:

  1. SELECT id
  2. FROM tabela
  3. WHERE (tabela.id = (SELECT FLOOR(1 + (RAND() * ((SELECT COUNT(id) FROM tabela) - 1) ))))
  4. LIMIT 1;


To zapytanie wylosuje jedną liczbę od 1 do N, gdzie N, to liczba rekordów w Twojej tabeli a następnie zwróci rekord o podanym ID.
To rozwiązanie ma tą wadę, że jeśli rekordy są niespójne (nie ma ciągłości identyfikatorów ID) i część z nich jest usunięta, to wtedy zapytanie czasem zwróci pusty wynik (NULL).
Można to spróbować jakoś rozwiązać w SQL-u, albo pójść na łatwiznę i w aplikacji webowej możesz zrobić pętlę do ... while, w której wykonasz to zapytanie raz jeszcze, gdy zwróci wartość NULL.
Jeśli nieciągłość w identyfikatorach ID jest mała, rozwiązanie powinno się sprawdzić. Jeśli natomiast rozbieżność jest duża, może to działać bardzo wolno.
Tak czy inaczej, powyższe zapytanie działa szybciej, niż ... ORDER BY RAND() choć jest mniej stabilne.

Ten post edytował Speedy 18.06.2011, 14:55:28


--------------------
Sygnatura niezgodna z regulaminem.
Go to the top of the page
+Quote Post
#luq
post 18.06.2011, 15:59:48
Post #8





Grupa: Zarejestrowani
Postów: 589
Pomógł: 91
Dołączył: 22.05.2008
Skąd: Gliwice

Ostrzeżenie: (0%)
-----


Co Wy z tym WHERE id = xxx?

Przecież można:
  1. SELECT row FROM TABLE LIMIT $rand, 1


@lysy2005 a masz na polu z którego robisz warunek dla COUNT zrobiony index?


--------------------
Moja gra - scraby.io
Go to the top of the page
+Quote Post
lysy2005
post 19.06.2011, 17:09:18
Post #9





Grupa: Zarejestrowani
Postów: 56
Pomógł: 0
Dołączył: 18.01.2009

Ostrzeżenie: (0%)
-----


Cytat(#luq @ 18.06.2011, 16:59:48 ) *
@lysy2005 a masz na polu z którego robisz warunek dla COUNT zrobiony index?

Oczywiście, ale zapytanie miele i miele bo są cały czas dodawane nowe rekordy,
zrobiłem tymczasowo że pobieram 1000 rekordów z warunkiem i z nim w phpie losuje randem.

Sposób wskazany wyżej "count" nie zadziała bo dane nie są usegregowane po kolei, Id wygląda rak: 1,100,145,1111,456 itp,
ponieważ czasami właśnie rekordy nie spełniające warunków są usuwane.

Rozbudowałem troszkę skrypt i już dodaje 1GB na godzinę do bazy, baza ma 20GB wszystko zoptymalizowałem został ten rand nieszczęsny:(

Ale 1GB danych na godzinę to trochę mało dla mnie, bo całość ma mieć 500GB...
W takim tępię 20 dni by się robiło ;/

PS. może ktoś podpowie z konfiguracją my.cnf
Serwer 6GB ramu ~9 GHz
aktualnie mam zajęte 95% ramu.

Wycinek najważniejszych ustawień:
key_buffer = 80M
max_allowed_packet = 80M
thread_stack = 40M
thread_cache_size = 6
query_cache_limit = 40M
query_cache_size = 400M

System Ubuntu 10, 64bit
Baza jest w InnoDB
Mysql Wersja serwera: 5.1.49-1ubuntu8.1

Z góry dzięki za pomoc!



Go to the top of the page
+Quote Post
aachi
post 19.06.2011, 17:35:00
Post #10





Grupa: Zarejestrowani
Postów: 54
Pomógł: 12
Dołączył: 4.08.2007

Ostrzeżenie: (0%)
-----


Może rozbij tablice na kilkanaście mniejszych. Nie wiem jakie dane przechowujesz, ale np tablica1 mogłaby zawierać tylko wiersze z wartością pole 1 (albo 1 do 100,000, jeśli pole może przyjmować wartości od 1 do miliona).
Go to the top of the page
+Quote Post
Speedy
post 19.06.2011, 20:09:23
Post #11





Grupa: Zarejestrowani
Postów: 651
Pomógł: 28
Dołączył: 4.12.2004

Ostrzeżenie: (0%)
-----


Cytat(#luq @ 18.06.2011, 16:59:48 ) *
Co Wy z tym WHERE id = xxx?

Przecież można:
  1. SELECT row FROM TABLE LIMIT $rand, 1


@lysy2005 a masz na polu z którego robisz warunek dla COUNT zrobiony index?


Można tak, ale tego nie zrobisz w samym MySQL-u, ponieważ w klauzuli LIMIT nie można stosować zmiennych.

Wymyśliłem inny sposób na rozwiązanie tego problemu, który jest pewną wariacją mojego poprzedniego pomysłu:

  1. SELECT id
  2. FROM tabela
  3. WHERE (tabela.id >= (SELECT FLOOR(1 + (RAND() * ((SELECT COUNT(id) FROM tabela) - 1) ))))
  4. LIMIT 1;


Zamiast dokładnego porównania ID do losowej wartości, wyszukujemy rekordy, które są większe lub równe, niż losowa wartość od 1 do N, gdzie N, to liczba wszystkich rekordów.
Można też zmienić operator na znak mniejszości, ale wtedy zapytanie zawsze zwróci pierwszy wynik z tabeli, więc lepiej jest zrobić tak, jak napisałem wyżej.
Wtedy nie musimy znajdować rekordu, który ma identyczny ID, jak losowy, ale znajdujemy więcej rekordów z danego zakresu i wybieramy pierwszy z nich. W takiej sytuacji zawsze wylosujemy jakiś rekord i zapytanie nigdy nie powinno zwrócić pustego wyniku.


--------------------
Sygnatura niezgodna z regulaminem.
Go to the top of the page
+Quote Post
aachi
post 19.06.2011, 20:27:39
Post #12





Grupa: Zarejestrowani
Postów: 54
Pomógł: 12
Dołączył: 4.08.2007

Ostrzeżenie: (0%)
-----


Panowie, ale pamiętacie, że autor nie potrzebuje losowego wiersza z tabeli, tylko losowego wiersza spełniającego jakiśtam warunek?
Go to the top of the page
+Quote Post
Fifi209
post 19.06.2011, 20:36:07
Post #13





Grupa: Zarejestrowani
Postów: 4 655
Pomógł: 556
Dołączył: 17.03.2009
Skąd: Katowice

Ostrzeżenie: (0%)
-----


Fajnie jakbyś pokazał strukturę tej tabeli lub nawet tabel o ile są powiązane. Może da się to jakoś rozbić?

Swoją drogą co Ty przechowujesz w tej bazie, że w ciągu godziny o takie rozmiary się powiększa?


--------------------
Zainteresowania: C#, PHP, JS, SQL, AJAX, XML, C dla AVR
Chętnie pomogę, lecz zanim napiszesz: Wujek Google , Manual PHP
Go to the top of the page
+Quote Post
Speedy
post 19.06.2011, 20:40:20
Post #14





Grupa: Zarejestrowani
Postów: 651
Pomógł: 28
Dołączył: 4.12.2004

Ostrzeżenie: (0%)
-----


Cytat(aachi @ 19.06.2011, 21:27:39 ) *
Panowie, ale pamiętacie, że autor nie potrzebuje losowego wiersza z tabeli, tylko losowego wiersza spełniającego jakiśtam warunek?


Tylko nie wiadomo, co to za warunek, więc teraz mógłbym jedynie zgadywać (musiałbym mieć więcej szczegółów), ale rzeczywiście przy odpowiednim warunku moje ostatnie rozwiązanie może nie zadziałać zawsze.
Jeżeli pominiemy ten warunek, rozwiązanie działa.


--------------------
Sygnatura niezgodna z regulaminem.
Go to the top of the page
+Quote Post
vokiel
post 19.06.2011, 20:56:24
Post #15





Grupa: Zarejestrowani
Postów: 2 592
Pomógł: 445
Dołączył: 12.03.2007

Ostrzeżenie: (0%)
-----


Wybaczcie link do bloga, ale kiedyś o tym pisałem, przepisywanie z bloga nie ma sensu: MySQL RAND() – jak pobrać losowe wiersze


--------------------
Go to the top of the page
+Quote Post
aachi
post 19.06.2011, 21:15:32
Post #16





Grupa: Zarejestrowani
Postów: 54
Pomógł: 12
Dołączył: 4.08.2007

Ostrzeżenie: (0%)
-----


Cytat(vokiel @ 19.06.2011, 20:56:24 ) *
Wybaczcie link do bloga, ale kiedyś o tym pisałem, przepisywanie z bloga nie ma sensu: MySQL RAND() ? jak pobrać losowe wiersze


Z tego co rozumiem proponujesz autorowi tworzenie tymczasowej tabeli zawierającej wszystkie id wierszy spełniających określony warunek WHERE? A następnie losowanie z tej tymczasowej tabeli id wiersza? I to wiedząc, że w ciągu godziny baza przybiera o 1GB?
Czy może masz na myśli jakiś inny sposób podany na blogu?

Ten post edytował aachi 19.06.2011, 21:16:08
Go to the top of the page
+Quote Post
by_ikar
post 20.06.2011, 09:16:55
Post #17





Grupa: Zarejestrowani
Postów: 1 798
Pomógł: 307
Dołączył: 13.05.2009
Skąd: Gubin/Wrocław

Ostrzeżenie: (0%)
-----


Dobrym rozwiązaniem byłoby zastosowanie cron'a, który powiedzmy co 10min mieliłby nawet bardzo skomplikowane zapytanie, z losowymi rekordami, ale większą ilością, powiedzmy 10k czy tam nawet 100k i zapisywał ich nie wiem, id, czy nawet całe wiersze, do pliku i potem odczytywać te 10k losowych rekordów i z tych 10k losowych rekordów wyciągać powiedzmy 20 losowych przy każdym requeście, które nam są potrzebne. Jakaś tam losowość pozostaje. Mysql akurat w przypadku tak dużej ilości danych nie jest dobrym rozwiązaniem. Do przechowywania dużej ilości danych dobre są bazy typu nosql, które lepiej sobie radzą z tak dużą ilością danych. Nie wiem jak mysql zachowa się powiedzmy przy 500gb danych. Radzę się zastanowić nad zmianą bazy.
Go to the top of the page
+Quote Post
uupah5
post 20.06.2011, 09:49:40
Post #18





Grupa: Zarejestrowani
Postów: 207
Pomógł: 18
Dołączył: 4.09.2010
Skąd: warszawa

Ostrzeżenie: (0%)
-----


coraz ciekawsze pomysły:
rozbijanie tabeli na wiele z powodu jednego zapytania? a reszta logiki aplikacji sama się dostosuje?
20GB to za dużo dla mysql?

Autorze, moim skromnym zdaniem problem jest rozwiązywalny bez zmiany bazy
Rzuć więcej info o samej bazie/tabeli/zapytaniach. opicy tego co robisz za pomocą php możesz sobie darować

@all: no offence;)
Go to the top of the page
+Quote Post
thek
post 20.06.2011, 14:15:08
Post #19





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Ja osobiście kombinowałbym także pod kątem:
Zlicz ilość rekordów spełniających warunek,
Wybierz liczbę od 0 do ilość-1
Zapytanie z WHERE oraz LIMIT wylosowana_liczba, 1.

Omija się problem nieciągłości. Problemem jest jedynie szybka modyfikacja danych bazy, ale można to ominąć choćby funkcjami.


--------------------
Najpierw był manual... Jeśli tam nie zawarto słów mądrości to zapytaj wszechwiedzącego Google zadając mu własciwe pytania. A jeśli i on milczy to Twój problem nie istnieje :D
Go to the top of the page
+Quote Post
by_ikar
post 20.06.2011, 16:59:57
Post #20





Grupa: Zarejestrowani
Postów: 1 798
Pomógł: 307
Dołączył: 13.05.2009
Skąd: Gubin/Wrocław

Ostrzeżenie: (0%)
-----


Cytat(uupah5 @ 20.06.2011, 10:49:40 ) *
coraz ciekawsze pomysły:
rozbijanie tabeli na wiele z powodu jednego zapytania? a reszta logiki aplikacji sama się dostosuje?
20GB to za dużo dla mysql?

Autorze, moim skromnym zdaniem problem jest rozwiązywalny bez zmiany bazy
Rzuć więcej info o samej bazie/tabeli/zapytaniach. opicy tego co robisz za pomocą php możesz sobie darować

@all: no offence;)


20gb na mysql to nie jest za dużo, ale do składowania dużej ilości danych, używa się wyspecjalizowanych w tym kierunku baz danych, które radzą sobie o niebo lepiej, od średniego mysqla.
Go to the top of the page
+Quote Post

2 Stron V   1 2 >
Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 12.06.2025 - 16:09