2008-12-05 24 views
24

La carga de DB en mi sitio se está volviendo realmente alta, así que es hora de almacenar en caché las consultas comunes que se llaman 1000 veces de una hora donde los resultados no cambian. Así, por ejemplo, en mi modelo de ciudad que haga lo siguiente:Rails Caching DB Consultas y mejores prácticas

def self.fetch(id) 
    Rails.cache.fetch("city_#{id}") { City.find(id) } 
end 

def after_save 
    Rails.cache.delete("city_#{self.id}") 
end 

def after_destroy 
    Rails.cache.delete("city_#{self.id}") 
end 

Así que ahora cuando puedo City.find (1) la primera vez que llegué a la base de datos, pero los próximos 1.000 veces me sale el resultado de la memoria. Estupendo. Pero la mayoría de las llamadas a la ciudad no son City.find (1) sino @ user.city.name donde Rails no usa la búsqueda pero consulta el DB nuevamente ... lo cual tiene sentido pero no es exactamente lo que quiero que haga.

Puedo hacer City.find (@ user.city_id) pero eso es feo.

Así que mi pregunta a ustedes, chicos. ¿Qué están haciendo las personas inteligentes? ¿Cuál es la manera correcta de hacer esto?

Respuesta

-1

Salida cached_model

+0

memoization simplemente envuelve el Rails.cache. No creo que ayude a las asociaciones de modelos que está buscando. – Bill

+1

La memorización no ajusta la memoria caché de Rails. Rails.cache suele ser un almacén de caché compartido entre procesos (por ejemplo, que realmente obtiene beneficios de almacenamiento en caché). La memorización solo ocurre dentro del proceso actual. – Michael

0

que sería seguir adelante y echar un vistazo a Memoization, que se encuentra ahora en Rails 2.2.

"memoization es un patrón de inicializar un método de una vez y luego stashing su valor de distancia de repetición uso".

Hubo una gran Railscast episode en ella recientemente que debería ponerlo en funcionamiento muy bien.

ejemplo de código rápido de la Railscast:

class Product < ActiveRecord::Base 
    extend ActiveSupport::Memoizable 

    belongs_to :category 

    def filesize(num = 1) 
    # some expensive operation 
    sleep 2 
    12345789 * num 
    end 
    memoize :filesize 
end 

More on Memoization

+0

¿Cuál es el beneficio de la memorización de Rails.cache y cómo resuelve el problema de User.city.name donde Rails hace un descubrimiento en City en lugar de usar datos en caché? –

+9

La memorización solo almacenará el resultado por proceso de servidor y no invalidará, por lo que es relativamente inadecuado para su situación y no debe utilizarse de esta manera. – Michael

23

Con respecto al almacenamiento en caché, un par de puntos de menor importancia:

Es digno de usar barra de separación del tipo de objeto y el ID , que es la convención de los carriles. Mejor aún, los modelos ActiveRecord proporcionan el método de instancia cacke_key que proporcionará un identificador único del nombre y el id de la tabla, "cities/13", etc.

Una pequeña corrección en el filtro after_save. Como tiene los datos a mano, también puede escribirlos de nuevo en la memoria caché en lugar de eliminarlos. Que está ahorrando un solo viaje a la base de datos;)

 
def after_save 
    Rails.cache.write(cache_key,self) 
end 

En cuanto a la raíz de la cuestión, si usted está tirando continuamente @ user.city.name, hay dos opciones reales:

  • Denormalizar el nombre de la ciudad del usuario en la fila del usuario. @ user.city_name (mantenga la clave foránea city_id). Este valor debe escribirse para ahorrar tiempo.

-o-

  • poner en práctica su método User.fetch a la carga ansiosa la ciudad. Solo haga esto si el contenido de la fila de la ciudad nunca cambia (es decir, nombre, etc.), de lo contrario, puede abrir una lata de gusanos con respecto a la invalidación de caché.

Opinión personal: Implementar Identificación básica basada métodos de captación (o utilizar un plug-in) para integrarse con memcached, y eliminar la normalización del nombre de la ciudad a la fila del usuario.

Personalmente no soy un gran admirador de los complementos de estilo de modelo en caché, nunca he visto uno que haya ahorrado una cantidad significativa de tiempo de desarrollo de la que no me haya salido apurado.

Si recibe demasiadas consultas de la base de datos, definitivamente vale la pena verificar la carga ansiosa (a través de: incluir) si no lo ha hecho ya. Ese debería ser el primer paso para reducir la cantidad de consultas a la base de datos.

+1

EG 'User.where (id: 3) .includes (: city) .first'. Menospreciado es 'join-merge' para muchas relaciones de muchos a muchos. EG 'City.joins (: restaurants) .merge (user.favorite_restaurants)', devolverá todas las ciudades donde un usuario tiene muchos restaurantes favoritos, y los restaurantes tienen muchas ciudades. Marque [incluye y se une] (http://guides.rubyonrails.org/active_record_querying.html#joining-tables). Definitivamente vale la pena aprender a reducir llamadas con consultas potentes en lugar de pasar por el trabajo de almacenamiento en caché. –

0

Si necesita acelerar consultas sql en datos que no cambian mucho con el tiempo, puede usar vistas materializadas.

A matview almacena los resultados de una consulta en una estructura de tabla de , de la cual se pueden consultar los datos. No es posible agregar o eliminar filas, pero el resto del tiempo se comporta como una tabla real . Las consultas son más rápidas, y la propia matview puede ser indexada.

En el momento de escribir estas líneas, los matviews están disponibles de forma nativa en Oracle DB, PostgreSQL, Sybase, IBM DB2 y Microsoft SQL Server. MySQL no proporciona soporte nativo para matviews, desafortunadamente, pero hay alternativas de código abierto.

aquí tienes buenos artículos sobre el uso de matviews en Rails

sitepoint.com/speed-up-with-materialized-views-on-postgresql-and-rails

hashrocket.com/materialized-view-strategies-using-postgresql

Cuestiones relacionadas