7

He estado trabajando en la optimización de las llamadas de base de datos de mi proyecto y me di cuenta de una diferencia "significativa" en el rendimiento entre las dos llamadas idénticas a continuación:Consulta ActiveRecord mucho más lenta que SQL recta?

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

y la segunda versión:

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

El el método que utiliza la primera versión, en promedio, tarda 300 ms en ejecutarse (la operación se llama un par de miles de veces en total), y el método que utiliza la segunda versión lleva unos 550 ms. Eso es casi un 100% de disminución en la velocidad.

Comprobé dos veces el SQL generado por la segunda versión, es idéntico al primero con excepción de anteponer las columnas de la tabla con el nombre de la tabla.

  • ¿Por qué la ralentización? ¿La conversión entre ActiveRecord y SQL realmente hace que la operación tome casi 2 veces?
  • ¿Debo seguir escribiendo SQL directamente (tal vez incluso un sproc) si necesito realizar la misma operación una tonelada de veces y no quiero tocar la cabeza?

Gracias!

+1

sólo tiene que utilizar • Explicar y mirar a la consulta que se generó, estoy seguro de que tiene un aspecto diferente y es por eso que se necesita mucho más tiempo – antpaw

+0

Verifiqué los planes de consulta, los dos son idénticos, el costo y todas. Tuve que sustituir .select de .sum en la segunda versión, ya que obtiene un Fixnum de ese y no puedo encontrar una forma de hacer un .explain en la consulta que se utilizó para generarlo. –

Respuesta

2

Un par de cosas saltan.

En primer lugar, si se llama a este código 2000 veces y se requieren 250ms adicionales para ejecutarse, eso es ~ 0.125ms por llamada para convertir el Arel a SQL, lo cual no es poco realista.

En segundo lugar, no estoy seguro de los detalles internos de la gama en Ruby, pero lower..upper puede estar haciendo cálculos, tales como el tamaño de la gama y otras cosas, que será un gran impacto en el rendimiento.

¿Ve el mismo rendimiento alcanzado con lo siguiente?

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

Por lo que yo entiendo, el .. es simplemente azúcar sintáctica que ActiveRecord transforma en una declaración BETWEEN si determina que los operandos son objetos Time. Probé esa versión y seguí obteniendo los mismos números exactos que la versión lenta. Básicamente parece que la conversión está ocupando el tiempo. Lo perfilaré para obtener más granularidad. –

+0

Sí, todavía tiene que generar un objeto Range que pase, pero supongo que no hace ningún trabajo duro porque no se genera un iterador. ¿Podría AR estar haciendo objetos para cada elemento antes de sumarlos? Eso podría ralentizarlo. Parece poco probable sin embargo. – iHiD

+0

Según el perfil, la primera versión salta directamente a la ejecución de la consulta. Sin embargo, la segunda versión gasta aproximadamente un 35% extra del tiempo total de ejecución en una pila de 30 niveles de varias magias de AR. La versión más lenta se puede ver aquí: http://pastebin.com/bipTy3c5 La versión SQL directa está aquí: http://pastebin.com/LysaGUTy –

Cuestiones relacionadas