7

pracuję na optymalizację połączeń DB mojego projektu i zauważyłem „znaczące” różnicę w wydajności pomiędzy tymi dwoma identycznymi połączeń poniżej:Zapytanie ActiveRecord jest znacznie wolniejsze niż proste zapytanie SQL?

connection = ActiveRecord::Base.connection() 
pgresult = connection.execute(
    "SELECT SUM(my_column) 
    FROM table 
    WHERE id = #{id} 
    AND created_at BETWEEN '#{lower}' and '#{upper}'") 

i drugą wersję:

sum = Table. 
     where(:id => id, :created_at => lower..upper). 
     sum(:my_column) 

metoda używająca pierwszej wersji zajmuje średnio 300ms do wykonania (operacja nazywa się kilka tysięcy razy łącznie), a metoda wykorzystująca drugą wersję zajmuje około 550ms. To prawie 100% spadek prędkości.

Sprawdziłem podwójnie SQL, który został wygenerowany przez drugą wersję, jest identyczny z pierwszym z wyjątkiem dla poprzedzających kolumn tabeli z nazwą tabeli.

  • Dlaczego spowolnienie? Czy konwersja między ActiveRecord i SQL naprawdę sprawia, że ​​operacja zajmuje prawie 2x?
  • Czy muszę trzymać się pisania prostego kodu SQL (może nawet sproca), jeśli muszę wykonać tę samą operację tonę razy i nie chcę uderzyć w obciążenie?

Dzięki!

+1

wystarczy użyć .explain i spojrzeć na zapytania, który został wygenerowany, jestem pewien, że to wygląda inaczej i to dlaczego to trwa tak znacznie dłużej – antpaw

+0

I dwukrotnie sprawdzane planów zapytań, obie są identyczne, a koszt wszystko. Musiałam zastąpić .select z .sum w drugiej wersji, gdy otrzymasz od tego Fixnuma i nie mogę znaleźć sposobu na wykonanie .explain na kwerendzie użytej do wygenerowania. –

Odpowiedz

2

Kilka rzeczy wyskoczy.

Po pierwsze, jeśli ten kod jest wywoływany 2000 razy i wymaga dodatkowych 250ms, to ~ 0.125ms na połączenie, aby przekonwertować Arel na SQL, co nie jest nierealne.

Po drugie, nie jestem pewien wewnętrznych elementów zakresu w Ruby, ale lower..upper może wykonywać obliczenia, takie jak rozmiar zakresu i inne rzeczy, które będą hitem wydajności.

Czy widzisz ten sam efekt wydajnościowy z następującymi?

sum = Table. 
     where(:id => id). 
     where(:created_at => "BETWEEN ? and ?", lower, upper). 
     sum(:my_column) 
+1

O ile rozumiem, ... jest po prostu syntaktycznym cukrem, który ActiveRecord przekształca w instrukcję BETWEEN, jeśli stwierdzi, że operandy są obiektami czasu. Próbowałem tej wersji i ciągle otrzymywałem te same numery co wolna wersja. Zasadniczo brzmi to jak konwersja zabiera czas. Zrobię profil, by uzyskać większą ziarnistość. –

+0

Tak, wciąż musi generować obiekt Range, który przechodzi, ale myślę, że to nie robi żadnej ciężkiej pracy, ponieważ iterator nie jest generowany. Czy AR może tworzyć obiekty dla każdego przedmiotu przed ich podsumowaniem? To może spowolnić to. Wydaje się jednak mało prawdopodobne. – iHiD

+0

Na podstawie profilowania pierwsza wersja przechodzi bezpośrednio do wykonania zapytania. Druga wersja kosztuje około 35% całkowitego czasu wykonania na 30 poziomowym stosie różnych magii AR. Wersję wolniejszą wersję można zobaczyć tutaj: http://pastebin.com/bipTy3c5 Prosta wersja SQL jest tutaj: http://pastebin.com/LysaGUTy –

Powiązane problemy