2012-03-28 13 views
5

Szukam skutecznego sposobu losowego wybierania 100 wierszy spełniających pewne warunki z tabeli MySQL z potencjalnie milionów wierszy.Jak losowo wybrać wiele wierszy spełniających pewne warunki z tabeli MySQL?

Niemal wszystko, co znalazłem sugeruje unikanie użycia polecenia RAND(), ze względu na niską wydajność i skalowalność.

Jednakże, this article sugeruje, że ORDER BY RAND() może nadal być używany jako "miły i szybki sposób" do pobierania danych randow.

Na podstawie tego artykułu poniżej przedstawiono przykładowy kod pokazujący, co próbuję wykonać. Moje pytania to:

  1. Czy jest to skuteczny sposób losowy wybór 100 (lub nawet kilkuset) wiersze z tabeli z potencjalnie miliony wierszy?

  2. Kiedy wydajność stanie się problemem?

 
    SELECT user.* 
    FROM (
      SELECT id 
      FROM user 
      WHERE is_active = 1 
      AND  deleted = 0 
      AND  expiretime > '.time().' 
      AND  id NOT IN (10, 13, 15) 
      AND  id NOT IN (20, 30, 50) 
      AND  id NOT IN (103, 140, 250) 
     ORDER BY RAND() 
      LIMIT 100 
      ) 
      AS  random_users 
    STRAIGHT JOIN user 
    ON  user.id = random_users.id 
+0

Sensowne jest wybieranie losowych wartości na polu z indeksem. – Kayser

+0

@Kayser, Obawiam się, że nadal musimy skanować WSZYSTKIE wiersze dla warunków WHERE. Czy to wpłynie na wydajność z ogromną tabelą (potencjalnie miliony wierszy)? – user1298692

+0

Metoda z podświetleniem pk prawdopodobnie ograniczy tylko marginalnie czas wykonania. Dzieje się tak, ponieważ z tą techniką lub bez niej, funkcja rand() jest wywoływana dla wszystkich pasujących wierszy, a liczba wierszy do posortowania jest taka sama. Prawdopodobnie jest to interesujące, jeśli "użytkownik" ma dużo kolumn lub dużych kolumn, a mysql nie jest na tyle sprytny, by czekać po LIMIT, aby zmaterializować użytkownika. * (Które powinno być testowane). –

Odpowiedz

0

Obawiam się, nikt nie będzie w stanie odpowiedzieć na to pytanie z dowolnej dokładności. Jeśli naprawdę chcesz wiedzieć, będziesz musiał przeprowadzić testy porównawcze w swoim systemie (a nie na żywo najlepiej, ale dokładnie kopię). Porównaj to rozwiązanie z innym rozwiązaniem (na przykład losowymi wierszami używając PHP) i porównaj liczby z tym, co Ty/twój klient uznają za "dobrą wydajność", a następnie zwiększaj swoje dane, starając się utrzymać rozkład wartości kolumn tak blisko rzeczywistych jak możesz i zobacz, gdzie wydajność zaczyna spadać.) Szczerze mówiąc, jeśli to działa teraz z odrobiną rezerwy, to idę na to. Kiedy (jeśli!) staje się wąskim gardłem, to możesz na to spojrzeć ponownie - lub po prostu chwyć dodatkowe żelazko w bazie danych ...

1

Gorąco zachęcamy do przeczytania tego article. Ostatni segment będzie obejmował wybór wielu losowych wierszy.I powinieneś być w stanie zauważyć oświadczenie SELECT w PROCEDURE, która zostanie tam opisana. To byłby punkt, w którym dodajesz swój specyficzny kod WHERE ditions.

Problem z ORDER BY RAND() polega na tym, że operacja ta ma złożoność n*log2(n), podczas gdy metoda opisana w artykule, który łączyłem, ma prawie stałą złożoność.

Załóżmy, że wybierając losowo wiersz z tabeli, która zawiera 10 wpisów, używając ORDER BY RAND() trwa 1 time unit:

entries | time units 
------------------------- 
     10 |   1  /* if this takes 0.001s */ 
     100 |  20 
    1'000 |  300 
    10'000 |  4'000 
    100'000 | 50'000 
1'000'000 | 600'000  /* then this will need 10 minutes */ 

I napisałeś, że masz do czynienia z tabeli na skalę milionów.

0

Preprocesuj jak najwięcej spróbować czegoś podobnego (np VB-like)

Dim sRND = New StringBuilder : Dim iRandom As New Random() 
Dim iMaxID As Integer = **put you maxId here** 
Dim Cnt as Integer=0 
While Cnt < 100 
     Dim RndVal As Integer = iRandom.Next(1, iMaxID) 
     If Not ("10,13,15,20,30,50,103,140,250").Contains(RndVal) Then 
      Cnt += 1 
      sRND.Append("," & RndVal) 
     end if 
End While 
String.Format("SELECT * FROM (Select ID FROM(User) WHERE(is_active = 1) AND deleted = 0 AND expiretime > {0} AND id IN ({1}) .blahblablah.... LIMIT 100",time(), Mid(sRND.ToString, 2)) 

nie sprawdzić składnię ale dostaniesz mój prąd mam nadzieję. Spowoduje to, że MySql odczyta rekordy, które pasują do "IN" i zatrzyma się, gdy osiągnie 100 bez konieczności wstępnego przetwarzania wszystkich rekordów.

Proszę dać mi znać różnica czasu, jeśli spróbujesz. (Jestem qurious)

Powiązane problemy